commit ce38a8cca46c4aeb7b87c96dadd197969c1bbdde Author: Jacob Welsh AuthorDate: Wed Nov 2 17:51:13 2022 +0000 Commit: Jacob Welsh CommitDate: Wed Nov 2 20:10:36 2022 +0000 Type: feature removal plugins: drop unloved plugins with overly complex and special loading mechanics (fs-compress, mail-crypt, var-expand-crypt, fts-lucene) Pursuant to http://fixpoint.welshcomputing.com/2022/classifying-the-dovecot-module-menagerie-by-load-point/ , these would add unwarranted costs to the static module conversion effort. diff --git a/configure.ac b/configure.ac index 06c572b87e..bd1ef1ed4e 100644 --- a/configure.ac +++ b/configure.ac @@ -131,12 +131,6 @@ AS_HELP_STRING([--with-cassandra], [Build with Cassandra driver support]), TEST_WITH(cassandra, $withval), want_cassandra=no) -AC_ARG_WITH(lucene, -AS_HELP_STRING([--with-lucene], [Build with CLucene full text search support]), - TEST_WITH(lucene, $withval), - want_lucene=no) -AM_CONDITIONAL(BUILD_LUCENE, test "$want_lucene" = "yes") - AC_ARG_WITH(stemmer, AS_HELP_STRING([--with-stemmer], [Build with libstemmer support (for FTS) (auto)]), TEST_WITH(stemmer, $withval), @@ -270,7 +264,6 @@ want_prefetch_userdb=yes AC_ISC_POSIX AC_PROG_CC AC_PROG_CPP -AC_PROG_CXX # lucene plugin needs this AC_CHECK_TOOL([FLEX],[flex],[:]) AC_CHECK_TOOL([BISON],[bison],[:]) AS_IF([test "$BISON" = ":" && test ! -e src/lib/event-filter-parser.h], @@ -284,9 +277,6 @@ AC_C_INLINE AC_PROG_LIBTOOL AM_ICONV -# SIZE_MAX is missing without this -CXXFLAGS="$CXXFLAGS -D__STDC_LIMIT_MACROS" - AC_DEFINE_UNQUOTED(DOVECOT_NAME, "$PACKAGE_NAME", [Dovecot name]) AC_DEFINE_UNQUOTED(DOVECOT_STRING, "$PACKAGE_STRING", [Dovecot string]) AC_DEFINE_UNQUOTED(DOVECOT_VERSION, "$PACKAGE_VERSION", [Dovecot version]) @@ -715,7 +705,6 @@ fts=" squat" not_fts="" DOVECOT_WANT_SOLR -DOVECOT_WANT_CLUCENE DOVECOT_WANT_STEMMER DOVECOT_WANT_TEXTCAT @@ -723,9 +712,6 @@ DOVECOT_WANT_ICU DOVECOT_WANT_APPARMOR -if test $have_lucene = no; then - not_fts="$not_fts lucene" -fi if test $have_solr = no; then not_fts="$not_fts solr" fi @@ -863,9 +849,7 @@ src/util/Makefile src/plugins/Makefile src/plugins/acl/Makefile src/plugins/imap-acl/Makefile -src/plugins/fs-compress/Makefile src/plugins/fts/Makefile -src/plugins/fts-lucene/Makefile src/plugins/fts-solr/Makefile src/plugins/fts-squat/Makefile src/plugins/last-login/Makefile @@ -889,8 +873,6 @@ src/plugins/virtual/Makefile src/plugins/welcome/Makefile src/plugins/zlib/Makefile src/plugins/imap-zlib/Makefile -src/plugins/mail-crypt/Makefile -src/plugins/var-expand-crypt/Makefile src/plugins/apparmor/Makefile src/plugins/charset-alias/Makefile stamp.h diff --git a/m4/want_clucene.m4 b/m4/want_clucene.m4 deleted file mode 100644 index 748ab5a85d..0000000000 --- a/m4/want_clucene.m4 +++ /dev/null @@ -1,12 +0,0 @@ -AC_DEFUN([DOVECOT_WANT_CLUCENE], [ - have_lucene=no - if test "$want_lucene" = "yes"; then - PKG_CHECK_MODULES(CLUCENE, libclucene-core,, [ - # no pkg-config file for clucene. fallback to defaults. - # FIXME: we should verify here that this actually works.. - CLUCENE_LIBS="-lclucene-shared -lclucene-core" - ]) - have_lucene=yes - fts="$fts lucene" - fi -]) diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am index ab0b9d14f8..249a566e8c 100644 --- a/src/plugins/Makefile.am +++ b/src/plugins/Makefile.am @@ -2,10 +2,6 @@ if BUILD_ZLIB_PLUGIN ZLIB = zlib imap-zlib endif -if BUILD_LUCENE -FTS_LUCENE = fts-lucene -endif - if BUILD_SOLR FTS_SOLR = fts-solr endif @@ -39,15 +35,11 @@ SUBDIRS = \ replication \ old-stats \ imap-old-stats \ - mail-crypt \ trash \ virtual \ welcome \ $(ZLIB) \ - $(FTS_LUCENE) \ $(FTS_SOLR) \ $(DICT_LDAP) \ $(APPARMOR) \ - fs-compress \ - var-expand-crypt \ charset-alias diff --git a/src/plugins/fs-compress/Makefile.am b/src/plugins/fs-compress/Makefile.am deleted file mode 100644 index 77ba7336d2..0000000000 --- a/src/plugins/fs-compress/Makefile.am +++ /dev/null @@ -1,14 +0,0 @@ -fs_moduledir = $(moduledir) -fs_module_LTLIBRARIES = \ - libfs_compress.la - -AM_CPPFLAGS = \ - -I$(top_srcdir)/src/lib \ - -I$(top_srcdir)/src/lib-compression \ - -I$(top_srcdir)/src/lib-fs - -NOPLUGIN_LDFLAGS = -libfs_compress_la_SOURCES = fs-compress.c -libfs_compress_la_LIBADD = ../../lib-compression/libdovecot-compression.la -libfs_compress_la_DEPENDENCIES = ../../lib-compression/libdovecot-compression.la -libfs_compress_la_LDFLAGS = -module -avoid-version diff --git a/src/plugins/fs-compress/fs-compress.c b/src/plugins/fs-compress/fs-compress.c deleted file mode 100644 index ab03076e03..0000000000 --- a/src/plugins/fs-compress/fs-compress.c +++ /dev/null @@ -1,285 +0,0 @@ -/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */ - -#include "lib.h" -#include "array.h" -#include "istream.h" -#include "istream-tee.h" -#include "istream-try.h" -#include "ostream.h" -#include "iostream-temp.h" -#include "compression.h" -#include "fs-api-private.h" - -struct compress_fs { - struct fs fs; - const struct compression_handler *compress_handler; - int compress_level; - bool try_plain; -}; - -struct compress_fs_file { - struct fs_file file; - struct compress_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 COMPRESS_FS(ptr) container_of((ptr), struct compress_fs, fs) -#define COMPRESS_FILE(ptr) container_of((ptr), struct compress_fs_file, file) - -extern const struct fs fs_class_compress; - -static struct fs *fs_compress_alloc(void) -{ - struct compress_fs *fs; - - fs = i_new(struct compress_fs, 1); - fs->fs = fs_class_compress; - return &fs->fs; -} - -static int -fs_compress_init(struct fs *_fs, const char *args, - const struct fs_settings *set, const char **error_r) -{ - struct compress_fs *fs = COMPRESS_FS(_fs); - const char *p, *compression_name, *level_str; - const char *parent_name, *parent_args; - int ret; - - /* get compression handler name */ - if (str_begins(args, "maybe-")) { - fs->try_plain = TRUE; - args += 6; - } - - p = strchr(args, ':'); - if (p == NULL) { - *error_r = "Compression method not given as parameter"; - return -1; - } - compression_name = t_strdup_until(args, p++); - args = p; - - /* get compression level */ - p = strchr(args, ':'); - if (p == NULL || p[1] == '\0') { - *error_r = "Parent filesystem not given as parameter"; - return -1; - } - - level_str = t_strdup_until(args, p++); - args = p; - ret = compression_lookup_handler(compression_name, &fs->compress_handler); - if (ret <= 0) { - *error_r = t_strdup_printf("Compression method '%s' %s.", - compression_name, ret == 0 ? - "not supported" : "unknown"); - return -1; - } - if (str_to_int(level_str, &fs->compress_level) < 0 || - fs->compress_level < fs->compress_handler->get_min_level() || - fs->compress_level > fs->compress_handler->get_max_level()) { - *error_r = t_strdup_printf( - "Invalid compression level parameter '%s': " - "Level must be between %d..%d", level_str, - fs->compress_handler->get_min_level(), - fs->compress_handler->get_max_level()); - 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++; - } - return fs_init(parent_name, parent_args, set, &_fs->parent, error_r); -} - -static void fs_compress_free(struct fs *_fs) -{ - struct compress_fs *fs = COMPRESS_FS(_fs); - - i_free(fs); -} - -static struct fs_file *fs_compress_file_alloc(void) -{ - struct compress_fs_file *file = i_new(struct compress_fs_file, 1); - return &file->file; -} - -static void -fs_compress_file_init(struct fs_file *_file, const char *path, - enum fs_open_mode mode, enum fs_open_flags flags) -{ - struct compress_fs *fs = COMPRESS_FS(_file->fs); - struct compress_fs_file *file = COMPRESS_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 parent, 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_compress_file_deinit(struct fs_file *_file) -{ - struct compress_fs_file *file = COMPRESS_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_compress_file_close(struct fs_file *_file) -{ - struct compress_fs_file *file = COMPRESS_FILE(_file); - - i_stream_unref(&file->input); - fs_file_close(file->super_read); - fs_file_close(_file->parent); -} - -static struct istream * -fs_compress_read_stream(struct fs_file *_file, size_t max_buffer_size) -{ - struct compress_fs_file *file = COMPRESS_FILE(_file); - struct istream *input; - enum istream_decompress_flags flags = 0; - - 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); - if (file->fs->try_plain) - flags |= ISTREAM_DECOMPRESS_FLAG_TRY; - file->input = i_stream_create_decompress(input, flags); - i_stream_unref(&input); - i_stream_ref(file->input); - return file->input; -} - -static void fs_compress_write_stream(struct fs_file *_file) -{ - struct compress_fs_file *file = COMPRESS_FILE(_file); - - if (file->fs->compress_level == 0) { - fs_wrapper_write_stream(_file); - return; - } - - i_assert(_file->output == NULL); - - file->temp_output = - iostream_temp_create_named(_file->fs->temp_path_prefix, - IOSTREAM_TEMP_FLAG_TRY_FD_DUP, - fs_file_path(_file)); - _file->output = file->fs->compress_handler-> - create_ostream(file->temp_output, file->fs->compress_level); -} - -static int fs_compress_write_stream_finish(struct fs_file *_file, bool success) -{ - struct compress_fs_file *file = COMPRESS_FILE(_file); - struct istream *input; - int ret; - - if (file->fs->compress_level == 0) - return fs_wrapper_write_stream_finish(_file, success); - - if (_file->output != NULL) { - if (_file->output->closed) - success = FALSE; - if (_file->output == file->super_output) - _file->output = NULL; - else - o_stream_unref(&_file->output); - } - if (!success) { - o_stream_destroy(&file->temp_output); - if (file->super_output != NULL) - fs_write_stream_abort_parent(_file, &file->super_output); - return -1; - } - - if (file->super_output != NULL) { - 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(_file->parent, &file->temp_output); - } - /* 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; -} - -const struct fs fs_class_compress = { - .name = "compress", - .v = { - fs_compress_alloc, - fs_compress_init, - NULL, - fs_compress_free, - fs_wrapper_get_properties, - fs_compress_file_alloc, - fs_compress_file_init, - fs_compress_file_deinit, - fs_compress_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_compress_read_stream, - fs_write_via_stream, - fs_compress_write_stream, - fs_compress_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/fts-lucene/Makefile.am b/src/plugins/fts-lucene/Makefile.am deleted file mode 100644 index d68e6aed28..0000000000 --- a/src/plugins/fts-lucene/Makefile.am +++ /dev/null @@ -1,61 +0,0 @@ -doveadm_moduledir = $(moduledir)/doveadm - -AM_CPPFLAGS = \ - -I$(top_srcdir)/src/lib \ - -I$(top_srcdir)/src/lib-mail \ - -I$(top_srcdir)/src/lib-index \ - -I$(top_srcdir)/src/lib-storage \ - -I$(top_srcdir)/src/plugins/fts \ - -I$(top_srcdir)/src/doveadm - -AM_CXXFLAGS = \ - $(CLUCENE_CFLAGS) \ - $(LIBEXTTEXTCAT_CFLAGS) - -NOPLUGIN_LDFLAGS = -lib21_fts_lucene_plugin_la_LDFLAGS = -module -avoid-version -lib20_doveadm_fts_lucene_plugin_la_LDFLAGS = -module -avoid-version - -module_LTLIBRARIES = \ - lib21_fts_lucene_plugin.la - -if BUILD_FTS_STEMMER -STEMMER_LIBS = -lstemmer -SHOWBALL_SOURCES = Snowball.cc -endif - -if BUILD_FTS_EXTTEXTCAT -TEXTCAT_LIBS = $(LIBEXTTEXTCAT_LIBS) -else -if BUILD_FTS_TEXTCAT -TEXTCAT_LIBS = -ltextcat -endif -endif - -lib21_fts_lucene_plugin_la_LIBADD = \ - $(CLUCENE_LIBS) $(TEXTCAT_LIBS) $(STEMMER_LIBS) - -lib21_fts_lucene_plugin_la_SOURCES = \ - fts-lucene-plugin.c \ - fts-backend-lucene.c \ - lucene-wrapper.cc \ - $(SHOWBALL_SOURCES) - -noinst_HEADERS = \ - fts-lucene-plugin.h \ - lucene-wrapper.h \ - SnowballAnalyzer.h \ - SnowballFilter.h - -if BUILD_FTS_TEXTCAT -exampledir = $(docdir)/example-config -example_DATA = \ - textcat.conf -endif -EXTRA_DIST = textcat.conf - -doveadm_module_LTLIBRARIES = \ - lib20_doveadm_fts_lucene_plugin.la - -lib20_doveadm_fts_lucene_plugin_la_SOURCES = \ - doveadm-fts-lucene.c diff --git a/src/plugins/fts-lucene/Snowball.cc b/src/plugins/fts-lucene/Snowball.cc deleted file mode 100644 index 43b54e36e9..0000000000 --- a/src/plugins/fts-lucene/Snowball.cc +++ /dev/null @@ -1,151 +0,0 @@ -/*------------------------------------------------------------------------------ -* Copyright (C) 2003-2006 Ben van Klinken and the CLucene Team -* -* Distributable under the terms of either the Apache License (Version 2.0) or -* the GNU Lesser General Public License, as specified in the COPYING file. -------------------------------------------------------------------------------*/ -#include -#include "SnowballAnalyzer.h" -#include "SnowballFilter.h" -#include -#include -#include -#include - -extern "C" { -#include "lib.h" -#include "buffer.h" -#include "unichar.h" -#include "lucene-wrapper.h" -}; - -CL_NS_USE(analysis) -CL_NS_USE(util) -CL_NS_USE2(analysis,standard) - -CL_NS_DEF2(analysis,snowball) - - /** Builds the named analyzer with no stop words. */ - SnowballAnalyzer::SnowballAnalyzer(normalizer_func_t *_normalizer, const char* _language) - : language(i_strdup(_language)), - normalizer(_normalizer), - stopSet(NULL), - prevstream(NULL) - { - } - - SnowballAnalyzer::~SnowballAnalyzer() - { - if (prevstream) - _CLDELETE(prevstream); - i_free(language); - if ( stopSet != NULL ) - _CLDELETE(stopSet); - } - - /** Builds the named analyzer with the given stop words. - */ - SnowballAnalyzer::SnowballAnalyzer(const char* language, const TCHAR** stopWords) - : language(i_strdup(language)), - normalizer(NULL), - stopSet(_CLNEW CLTCSetList(true)), - prevstream(NULL) - { - StopFilter::fillStopTable(stopSet,stopWords); - } - - TokenStream* SnowballAnalyzer::tokenStream(const TCHAR* fieldName, CL_NS(util)::Reader* reader) { - return this->tokenStream(fieldName,reader,false); - } - - /** Constructs a {@link StandardTokenizer} filtered by a {@link - StandardFilter}, a {@link LowerCaseFilter} and a {@link StopFilter}. */ - TokenStream* SnowballAnalyzer::tokenStream(const TCHAR* fieldName, CL_NS(util)::Reader* reader, bool deleteReader) { - BufferedReader* bufferedReader = reader->__asBufferedReader(); - TokenStream* result; - - if ( bufferedReader == NULL ) - result = _CLNEW StandardTokenizer( _CLNEW FilteredBufferedReader(reader, deleteReader), true ); - else - result = _CLNEW StandardTokenizer(bufferedReader, deleteReader); - - result = _CLNEW StandardFilter(result, true); - result = _CLNEW CL_NS(analysis)::LowerCaseFilter(result, true); - if (stopSet != NULL) - result = _CLNEW CL_NS(analysis)::StopFilter(result, true, stopSet); - result = _CLNEW SnowballFilter(result, normalizer, language, true); - return result; - } - - TokenStream* SnowballAnalyzer::reusableTokenStream(const TCHAR* fieldName, CL_NS(util)::Reader* reader) { - if (prevstream) _CLDELETE(prevstream); - prevstream = this->tokenStream(fieldName, reader); - return prevstream; - } - - - - - - - /** Construct the named stemming filter. - * - * @param in the input tokens to stem - * @param name the name of a stemmer - */ - SnowballFilter::SnowballFilter(TokenStream* in, normalizer_func_t *normalizer, const char* language, bool deleteTS): - TokenFilter(in,deleteTS) - { - stemmer = sb_stemmer_new(language, NULL); //use utf8 encoding - this->normalizer = normalizer; - - if ( stemmer == NULL ){ - _CLTHROWA(CL_ERR_IllegalArgument, "language not available for stemming\n"); //todo: richer error - } - } - - SnowballFilter::~SnowballFilter(){ - sb_stemmer_delete(stemmer); - } - - /** Returns the next input Token, after being stemmed */ - Token* SnowballFilter::next(Token* token){ - if (input->next(token) == NULL) - return NULL; - - unsigned char utf8text[LUCENE_MAX_WORD_LEN*5+1]; - unsigned int len = I_MIN(LUCENE_MAX_WORD_LEN, token->termLength()); - - buffer_t buf = { { 0, 0 } }; - i_assert(sizeof(wchar_t) == sizeof(unichar_t)); - buffer_create_from_data(&buf, utf8text, sizeof(utf8text)); - uni_ucs4_to_utf8((const unichar_t *)token->termBuffer(), len, &buf); - - const sb_symbol* stemmed = sb_stemmer_stem(stemmer, utf8text, buf.used); - if ( stemmed == NULL ) - _CLTHROWA(CL_ERR_Runtime,"Out of memory"); - - int stemmedLen=sb_stemmer_length(stemmer); - - if (normalizer == NULL) { - unsigned int tchartext_size = - uni_utf8_strlen_n(stemmed, stemmedLen) + 1; - TCHAR tchartext[tchartext_size]; - lucene_utf8_n_to_tchar(stemmed, stemmedLen, tchartext, tchartext_size); - token->set(tchartext,token->startOffset(), token->endOffset(), token->type()); - } else T_BEGIN { - buffer_t *norm_buf = t_buffer_create(stemmedLen); - normalizer(stemmed, stemmedLen, norm_buf); - - unsigned int tchartext_size = - uni_utf8_strlen_n(norm_buf->data, norm_buf->used) + 1; - TCHAR tchartext[tchartext_size]; - lucene_utf8_n_to_tchar((const unsigned char *)norm_buf->data, - norm_buf->used, tchartext, tchartext_size); - token->set(tchartext,token->startOffset(), token->endOffset(), token->type()); - } T_END; - return token; - } - - -CL_NS_END2 diff --git a/src/plugins/fts-lucene/SnowballAnalyzer.h b/src/plugins/fts-lucene/SnowballAnalyzer.h deleted file mode 100644 index 45455c50d1..0000000000 --- a/src/plugins/fts-lucene/SnowballAnalyzer.h +++ /dev/null @@ -1,51 +0,0 @@ -/*------------------------------------------------------------------------------ -* Copyright (C) 2003-2006 Ben van Klinken and the CLucene Team -* -* Distributable under the terms of either the Apache License (Version 2.0) or -* the GNU Lesser General Public License, as specified in the COPYING file. -------------------------------------------------------------------------------*/ -#ifndef _lucene_analysis_snowball_analyser_ -#define _lucene_analysis_snowball_analyser_ - -extern "C" { -#include "lib.h" -#include "unichar.h" -}; -#include "CLucene/analysis/AnalysisHeader.h" - -CL_CLASS_DEF(util,BufferedReader) -CL_NS_DEF2(analysis,snowball) - -/** Filters {@link StandardTokenizer} with {@link StandardFilter}, {@link - * LowerCaseFilter}, {@link StopFilter} and {@link SnowballFilter}. - * - * Available stemmers are listed in {@link net.sf.snowball.ext}. The name of a - * stemmer is the part of the class name before "Stemmer", e.g., the stemmer in - * {@link EnglishStemmer} is named "English". - */ -class CLUCENE_CONTRIBS_EXPORT SnowballAnalyzer: public Analyzer { - char* language; - normalizer_func_t *normalizer; - CLTCSetList* stopSet; - TokenStream *prevstream; - -public: - /** Builds the named analyzer with no stop words. */ - SnowballAnalyzer(normalizer_func_t *normalizer, const char* language="english"); - - /** Builds the named analyzer with the given stop words. - */ - SnowballAnalyzer(const char* language, const TCHAR** stopWords); - - ~SnowballAnalyzer(); - - /** Constructs a {@link StandardTokenizer} filtered by a {@link - StandardFilter}, a {@link LowerCaseFilter} and a {@link StopFilter}. */ - TokenStream* tokenStream(const TCHAR* fieldName, CL_NS(util)::Reader* reader); - TokenStream* tokenStream(const TCHAR* fieldName, CL_NS(util)::Reader* reader, bool deleteReader); - TokenStream* reusableTokenStream(const TCHAR* fieldName, CL_NS(util)::Reader* reader); -}; - -CL_NS_END2 -#endif - diff --git a/src/plugins/fts-lucene/SnowballFilter.h b/src/plugins/fts-lucene/SnowballFilter.h deleted file mode 100644 index 6a0ed122a5..0000000000 --- a/src/plugins/fts-lucene/SnowballFilter.h +++ /dev/null @@ -1,42 +0,0 @@ -/*------------------------------------------------------------------------------ -* Copyright (C) 2003-2006 Ben van Klinken and the CLucene Team -* -* Distributable under the terms of either the Apache License (Version 2.0) or -* the GNU Lesser General Public License, as specified in the COPYING file. -------------------------------------------------------------------------------*/ -#ifndef _lucene_analysis_snowball_filter_ -#define _lucene_analysis_snowball_filter_ - -#include "CLucene/analysis/AnalysisHeader.h" -#include "libstemmer.h" - -CL_NS_DEF2(analysis,snowball) - -/** A filter that stems words using a Snowball-generated stemmer. - * - * Available stemmers are listed in {@link net.sf.snowball.ext}. The name of a - * stemmer is the part of the class name before "Stemmer", e.g., the stemmer in - * {@link EnglishStemmer} is named "English". - * - * Note: todo: This is not thread safe... - */ -class CLUCENE_CONTRIBS_EXPORT SnowballFilter: public TokenFilter { - struct sb_stemmer * stemmer; - normalizer_func_t *normalizer; -public: - - /** Construct the named stemming filter. - * - * @param in the input tokens to stem - * @param name the name of a stemmer - */ - SnowballFilter(TokenStream* in, normalizer_func_t *normalizer, const char* language, bool deleteTS); - - ~SnowballFilter(); - - /** Returns the next input Token, after being stemmed */ - Token* next(Token* token); -}; - -CL_NS_END2 -#endif diff --git a/src/plugins/fts-lucene/doveadm-fts-lucene.c b/src/plugins/fts-lucene/doveadm-fts-lucene.c deleted file mode 100644 index a761907009..0000000000 --- a/src/plugins/fts-lucene/doveadm-fts-lucene.c +++ /dev/null @@ -1,70 +0,0 @@ -/* Copyright (c) 2011-2018 Dovecot authors, see the included COPYING file */ - -#include "lib.h" -#include "doveadm-dump.h" -#include "doveadm-fts.h" -#include "lucene-wrapper.h" - -#include -#include - -const char *doveadm_fts_lucene_plugin_version = DOVECOT_ABI_VERSION; - -void doveadm_fts_lucene_plugin_init(struct module *module); -void doveadm_fts_lucene_plugin_deinit(void); - -static void -cmd_dump_fts_lucene(const char *path, const char *const *args ATTR_UNUSED) -{ - struct lucene_index *index; - struct lucene_index_iter *iter; - guid_128_t prev_guid; - const struct lucene_index_record *rec; - bool first = TRUE; - - i_zero(&prev_guid); - index = lucene_index_init(path, NULL, NULL); - iter = lucene_index_iter_init(index); - while ((rec = lucene_index_iter_next(iter)) != NULL) { - if (memcmp(prev_guid, rec->mailbox_guid, - sizeof(prev_guid)) != 0) { - if (first) - first = FALSE; - else - printf("\n"); - memcpy(prev_guid, rec->mailbox_guid, sizeof(prev_guid)); - printf("%s: ", guid_128_to_string(prev_guid)); - } - printf("%u", rec->uid); - if (rec->part_num != 0) - printf("[%u]", rec->part_num); - printf("\n"); - } - printf("\n"); - if (lucene_index_iter_deinit(&iter) < 0) - i_error("Lucene index iteration failed"); - lucene_index_deinit(index); -} - -static bool test_dump_fts_lucene(const char *path) -{ - struct stat st; - - path = t_strconcat(path, "/segments.gen", NULL); - return stat(path, &st) == 0; -} - -static const struct doveadm_cmd_dump doveadm_cmd_dump_fts_lucene = { - "fts-lucene", - test_dump_fts_lucene, - cmd_dump_fts_lucene -}; - -void doveadm_fts_lucene_plugin_init(struct module *module ATTR_UNUSED) -{ - doveadm_dump_register(&doveadm_cmd_dump_fts_lucene); -} - -void doveadm_fts_lucene_plugin_deinit(void) -{ -} diff --git a/src/plugins/fts-lucene/fts-backend-lucene.c b/src/plugins/fts-lucene/fts-backend-lucene.c deleted file mode 100644 index 963dbdfdb4..0000000000 --- a/src/plugins/fts-lucene/fts-backend-lucene.c +++ /dev/null @@ -1,605 +0,0 @@ -/* Copyright (c) 2006-2018 Dovecot authors, see the included COPYING file */ - -#include "lib.h" -#include "array.h" -#include "hash.h" -#include "hex-binary.h" -#include "strescape.h" -#include "message-part.h" -#include "mail-namespace.h" -#include "mail-storage-private.h" -#include "fts-expunge-log.h" -#include "lucene-wrapper.h" -#include "fts-indexer.h" -#include "fts-lucene-plugin.h" - -#include - -#define LUCENE_INDEX_DIR_NAME "lucene-indexes" -#define LUCENE_EXPUNGE_LOG_NAME "dovecot-expunges.log" -#define LUCENE_OPTIMIZE_BATCH_MSGS_COUNT 100 - -struct lucene_fts_backend { - struct fts_backend backend; - char *dir_path; - - struct lucene_index *index; - struct mailbox *selected_box; - unsigned int selected_box_generation; - guid_128_t selected_box_guid; - - struct fts_expunge_log *expunge_log; - - bool dir_created:1; - bool updating:1; -}; - -struct lucene_fts_backend_update_context { - struct fts_backend_update_context ctx; - - struct mailbox *box; - uint32_t last_uid; - uint32_t last_indexed_uid; - char *first_box_vname; - - uint32_t uid, part_num; - char *hdr_name; - - unsigned int added_msgs; - struct fts_expunge_log_append_ctx *expunge_ctx; - - bool lucene_opened; - bool last_indexed_uid_set; - bool mime_parts; -}; - -static int fts_backend_lucene_mkdir(struct lucene_fts_backend *backend) -{ - if (backend->dir_created) - return 0; - - backend->dir_created = TRUE; - if (mailbox_list_mkdir_root(backend->backend.ns->list, - backend->dir_path, - MAILBOX_LIST_PATH_TYPE_INDEX) < 0) - return -1; - return 0; -} - -static int -fts_lucene_get_mailbox_guid(struct mailbox *box, guid_128_t guid_r) -{ - struct mailbox_metadata metadata; - - if (mailbox_get_metadata(box, MAILBOX_METADATA_GUID, - &metadata) < 0) { - i_error("lucene: Couldn't get mailbox %s GUID: %s", - box->vname, mailbox_get_last_internal_error(box, NULL)); - return -1; - } - memcpy(guid_r, metadata.guid, GUID_128_SIZE); - return 0; -} - -static int -fts_backend_select(struct lucene_fts_backend *backend, struct mailbox *box) -{ - guid_128_t guid; - unsigned char guid_hex[MAILBOX_GUID_HEX_LENGTH]; - wchar_t wguid_hex[MAILBOX_GUID_HEX_LENGTH]; - buffer_t buf; - unsigned int i; - - i_assert(box != NULL); - - if (backend->selected_box == box && - backend->selected_box_generation == box->generation_sequence) - return 0; - - if (fts_lucene_get_mailbox_guid(box, guid) < 0) - return -1; - buffer_create_from_data(&buf, guid_hex, MAILBOX_GUID_HEX_LENGTH); - binary_to_hex_append(&buf, guid, GUID_128_SIZE); - for (i = 0; i < N_ELEMENTS(wguid_hex); i++) - wguid_hex[i] = guid_hex[i]; - - lucene_index_select_mailbox(backend->index, wguid_hex); - - backend->selected_box = box; - memcpy(backend->selected_box_guid, guid, - sizeof(backend->selected_box_guid)); - backend->selected_box_generation = box->generation_sequence; - return 0; -} - -static struct fts_backend *fts_backend_lucene_alloc(void) -{ - struct lucene_fts_backend *backend; - - backend = i_new(struct lucene_fts_backend, 1); - backend->backend = fts_backend_lucene; - return &backend->backend; -} - -static int -fts_backend_lucene_init(struct fts_backend *_backend, const char **error_r) -{ - struct lucene_fts_backend *backend = - (struct lucene_fts_backend *)_backend; - struct fts_lucene_user *fuser = - FTS_LUCENE_USER_CONTEXT(_backend->ns->user); - const char *path; - - if (fuser == NULL) { - /* invalid settings */ - *error_r = "Invalid fts_lucene settings"; - return -1; - } - /* fts already checked that index exists */ - - if (fuser->set.use_libfts) { - /* change our flags so we get proper input */ - _backend->flags &= ENUM_NEGATE(FTS_BACKEND_FLAG_FUZZY_SEARCH); - _backend->flags |= FTS_BACKEND_FLAG_TOKENIZED_INPUT; - } - path = mailbox_list_get_root_forced(_backend->ns->list, - MAILBOX_LIST_PATH_TYPE_INDEX); - - backend->dir_path = i_strconcat(path, "/"LUCENE_INDEX_DIR_NAME, NULL); - backend->index = lucene_index_init(backend->dir_path, - _backend->ns->list, - &fuser->set); - - path = t_strconcat(backend->dir_path, "/"LUCENE_EXPUNGE_LOG_NAME, NULL); - backend->expunge_log = fts_expunge_log_init(path); - return 0; -} - -static void fts_backend_lucene_deinit(struct fts_backend *_backend) -{ - struct lucene_fts_backend *backend = - (struct lucene_fts_backend *)_backend; - - if (backend->index != NULL) - lucene_index_deinit(backend->index); - if (backend->expunge_log != NULL) - fts_expunge_log_deinit(&backend->expunge_log); - i_free(backend->dir_path); - i_free(backend); -} - -static int -fts_backend_lucene_get_last_uid(struct fts_backend *_backend, - struct mailbox *box, uint32_t *last_uid_r) -{ - struct lucene_fts_backend *backend = - (struct lucene_fts_backend *)_backend; - struct fts_lucene_user *fuser = - FTS_LUCENE_USER_CONTEXT_REQUIRE(_backend->ns->user); - struct fts_index_header hdr; - uint32_t set_checksum; - int ret; - - if (fts_index_get_header(box, &hdr)) { - set_checksum = fts_lucene_settings_checksum(&fuser->set); - ret = fts_index_have_compatible_settings(_backend->ns->list, - set_checksum); - if (ret < 0) - return -1; - if (ret == 0) { - /* need to rebuild the index */ - *last_uid_r = 0; - } else { - *last_uid_r = hdr.last_indexed_uid; - } - return 0; - } - - /* either nothing has been indexed, or the index was corrupted. - do it the slow way. */ - if (fts_backend_select(backend, box) < 0) - return -1; - if (lucene_index_get_last_uid(backend->index, last_uid_r) < 0) - return -1; - - fts_index_set_last_uid(box, *last_uid_r); - return 0; -} - -static struct fts_backend_update_context * -fts_backend_lucene_update_init(struct fts_backend *_backend) -{ - struct lucene_fts_backend *backend = - (struct lucene_fts_backend *)_backend; - struct lucene_fts_backend_update_context *ctx; - struct fts_lucene_user *fuser = - FTS_LUCENE_USER_CONTEXT_REQUIRE(_backend->ns->user); - - i_assert(!backend->updating); - - ctx = i_new(struct lucene_fts_backend_update_context, 1); - ctx->ctx.backend = _backend; - ctx->mime_parts = fuser->set.mime_parts; - backend->updating = TRUE; - return &ctx->ctx; -} - -static bool -fts_backend_lucene_need_optimize(struct lucene_fts_backend_update_context *ctx) -{ - struct lucene_fts_backend *backend = - (struct lucene_fts_backend *)ctx->ctx.backend; - unsigned int expunges; - uint32_t numdocs; - - if (ctx->added_msgs >= LUCENE_OPTIMIZE_BATCH_MSGS_COUNT) - return TRUE; - if (lucene_index_get_doc_count(backend->index, &numdocs) < 0) - return FALSE; - - if (fts_expunge_log_uid_count(backend->expunge_log, &expunges) < 0) - return FALSE; - return expunges > 0 && - numdocs / expunges <= 50; /* >2% of index has been expunged */ -} - -static int -fts_backend_lucene_update_deinit(struct fts_backend_update_context *_ctx) -{ - struct lucene_fts_backend_update_context *ctx = - (struct lucene_fts_backend_update_context *)_ctx; - struct lucene_fts_backend *backend = - (struct lucene_fts_backend *)_ctx->backend; - int ret = _ctx->failed ? -1 : 0; - - i_assert(backend->updating); - - backend->updating = FALSE; - if (ctx->lucene_opened) { - if (lucene_index_build_deinit(backend->index) < 0) - ret = -1; - } - - if (ctx->expunge_ctx != NULL) { - if (fts_expunge_log_append_commit(&ctx->expunge_ctx) < 0) { - struct stat st; - ret = -1; - - if (stat(backend->dir_path, &st) < 0 && errno == ENOENT) { - /* lucene-indexes directory doesn't even exist, - so dovecot.index's last_index_uid is wrong. - rescan to update them. */ - (void)lucene_index_rescan(backend->index); - ret = 0; - } - } - } - - if (fts_backend_lucene_need_optimize(ctx)) { - if (ctx->lucene_opened) - (void)fts_backend_optimize(_ctx->backend); - else if (ctx->first_box_vname != NULL) { - struct mail_user *user = backend->backend.ns->user; - const char *cmd, *path; - int fd; - - /* the optimize affects all mailboxes within namespace, - so just use any mailbox name in it */ - cmd = t_strdup_printf("OPTIMIZE\t0\t%s\t%s\n", - str_tabescape(user->username), - str_tabescape(ctx->first_box_vname)); - fd = fts_indexer_cmd(user, cmd, &path); - i_close_fd(&fd); - } - } - - i_free(ctx->first_box_vname); - i_free(ctx); - return ret; -} - -static void -fts_backend_lucene_update_set_mailbox(struct fts_backend_update_context *_ctx, - struct mailbox *box) -{ - struct lucene_fts_backend_update_context *ctx = - (struct lucene_fts_backend_update_context *)_ctx; - - if (ctx->last_uid != 0) { - fts_index_set_last_uid(ctx->box, ctx->last_uid); - ctx->last_uid = 0; - } - if (ctx->first_box_vname == NULL && box != NULL) - ctx->first_box_vname = i_strdup(box->vname); - ctx->box = box; - ctx->last_indexed_uid_set = FALSE; -} - -static void -fts_backend_lucene_update_expunge(struct fts_backend_update_context *_ctx, - uint32_t uid) -{ - struct lucene_fts_backend_update_context *ctx = - (struct lucene_fts_backend_update_context *)_ctx; - struct lucene_fts_backend *backend = - (struct lucene_fts_backend *)_ctx->backend; - struct fts_index_header hdr; - - if (!ctx->last_indexed_uid_set) { - if (!fts_index_get_header(ctx->box, &hdr)) - ctx->last_indexed_uid = 0; - else - ctx->last_indexed_uid = hdr.last_indexed_uid; - ctx->last_indexed_uid_set = TRUE; - } - if (ctx->last_indexed_uid == 0 || - uid > ctx->last_indexed_uid + 100) { - /* don't waste time adding expunge to log for a message that - isn't even indexed. this check is racy, because indexer may - just be in the middle of indexing this message. we'll - attempt to avoid that by skipping the expunging only if - indexing hasn't been done for a while (100 msgs). */ - return; - } - - if (ctx->expunge_ctx == NULL) { - ctx->expunge_ctx = - fts_expunge_log_append_begin(backend->expunge_log); - } - - if (fts_backend_select(backend, ctx->box) < 0) - _ctx->failed = TRUE; - - fts_expunge_log_append_next(ctx->expunge_ctx, - backend->selected_box_guid, uid); -} - -static bool -fts_backend_lucene_update_set_build_key(struct fts_backend_update_context *_ctx, - const struct fts_backend_build_key *key) -{ - struct lucene_fts_backend_update_context *ctx = - (struct lucene_fts_backend_update_context *)_ctx; - struct lucene_fts_backend *backend = - (struct lucene_fts_backend *)_ctx->backend; - - if (!ctx->lucene_opened) { - if (fts_backend_lucene_mkdir(backend) < 0) - ctx->ctx.failed = TRUE; - if (lucene_index_build_init(backend->index) < 0) - ctx->ctx.failed = TRUE; - ctx->lucene_opened = TRUE; - } - - if (fts_backend_select(backend, ctx->box) < 0) - _ctx->failed = TRUE; - - switch (key->type) { - case FTS_BACKEND_BUILD_KEY_HDR: - case FTS_BACKEND_BUILD_KEY_MIME_HDR: - i_assert(key->hdr_name != NULL); - - i_free(ctx->hdr_name); - ctx->hdr_name = i_strdup(key->hdr_name); - break; - case FTS_BACKEND_BUILD_KEY_BODY_PART: - i_free_and_null(ctx->hdr_name); - break; - case FTS_BACKEND_BUILD_KEY_BODY_PART_BINARY: - i_unreached(); - } - - if (key->uid != ctx->last_uid) { - i_assert(key->uid >= ctx->last_uid); - ctx->last_uid = key->uid; - ctx->added_msgs++; - } - - ctx->uid = key->uid; - if (ctx->mime_parts) - ctx->part_num = message_part_to_idx(key->part); - return TRUE; -} - -static void -fts_backend_lucene_update_unset_build_key(struct fts_backend_update_context *_ctx) -{ - struct lucene_fts_backend_update_context *ctx = - (struct lucene_fts_backend_update_context *)_ctx; - - ctx->uid = 0; - ctx->part_num = 0; - i_free_and_null(ctx->hdr_name); -} - -static int -fts_backend_lucene_update_build_more(struct fts_backend_update_context *_ctx, - const unsigned char *data, size_t size) -{ - struct lucene_fts_backend_update_context *ctx = - (struct lucene_fts_backend_update_context *)_ctx; - struct lucene_fts_backend *backend = - (struct lucene_fts_backend *)_ctx->backend; - int ret; - - i_assert(ctx->uid != 0); - - if (_ctx->failed) - return -1; - - T_BEGIN { - ret = lucene_index_build_more(backend->index, ctx->uid, - ctx->part_num, data, size, - ctx->hdr_name); - } T_END; - return ret; -} - -static int -fts_backend_lucene_refresh(struct fts_backend *_backend) -{ - struct lucene_fts_backend *backend = - (struct lucene_fts_backend *)_backend; - - if (backend->index != NULL) - lucene_index_close(backend->index); - return 0; -} - -static int fts_backend_lucene_rescan(struct fts_backend *_backend) -{ - struct lucene_fts_backend *backend = - (struct lucene_fts_backend *)_backend; - - if (lucene_index_rescan(backend->index) < 0) - return -1; - return lucene_index_optimize(backend->index); -} - -static int fts_backend_lucene_optimize(struct fts_backend *_backend) -{ - struct lucene_fts_backend *backend = - (struct lucene_fts_backend *)_backend; - int ret; - - ret = lucene_index_expunge_from_log(backend->index, - backend->expunge_log); - if (ret == 0) { - /* log was corrupted, need to rescan */ - ret = lucene_index_rescan(backend->index); - } - if (ret >= 0) - ret = lucene_index_optimize(backend->index); - return ret; -} - -static int -fts_backend_lucene_lookup(struct fts_backend *_backend, struct mailbox *box, - struct mail_search_arg *args, - enum fts_lookup_flags flags, - struct fts_result *result) -{ - struct lucene_fts_backend *backend = - (struct lucene_fts_backend *)_backend; - int ret; - - if (fts_backend_select(backend, box) < 0) - return -1; - T_BEGIN { - ret = lucene_index_lookup(backend->index, args, flags, result); - } T_END; - return ret; -} - -/* a char* hash function from ASU -- from glib */ -static unsigned int wstr_hash(const wchar_t *s) -{ - unsigned int g, h = 0; - - while (*s != '\0') { - h = (h << 4) + *s; - if ((g = h & 0xf0000000UL) != 0) { - h = h ^ (g >> 24); - h = h ^ g; - } - s++; - } - - return h; -} - -static int -mailboxes_get_guids(struct mailbox *const boxes[], - HASH_TABLE_TYPE(wguid_result) guids, - struct fts_multi_result *result) -{ - ARRAY(struct fts_result) box_results; - struct fts_result *box_result; - const char *guid; - wchar_t *guid_dup; - unsigned int i, j; - - p_array_init(&box_results, result->pool, 32); - /* first create the box_results - we'll be using pointers to them - later on and appending to the array changes the pointers */ - for (i = 0; boxes[i] != NULL; i++) { - box_result = array_append_space(&box_results); - box_result->box = boxes[i]; - } - for (i = 0; boxes[i] != NULL; i++) { - if (fts_mailbox_get_guid(boxes[i], &guid) < 0) - return -1; - - i_assert(strlen(guid) == MAILBOX_GUID_HEX_LENGTH); - guid_dup = t_new(wchar_t, MAILBOX_GUID_HEX_LENGTH + 1); - for (j = 0; j < MAILBOX_GUID_HEX_LENGTH; j++) - guid_dup[j] = guid[j]; - - box_result = array_idx_modifiable(&box_results, i); - hash_table_insert(guids, guid_dup, box_result); - } - - array_append_zero(&box_results); - result->box_results = array_front_modifiable(&box_results); - return 0; -} - -static int -fts_backend_lucene_lookup_multi(struct fts_backend *_backend, - struct mailbox *const boxes[], - struct mail_search_arg *args, - enum fts_lookup_flags flags, - struct fts_multi_result *result) -{ - struct lucene_fts_backend *backend = - (struct lucene_fts_backend *)_backend; - int ret; - - T_BEGIN { - HASH_TABLE_TYPE(wguid_result) guids; - - hash_table_create(&guids, default_pool, 0, wstr_hash, wcscmp); - ret = mailboxes_get_guids(boxes, guids, result); - if (ret == 0) { - ret = lucene_index_lookup_multi(backend->index, - guids, args, flags, - result); - } - hash_table_destroy(&guids); - } T_END; - return ret; -} - -static void fts_backend_lucene_lookup_done(struct fts_backend *_backend) -{ - /* the next refresh is going to close the index anyway, so we might as - well do it now */ - (void)fts_backend_lucene_refresh(_backend); -} - -struct fts_backend fts_backend_lucene = { - .name = "lucene", - .flags = FTS_BACKEND_FLAG_BUILD_FULL_WORDS | - FTS_BACKEND_FLAG_FUZZY_SEARCH, - - { - fts_backend_lucene_alloc, - fts_backend_lucene_init, - fts_backend_lucene_deinit, - fts_backend_lucene_get_last_uid, - fts_backend_lucene_update_init, - fts_backend_lucene_update_deinit, - fts_backend_lucene_update_set_mailbox, - fts_backend_lucene_update_expunge, - fts_backend_lucene_update_set_build_key, - fts_backend_lucene_update_unset_build_key, - fts_backend_lucene_update_build_more, - fts_backend_lucene_refresh, - fts_backend_lucene_rescan, - fts_backend_lucene_optimize, - fts_backend_default_can_lookup, - fts_backend_lucene_lookup, - fts_backend_lucene_lookup_multi, - fts_backend_lucene_lookup_done - } -}; diff --git a/src/plugins/fts-lucene/fts-lucene-plugin.c b/src/plugins/fts-lucene/fts-lucene-plugin.c deleted file mode 100644 index 7c58fa7a4a..0000000000 --- a/src/plugins/fts-lucene/fts-lucene-plugin.c +++ /dev/null @@ -1,146 +0,0 @@ -/* Copyright (c) 2006-2018 Dovecot authors, see the included COPYING file */ - -#include "lib.h" -#include "crc32.h" -#include "mail-storage-hooks.h" -#include "lucene-wrapper.h" -#include "fts-user.h" -#include "fts-lucene-plugin.h" - -const char *fts_lucene_plugin_version = DOVECOT_ABI_VERSION; - -struct fts_lucene_user_module fts_lucene_user_module = - MODULE_CONTEXT_INIT(&mail_user_module_register); - -static int -fts_lucene_plugin_init_settings(struct mail_user *user, - struct fts_lucene_settings *set, - const char *str) -{ - const char *const *tmp; - - for (tmp = t_strsplit_spaces(str, " "); *tmp != NULL; tmp++) { - if (str_begins(*tmp, "default_language=")) { - set->default_language = - p_strdup(user->pool, *tmp + 17); - } else if (str_begins(*tmp, "textcat_conf=")) { - set->textcat_conf = p_strdup(user->pool, *tmp + 13); - } else if (str_begins(*tmp, "textcat_dir=")) { - set->textcat_dir = p_strdup(user->pool, *tmp + 12); - } else if (str_begins(*tmp, "whitespace_chars=")) { - set->whitespace_chars = p_strdup(user->pool, *tmp + 17); - } else if (strcmp(*tmp, "normalize") == 0) { - set->normalize = TRUE; - } else if (strcmp(*tmp, "no_snowball") == 0) { - set->no_snowball = TRUE; - } else if (strcmp(*tmp, "mime_parts") == 0) { - set->mime_parts = TRUE; - } else if (strcmp(*tmp, "use_libfts") == 0) { - set->use_libfts = TRUE; - } else { - i_error("fts_lucene: Invalid setting: %s", *tmp); - return -1; - } - } - if (set->textcat_conf != NULL && set->textcat_dir == NULL) { - i_error("fts_lucene: textcat_conf set, but textcat_dir unset"); - return -1; - } - if (set->textcat_conf == NULL && set->textcat_dir != NULL) { - i_error("fts_lucene: textcat_dir set, but textcat_conf unset"); - return -1; - } - if (set->whitespace_chars == NULL) - set->whitespace_chars = ""; -#ifndef HAVE_FTS_STEMMER - if (set->default_language != NULL) { - i_error("fts_lucene: default_language set, " - "but Dovecot built without stemmer support"); - return -1; - } -#else - if (set->default_language == NULL) - set->default_language = "english"; -#endif -#ifndef HAVE_FTS_TEXTCAT - if (set->textcat_conf != NULL) { - i_error("fts_lucene: textcat_dir set, " - "but Dovecot built without textcat support"); - return -1; - } -#endif - return 0; -} - -uint32_t fts_lucene_settings_checksum(const struct fts_lucene_settings *set) -{ - uint32_t crc; - - if (set->use_libfts) - return crc32_str("l"); - - /* checksum is always different when compiling with/without stemmer */ - crc = set->default_language == NULL ? 0 : - crc32_str(set->default_language); - crc = crc32_str_more(crc, set->whitespace_chars); - if (set->normalize) - crc = crc32_str_more(crc, "n"); - if (set->no_snowball) - crc = crc32_str_more(crc, "s"); - /* don't include mime_parts here, since changing it doesn't - necessarily need the index to be rebuilt */ - return crc; -} - -static void fts_lucene_mail_user_deinit(struct mail_user *user) -{ - struct fts_lucene_user *fuser = FTS_LUCENE_USER_CONTEXT_REQUIRE(user); - - fts_mail_user_deinit(user); - fuser->module_ctx.super.deinit(user); -} - -static void fts_lucene_mail_user_created(struct mail_user *user) -{ - struct mail_user_vfuncs *v = user->vlast; - struct fts_lucene_user *fuser; - const char *env, *error; - - fuser = p_new(user->pool, struct fts_lucene_user, 1); - env = mail_user_plugin_getenv(user, "fts_lucene"); - if (env == NULL) - env = ""; - - if (fts_lucene_plugin_init_settings(user, &fuser->set, env) < 0) { - /* invalid settings, disabling */ - return; - } - if (fts_mail_user_init(user, fuser->set.use_libfts, &error) < 0) { - i_error("fts_lucene: %s", error); - return; - } - - fuser->module_ctx.super = *v; - user->vlast = &fuser->module_ctx.super; - v->deinit = fts_lucene_mail_user_deinit; - MODULE_CONTEXT_SET(user, fts_lucene_user_module, fuser); -} - -static struct mail_storage_hooks fts_lucene_mail_storage_hooks = { - .mail_user_created = fts_lucene_mail_user_created -}; - -void fts_lucene_plugin_init(struct module *module ATTR_UNUSED) -{ - fts_backend_register(&fts_backend_lucene); - mail_storage_hooks_add(module, &fts_lucene_mail_storage_hooks); -} - -void fts_lucene_plugin_deinit(void) -{ - fts_backend_unregister(fts_backend_lucene.name); - mail_storage_hooks_remove(&fts_lucene_mail_storage_hooks); - lucene_shutdown(); -} - -const char *fts_lucene_plugin_dependencies[] = { "fts", NULL }; diff --git a/src/plugins/fts-lucene/fts-lucene-plugin.h b/src/plugins/fts-lucene/fts-lucene-plugin.h deleted file mode 100644 index 69440fb14a..0000000000 --- a/src/plugins/fts-lucene/fts-lucene-plugin.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef FTS_LUCENE_PLUGIN_H -#define FTS_LUCENE_PLUGIN_H - -#include "module-context.h" -#include "mail-user.h" -#include "fts-api-private.h" - -#define FTS_LUCENE_USER_CONTEXT(obj) \ - MODULE_CONTEXT(obj, fts_lucene_user_module) -#define FTS_LUCENE_USER_CONTEXT_REQUIRE(obj) \ - MODULE_CONTEXT_REQUIRE(obj, fts_lucene_user_module) - -struct fts_lucene_settings { - const char *default_language; - const char *textcat_conf, *textcat_dir; - const char *whitespace_chars; - bool normalize; - bool no_snowball; - bool mime_parts; - bool use_libfts; -}; - -struct fts_lucene_user { - union mail_user_module_context module_ctx; - struct fts_lucene_settings set; -}; - -extern struct fts_backend fts_backend_lucene; -extern MODULE_CONTEXT_DEFINE(fts_lucene_user_module, &mail_user_module_register); - -uint32_t fts_lucene_settings_checksum(const struct fts_lucene_settings *set); - -void fts_lucene_plugin_init(struct module *module); -void fts_lucene_plugin_deinit(void); - -#endif diff --git a/src/plugins/fts-lucene/lucene-wrapper.cc b/src/plugins/fts-lucene/lucene-wrapper.cc deleted file mode 100644 index 744669386b..0000000000 --- a/src/plugins/fts-lucene/lucene-wrapper.cc +++ /dev/null @@ -1,1639 +0,0 @@ -/* Copyright (c) 2006-2018 Dovecot authors, see the included COPYING file */ - -extern "C" { -#include "lib.h" -#include "array.h" -#include "unichar.h" -#include "hash.h" -#include "hex-binary.h" -#include "ioloop.h" -#include "unlink-directory.h" -#include "ioloop.h" -#include "mail-index.h" -#include "mail-search.h" -#include "mail-namespace.h" -#include "mailbox-list-private.h" -#include "mail-storage.h" -#include "fts-expunge-log.h" -#include "fts-lucene-plugin.h" -#include "lucene-wrapper.h" - -#include -#ifdef HAVE_LIBEXTTEXTCAT_TEXTCAT_H -# include -#elif defined (HAVE_LIBTEXTCAT_TEXTCAT_H) -# include -#elif defined (HAVE_FTS_TEXTCAT) -# include -#endif -}; -#include -#include -#include -#include "SnowballAnalyzer.h" - -/* Lucene's default is 10000. Use it here also.. */ -#define MAX_TERMS_PER_DOCUMENT 10000 -#define FTS_LUCENE_MAX_SEARCH_TERMS 1000 - -#define LUCENE_LOCK_OVERRIDE_SECS 60 -#define LUCENE_INDEX_CLOSE_TIMEOUT_MSECS (120*1000) - -using namespace lucene::document; -using namespace lucene::index; -using namespace lucene::search; -using namespace lucene::queryParser; -using namespace lucene::analysis; -using namespace lucene::analysis; -using namespace lucene::util; - -struct lucene_query { - Query *query; - BooleanClause::Occur occur; -}; -ARRAY_DEFINE_TYPE(lucene_query, struct lucene_query); - -struct lucene_analyzer { - char *lang; - Analyzer *analyzer; -}; - -struct lucene_index { - char *path; - struct mailbox_list *list; - struct fts_lucene_settings set; - normalizer_func_t *normalizer; - - wchar_t mailbox_guid[MAILBOX_GUID_HEX_LENGTH + 1]; - - IndexReader *reader; - IndexWriter *writer; - IndexSearcher *searcher; - struct timeout *to_close; - - buffer_t *normalizer_buf; - Analyzer *default_analyzer, *cur_analyzer; - ARRAY(struct lucene_analyzer) analyzers; - - Document *doc; - uint32_t prev_uid, prev_part_idx; - bool no_analyzer; -}; - -struct rescan_context { - struct lucene_index *index; - - struct mailbox *box; - guid_128_t box_guid; - int box_ret; - - pool_t pool; - HASH_TABLE(uint8_t *, uint8_t *) seen_mailbox_guids; - - ARRAY_TYPE(seq_range) uids; - struct seq_range_iter uids_iter; - unsigned int uids_iter_n; - - uint32_t last_existing_uid; - bool warned; -}; - -static void *textcat = NULL; -#ifdef HAVE_FTS_TEXTCAT -static bool textcat_broken = FALSE; -#endif -static int textcat_refcount = 0; - -static void lucene_handle_error(struct lucene_index *index, CLuceneError &err, - const char *msg); -static void rescan_clear_unseen_mailboxes(struct lucene_index *index, - struct rescan_context *rescan_ctx); - -struct lucene_index *lucene_index_init(const char *path, - struct mailbox_list *list, - const struct fts_lucene_settings *set) -{ - struct lucene_index *index; - - index = i_new(struct lucene_index, 1); - index->path = i_strdup(path); - index->list = list; - if (set != NULL) { - index->set = *set; - index->normalizer = !set->normalize ? NULL : - mailbox_list_get_namespace(list)->user->default_normalizer; - } else { - /* this is valid only for doveadm dump, so it doesn't matter */ - index->set.default_language = ""; - } - if (index->set.use_libfts) { - index->default_analyzer = _CLNEW KeywordAnalyzer(); - } else -#ifdef HAVE_FTS_STEMMER - if (set == NULL || !set->no_snowball) { - index->default_analyzer = - _CLNEW snowball::SnowballAnalyzer(index->normalizer, - index->set.default_language); - } else -#endif - { - index->default_analyzer = _CLNEW standard::StandardAnalyzer(); - if (index->normalizer != NULL) { - index->normalizer_buf = - buffer_create_dynamic(default_pool, 1024); - } - } - - i_array_init(&index->analyzers, 32); - textcat_refcount++; - - return index; -} - -void lucene_index_close(struct lucene_index *index) -{ - timeout_remove(&index->to_close); - - _CLDELETE(index->searcher); - if (index->writer != NULL) { - try { - index->writer->close(); - } catch (CLuceneError &err) { - lucene_handle_error(index, err, "IndexWriter::close"); - } - _CLDELETE(index->writer); - } - if (index->reader != NULL) { - try { - index->reader->close(); - } catch (CLuceneError &err) { - lucene_handle_error(index, err, "IndexReader::close"); - } - _CLDELETE(index->reader); - } -} - -void lucene_index_deinit(struct lucene_index *index) -{ - struct lucene_analyzer *a; - - lucene_index_close(index); - array_foreach_modifiable(&index->analyzers, a) { - i_free(a->lang); - _CLDELETE(a->analyzer); - } - array_free(&index->analyzers); - if (--textcat_refcount == 0 && textcat != NULL) { -#ifdef HAVE_FTS_TEXTCAT - textcat_Done(textcat); -#endif - textcat = NULL; - } - _CLDELETE(index->default_analyzer); - if (index->normalizer_buf != NULL) - buffer_free(&index->normalizer_buf); - i_free(index->path); - i_free(index); -} - -static void lucene_data_translate(struct lucene_index *index, - wchar_t *data, unsigned int len) -{ - const char *whitespace_chars = index->set.whitespace_chars; - unsigned int i; - - if (*whitespace_chars == '\0' || index->set.use_libfts) - return; - - for (i = 0; i < len; i++) { - if (strchr(whitespace_chars, data[i]) != NULL) - data[i] = ' '; - } -} - -void lucene_utf8_n_to_tchar(const unsigned char *src, size_t srcsize, - wchar_t *dest, size_t destsize) -{ - ARRAY_TYPE(unichars) dest_arr; - buffer_t buf = { { 0, 0 } }; - - i_assert(sizeof(wchar_t) == sizeof(unichar_t)); - - buffer_create_from_data(&buf, dest, sizeof(wchar_t) * destsize); - array_create_from_buffer(&dest_arr, &buf, sizeof(wchar_t)); - if (uni_utf8_to_ucs4_n(src, srcsize, &dest_arr) < 0) - i_unreached(); - i_assert(array_count(&dest_arr)+1 == destsize); - dest[destsize-1] = 0; -} - -static const wchar_t * -t_lucene_utf8_to_tchar(struct lucene_index *index, const char *str) -{ - ARRAY_TYPE(unichars) dest_arr; - const unichar_t *chars; - wchar_t *ret; - unsigned int len; - - i_assert(sizeof(wchar_t) == sizeof(unichar_t)); - - t_array_init(&dest_arr, strlen(str) + 1); - if (uni_utf8_to_ucs4(str, &dest_arr) < 0) - i_unreached(); - (void)array_append_space(&dest_arr); - - chars = array_get_modifiable(&dest_arr, &len); - ret = (wchar_t *)chars; - lucene_data_translate(index, ret, len - 1); - return ret; -} - -void lucene_index_select_mailbox(struct lucene_index *index, - const wchar_t guid[MAILBOX_GUID_HEX_LENGTH]) -{ - memcpy(index->mailbox_guid, guid, - MAILBOX_GUID_HEX_LENGTH * sizeof(wchar_t)); - index->mailbox_guid[MAILBOX_GUID_HEX_LENGTH] = '\0'; -} - -void lucene_index_unselect_mailbox(struct lucene_index *index) -{ - memset(index->mailbox_guid, 0, sizeof(index->mailbox_guid)); -} - -static void lucene_handle_error(struct lucene_index *index, CLuceneError &err, - const char *msg) -{ - const char *error, *what = err.what(); - - i_error("lucene index %s: %s failed (#%d): %s", - index->path, msg, err.number(), what); - - if (index->list != NULL && - (err.number() == CL_ERR_CorruptIndex || - err.number() == CL_ERR_IO)) { - /* delete corrupted index. most IO errors are also about - missing files and other such corruption.. */ - if (unlink_directory(index->path, (enum unlink_directory_flags)0, &error) < 0) - i_error("unlink_directory(%s) failed: %s", index->path, error); - rescan_clear_unseen_mailboxes(index, NULL); - } -} - -static int lucene_index_open(struct lucene_index *index) -{ - if (index->reader != NULL) { - i_assert(index->to_close != NULL); - timeout_reset(index->to_close); - return 1; - } - - if (!IndexReader::indexExists(index->path)) - return 0; - - try { - index->reader = IndexReader::open(index->path); - } catch (CLuceneError &err) { - lucene_handle_error(index, err, "IndexReader::open()"); - return -1; - } - i_assert(index->to_close == NULL); - index->to_close = timeout_add(LUCENE_INDEX_CLOSE_TIMEOUT_MSECS, - lucene_index_close, index); - return 1; -} - -static int lucene_index_open_search(struct lucene_index *index) -{ - int ret; - - if (index->searcher != NULL) - return 1; - - if ((ret = lucene_index_open(index)) <= 0) - return ret; - - index->searcher = _CLNEW IndexSearcher(index->reader); - return 1; -} - -static int -lucene_doc_get_uid(struct lucene_index *index, Document *doc, uint32_t *uid_r) -{ - Field *field = doc->getField(_T("uid")); - const TCHAR *uid = field == NULL ? NULL : field->stringValue(); - if (uid == NULL) { - i_error("lucene: Corrupted FTS index %s: No UID for document", - index->path); - return -1; - } - - uint32_t num = 0; - while (*uid != 0) { - num = num*10 + (*uid - '0'); - uid++; - } - *uid_r = num; - return 0; -} - -static uint32_t -lucene_doc_get_part(struct lucene_index *index, Document *doc) -{ - Field *field = doc->getField(_T("part")); - const TCHAR *part = field == NULL ? NULL : field->stringValue(); - if (part == NULL) - return 0; - - uint32_t num = 0; - while (*part != 0) { - num = num*10 + (*part - '0'); - part++; - } - return num; -} - -int lucene_index_get_last_uid(struct lucene_index *index, uint32_t *last_uid_r) -{ - int ret = 0; - - *last_uid_r = 0; - - if ((ret = lucene_index_open_search(index)) <= 0) - return ret; - - Term mailbox_term(_T("box"), index->mailbox_guid); - TermQuery query(&mailbox_term); - - uint32_t last_uid = 0; - try { - Hits *hits = index->searcher->search(&query); - - for (size_t i = 0; i < hits->length(); i++) { - uint32_t uid; - - if (lucene_doc_get_uid(index, &hits->doc(i), - &uid) < 0) { - ret = -1; - break; - } - - if (uid > last_uid) - last_uid = uid; - } - _CLDELETE(hits); - } catch (CLuceneError &err) { - lucene_handle_error(index, err, "last_uid search"); - ret = -1; - } - *last_uid_r = last_uid; - return ret; -} - -int lucene_index_get_doc_count(struct lucene_index *index, uint32_t *count_r) -{ - int ret; - - if (index->reader == NULL) { - lucene_index_close(index); - if ((ret = lucene_index_open(index)) < 0) - return -1; - if (ret == 0) { - *count_r = 0; - return 0; - } - } - *count_r = index->reader->numDocs(); - return 0; -} - -static int lucene_settings_check(struct lucene_index *index) -{ - uint32_t set_checksum; - const char *error; - int ret = 0; - - set_checksum = fts_lucene_settings_checksum(&index->set); - ret = fts_index_have_compatible_settings(index->list, set_checksum); - if (ret != 0) - return ret; - - i_warning("fts-lucene: Settings have changed, rebuilding index for mailbox"); - - /* settings changed, rebuild index */ - if (unlink_directory(index->path, (enum unlink_directory_flags)0, &error) < 0) { - i_error("unlink_directory(%s) failed: %s", index->path, error); - ret = -1; - } else { - rescan_clear_unseen_mailboxes(index, NULL); - } - return ret; -} - -int lucene_index_build_init(struct lucene_index *index) -{ - const char *lock_path; - struct stat st; - - lucene_index_close(index); - - lock_path = t_strdup_printf("%s/write.lock", index->path); - if (stat(lock_path, &st) == 0 && - st.st_mtime < time(NULL) - LUCENE_LOCK_OVERRIDE_SECS) { - if (unlink(lock_path) < 0) - i_error("unlink(%s) failed: %m", lock_path); - } - - if (lucene_settings_check(index) < 0) - return -1; - - bool exists = IndexReader::indexExists(index->path); - try { - index->writer = _CLNEW IndexWriter(index->path, - index->default_analyzer, - !exists); - } catch (CLuceneError &err) { - lucene_handle_error(index, err, "IndexWriter()"); - return -1; - } - index->writer->setMaxFieldLength(MAX_TERMS_PER_DOCUMENT); - return 0; -} - -#ifdef HAVE_FTS_TEXTCAT -static Analyzer *get_analyzer(struct lucene_index *index, const char *lang) -{ - normalizer_func_t *normalizer = index->normalizer; - const struct lucene_analyzer *a; - struct lucene_analyzer new_analyzer; - Analyzer *analyzer; - - array_foreach(&index->analyzers, a) { - if (strcmp(a->lang, lang) == 0) - return a->analyzer; - } - - memset(&new_analyzer, 0, sizeof(new_analyzer)); - new_analyzer.lang = i_strdup(lang); - new_analyzer.analyzer = - _CLNEW snowball::SnowballAnalyzer(normalizer, lang); - array_append_i(&index->analyzers.arr, &new_analyzer, 1); - return new_analyzer.analyzer; -} - -static void *textcat_init(struct lucene_index *index) -{ - const char *textcat_dir = index->set.textcat_dir; - unsigned int len; - - if (textcat_dir == NULL) - return NULL; - - /* textcat really wants the '/' suffix */ - len = strlen(textcat_dir); - if (len > 0 && textcat_dir[len-1] != '/') - textcat_dir = t_strconcat(textcat_dir, "/", NULL); - - return special_textcat_Init(index->set.textcat_conf, textcat_dir); -} - -static Analyzer * -guess_analyzer(struct lucene_index *index, const void *data, size_t size) -{ - const char *lang; - - if (textcat_broken) - return NULL; - - if (textcat == NULL) { - textcat = textcat_init(index); - if (textcat == NULL) { - textcat_broken = TRUE; - return NULL; - } - } - - /* try to guess the language */ - lang = textcat_Classify(textcat, (const char *)data, - I_MIN(size, 500)); - const char *p = strchr(lang, ']'); - if (lang[0] != '[' || p == NULL) - return NULL; - lang = t_strdup_until(lang+1, p); - if (strcmp(lang, index->set.default_language) == 0) - return index->default_analyzer; - - return get_analyzer(index, lang); -} -#else -static Analyzer * -guess_analyzer(struct lucene_index *index ATTR_UNUSED, - const void *data ATTR_UNUSED, size_t size ATTR_UNUSED) -{ - return NULL; -} -#endif - -static int lucene_index_build_flush(struct lucene_index *index) -{ - int ret = 0; - - if (index->doc == NULL) - return 0; - - try { - CL_NS(analysis)::Analyzer *analyzer = NULL; - - if (!index->set.use_libfts) { - analyzer = index->cur_analyzer != NULL ? - index->cur_analyzer : index->default_analyzer; - } - index->writer->addDocument(index->doc, analyzer); - } catch (CLuceneError &err) { - lucene_handle_error(index, err, "IndexWriter::addDocument()"); - ret = -1; - } - - _CLDELETE(index->doc); - index->doc = NULL; - index->cur_analyzer = NULL; - return ret; -} - -int lucene_index_build_more(struct lucene_index *index, uint32_t uid, - uint32_t part_idx, const unsigned char *data, - size_t size, const char *hdr_name) -{ - wchar_t id[MAX_INT_STRLEN]; - size_t namesize, datasize; - - if (uid != index->prev_uid || part_idx != index->prev_part_idx) { - if (lucene_index_build_flush(index) < 0) - return -1; - index->prev_uid = uid; - index->prev_part_idx = part_idx; - - index->doc = _CLNEW Document(); - swprintf(id, N_ELEMENTS(id), L"%u", uid); - index->doc->add(*_CLNEW Field(_T("uid"), id, Field::STORE_YES | Field::INDEX_UNTOKENIZED)); - if (part_idx != 0) { - swprintf(id, N_ELEMENTS(id), L"%u", part_idx); - index->doc->add(*_CLNEW Field(_T("part"), id, Field::STORE_YES | Field::INDEX_UNTOKENIZED)); - } - index->doc->add(*_CLNEW Field(_T("box"), index->mailbox_guid, Field::STORE_YES | Field::INDEX_UNTOKENIZED)); - } - - if (index->normalizer_buf != NULL && !index->set.use_libfts) { - buffer_set_used_size(index->normalizer_buf, 0); - index->normalizer(data, size, index->normalizer_buf); - data = (const unsigned char *)index->normalizer_buf->data; - size = index->normalizer_buf->used; - } - - datasize = uni_utf8_strlen_n(data, size) + 1; - wchar_t *dest, *dest_free = NULL; - if (datasize < 4096) - dest = t_new(wchar_t, datasize); - else - dest = dest_free = i_new(wchar_t, datasize); - lucene_utf8_n_to_tchar(data, size, dest, datasize); - lucene_data_translate(index, dest, datasize-1); - - int token_flag = index->set.use_libfts ? - Field::INDEX_UNTOKENIZED : Field::INDEX_TOKENIZED; - if (hdr_name != NULL) { - /* hdr_name should be ASCII, but don't break in case it isn't */ - hdr_name = t_str_lcase(hdr_name); - namesize = uni_utf8_strlen(hdr_name) + 1; - wchar_t wname[namesize]; - lucene_utf8_n_to_tchar((const unsigned char *)hdr_name, - strlen(hdr_name), wname, namesize); - if (!index->set.use_libfts) - index->doc->add(*_CLNEW Field(_T("hdr"), wname, Field::STORE_NO | token_flag)); - index->doc->add(*_CLNEW Field(_T("hdr"), dest, Field::STORE_NO | token_flag)); - - if (fts_header_want_indexed(hdr_name)) - index->doc->add(*_CLNEW Field(wname, dest, Field::STORE_NO | token_flag)); - } else if (size > 0) { - if (index->cur_analyzer == NULL && !index->set.use_libfts) - index->cur_analyzer = guess_analyzer(index, data, size); - index->doc->add(*_CLNEW Field(_T("body"), dest, Field::STORE_NO | token_flag)); - } - i_free(dest_free); - return 0; -} - -int lucene_index_build_deinit(struct lucene_index *index) -{ - int ret = 0; - - if (index->prev_uid == 0) { - /* no changes. */ - return 0; - } - index->prev_uid = 0; - index->prev_part_idx = 0; - - if (index->writer == NULL) { - lucene_index_close(index); - return -1; - } - - if (lucene_index_build_flush(index) < 0) - ret = -1; - - try { - index->writer->close(); - } catch (CLuceneError &err) { - lucene_handle_error(index, err, "IndexWriter::close()"); - ret = -1; - } - - lucene_index_close(index); - return ret; -} - -static int -wcharguid_to_guid(guid_128_t dest, const wchar_t *src) -{ - buffer_t buf = { { 0, 0 } }; - char src_chars[GUID_128_SIZE*2 + 1]; - unsigned int i; - - for (i = 0; i < sizeof(src_chars)-1; i++) { - if ((src[i] >= '0' && src[i] <= '9') || - (src[i] >= 'a' && src[i] <= 'f')) - src_chars[i] = src[i]; - else - return -1; - } - if (src[i] != '\0') - return -1; - src_chars[i] = '\0'; - - buffer_create_from_data(&buf, dest, GUID_128_SIZE); - return hex_to_binary(src_chars, &buf); -} - -static int -rescan_get_uids(struct mailbox *box, ARRAY_TYPE(seq_range) *uids) -{ - struct mailbox_status status; - - if (mailbox_get_status(box, STATUS_MESSAGES, &status) < 0) - return -1; - - if (status.messages > 0) T_BEGIN { - ARRAY_TYPE(seq_range) seqs; - - t_array_init(&seqs, 2); - seq_range_array_add_range(&seqs, 1, status.messages); - mailbox_get_uid_range(box, &seqs, uids); - } T_END; - return 0; -} - -static int rescan_finish(struct rescan_context *ctx) -{ - int ret; - - ret = fts_index_set_last_uid(ctx->box, ctx->last_existing_uid); - mailbox_free(&ctx->box); - return ret; -} - -static int -fts_lucene_get_mailbox_guid(struct lucene_index *index, Document *doc, - guid_128_t guid_r) -{ - Field *field = doc->getField(_T("box")); - const TCHAR *box_guid = field == NULL ? NULL : field->stringValue(); - if (box_guid == NULL) { - i_error("lucene: Corrupted FTS index %s: No mailbox for document", - index->path); - return -1; - } - - if (wcharguid_to_guid(guid_r, box_guid) < 0) { - i_error("lucene: Corrupted FTS index %s: " - "box field not in expected format", index->path); - return -1; - } - return 0; -} - -static int -rescan_open_mailbox(struct rescan_context *ctx, Document *doc) -{ - guid_128_t guid, *guidp; - int ret; - - if (fts_lucene_get_mailbox_guid(ctx->index, doc, guid) < 0) - return 0; - - if (memcmp(guid, ctx->box_guid, sizeof(guid)) == 0) { - /* same as last one */ - return ctx->box_ret; - } - memcpy(ctx->box_guid, guid, sizeof(ctx->box_guid)); - - guidp = p_new(ctx->pool, guid_128_t, 1); - memcpy(guidp, guid, sizeof(*guidp)); - hash_table_insert(ctx->seen_mailbox_guids, guidp, guidp); - - if (ctx->box != NULL) - rescan_finish(ctx); - ctx->box = mailbox_alloc_guid(ctx->index->list, guid, - (enum mailbox_flags)0); - if (mailbox_open(ctx->box) < 0) { - enum mail_error error; - const char *errstr; - - errstr = mailbox_get_last_internal_error(ctx->box, &error); - if (error == MAIL_ERROR_NOTFOUND) - ret = 0; - else { - i_error("lucene: Couldn't open mailbox %s: %s", - mailbox_get_vname(ctx->box), errstr); - ret = -1; - } - mailbox_free(&ctx->box); - ctx->box_ret = ret; - return ret; - } - if (mailbox_sync(ctx->box, (enum mailbox_sync_flags)0) < 0) { - i_error("lucene: Failed to sync mailbox %s: %s", - mailbox_get_vname(ctx->box), - mailbox_get_last_internal_error(ctx->box, NULL)); - mailbox_free(&ctx->box); - ctx->box_ret = -1; - return -1; - } - - array_clear(&ctx->uids); - rescan_get_uids(ctx->box, &ctx->uids); - - ctx->warned = FALSE; - ctx->last_existing_uid = 0; - ctx->uids_iter_n = 0; - seq_range_array_iter_init(&ctx->uids_iter, &ctx->uids); - - ctx->box_ret = 1; - return 1; -} - -static int -rescan_next(struct rescan_context *ctx, Document *doc) -{ - uint32_t lucene_uid, idx_uid; - - if (lucene_doc_get_uid(ctx->index, doc, &lucene_uid) < 0) - return 0; - - if (seq_range_array_iter_nth(&ctx->uids_iter, ctx->uids_iter_n, - &idx_uid)) { - if (idx_uid == lucene_uid) { - ctx->uids_iter_n++; - ctx->last_existing_uid = idx_uid; - return 1; - } - if (idx_uid < lucene_uid) { - /* lucene is missing an UID from the middle. delete - the rest of the messages from this mailbox and - reindex. */ - if (!ctx->warned) { - i_warning("lucene: Mailbox %s " - "missing UIDs in the middle", - mailbox_get_vname(ctx->box)); - ctx->warned = TRUE; - } - } else { - /* UID has been expunged from index. delete from - lucene as well. */ - } - return 0; - } else { - /* the rest of the messages have been expunged from index */ - return 0; - } -} - -static void -rescan_clear_unseen_mailbox(struct lucene_index *index, - struct rescan_context *rescan_ctx, - const char *vname, - const struct fts_index_header *hdr) -{ - struct mailbox *box; - struct mailbox_metadata metadata; - - box = mailbox_alloc(index->list, vname, - (enum mailbox_flags)0); - if (mailbox_open(box) == 0 && - mailbox_get_metadata(box, MAILBOX_METADATA_GUID, - &metadata) == 0 && - (rescan_ctx == NULL || - hash_table_lookup(rescan_ctx->seen_mailbox_guids, - metadata.guid) == NULL)) { - /* this mailbox had no records in lucene index. - make sure its last indexed uid is 0 */ - (void)fts_index_set_header(box, hdr); - } - mailbox_free(&box); -} - -static void rescan_clear_unseen_mailboxes(struct lucene_index *index, - struct rescan_context *rescan_ctx) -{ - const enum mailbox_list_iter_flags iter_flags = - (enum mailbox_list_iter_flags) - (MAILBOX_LIST_ITER_NO_AUTO_BOXES | - MAILBOX_LIST_ITER_RETURN_NO_FLAGS); - struct mailbox_list_iterate_context *iter; - const struct mailbox_info *info; - struct fts_index_header hdr; - struct mail_namespace *ns = index->list->ns; - const char *vname; - - memset(&hdr, 0, sizeof(hdr)); - hdr.settings_checksum = fts_lucene_settings_checksum(&index->set); - - iter = mailbox_list_iter_init(index->list, "*", iter_flags); - while ((info = mailbox_list_iter_next(iter)) != NULL) - rescan_clear_unseen_mailbox(index, rescan_ctx, info->vname, &hdr); - (void)mailbox_list_iter_deinit(&iter); - - if (ns->prefix_len > 0 && - ns->prefix[ns->prefix_len-1] == mail_namespace_get_sep(ns)) { - /* namespace prefix itself isn't returned by the listing */ - vname = t_strndup(index->list->ns->prefix, - index->list->ns->prefix_len-1); - rescan_clear_unseen_mailbox(index, rescan_ctx, vname, &hdr); - } -} - -int lucene_index_rescan(struct lucene_index *index) -{ - static const TCHAR *sort_fields[] = { _T("box"), _T("uid"), NULL }; - struct rescan_context ctx; - bool failed = false; - int ret; - - i_assert(index->list != NULL); - - if ((ret = lucene_index_open_search(index)) < 0) - return ret; - - Term term(_T("box"), _T("*")); - WildcardQuery query(&term); - Sort sort(sort_fields); - - memset(&ctx, 0, sizeof(ctx)); - ctx.index = index; - ctx.pool = pool_alloconly_create("guids", 1024); - hash_table_create(&ctx.seen_mailbox_guids, ctx.pool, 0, - guid_128_hash, guid_128_cmp); - i_array_init(&ctx.uids, 128); - - if (ret > 0) try { - Hits *hits = index->searcher->search(&query, &sort); - - for (size_t i = 0; i < hits->length(); i++) { - ret = rescan_open_mailbox(&ctx, &hits->doc(i)); - if (ret > 0) - ret = rescan_next(&ctx, &hits->doc(i)); - if (ret < 0) - failed = true; - else if (ret == 0) - index->reader->deleteDocument(hits->id(i)); - } - _CLDELETE(hits); - } catch (CLuceneError &err) { - lucene_handle_error(index, err, "rescan search"); - failed = true; - } - lucene_index_close(index); - if (ctx.box != NULL) - rescan_finish(&ctx); - array_free(&ctx.uids); - - rescan_clear_unseen_mailboxes(index, &ctx); - hash_table_destroy(&ctx.seen_mailbox_guids); - pool_unref(&ctx.pool); - return failed ? -1 : 0; -} - -static void guid128_to_wguid(const guid_128_t guid, - wchar_t wguid_hex[MAILBOX_GUID_HEX_LENGTH + 1]) -{ - buffer_t buf = { { 0, 0 } }; - unsigned char guid_hex[MAILBOX_GUID_HEX_LENGTH]; - unsigned int i; - - buffer_create_from_data(&buf, guid_hex, MAILBOX_GUID_HEX_LENGTH); - binary_to_hex_append(&buf, guid, GUID_128_SIZE); - for (i = 0; i < MAILBOX_GUID_HEX_LENGTH; i++) - wguid_hex[i] = guid_hex[i]; - wguid_hex[i] = '\0'; -} - -static bool -lucene_index_add_uid_filter(BooleanQuery *query, - const struct fts_expunge_log_read_record *rec) -{ - struct seq_range_iter iter; - wchar_t wuid[MAX_INT_STRLEN]; - unsigned int n; - uint32_t uid; - - /* RangeQuery and WildcardQuery work by enumerating through all terms - that match them, and then adding TermQueries for them. So we can - simply do the same directly, and if it looks like there are too - many terms just go through everything. */ - - if (seq_range_count(&rec->uids) > FTS_LUCENE_MAX_SEARCH_TERMS) - return false; - - seq_range_array_iter_init(&iter, &rec->uids); n = 0; - while (seq_range_array_iter_nth(&iter, n++, &uid)) { - swprintf(wuid, N_ELEMENTS(wuid), L"%u", uid); - - Term *term = _CLNEW Term(_T("uid"), wuid); - query->add(_CLNEW TermQuery(term), true, BooleanClause::SHOULD); - _CLDECDELETE(term); - } - return true; -} - -static int -lucene_index_expunge_record(struct lucene_index *index, - const struct fts_expunge_log_read_record *rec) -{ - int ret; - - if ((ret = lucene_index_open_search(index)) <= 0) - return ret; - - BooleanQuery query; - BooleanQuery uids_query; - - if (lucene_index_add_uid_filter(&uids_query, rec)) - query.add(&uids_query, BooleanClause::MUST); - - wchar_t wguid[MAILBOX_GUID_HEX_LENGTH + 1]; - guid128_to_wguid(rec->mailbox_guid, wguid); - Term term(_T("box"), wguid); - TermQuery mailbox_query(&term); - query.add(&mailbox_query, BooleanClause::MUST); - - try { - Hits *hits = index->searcher->search(&query); - - for (size_t i = 0; i < hits->length(); i++) { - uint32_t uid; - - if (lucene_doc_get_uid(index, &hits->doc(i), - &uid) < 0 || - seq_range_exists(&rec->uids, uid)) - index->reader->deleteDocument(hits->id(i)); - } - _CLDELETE(hits); - } catch (CLuceneError &err) { - lucene_handle_error(index, err, "expunge search"); - ret = -1; - } - return ret < 0 ? -1 : 0; -} - -int lucene_index_expunge_from_log(struct lucene_index *index, - struct fts_expunge_log *log) -{ - struct fts_expunge_log_read_ctx *ctx; - const struct fts_expunge_log_read_record *rec; - int ret = 0, ret2; - - ctx = fts_expunge_log_read_begin(log); - while ((rec = fts_expunge_log_read_next(ctx)) != NULL) { - if (lucene_index_expunge_record(index, rec) < 0) { - ret = -1; - break; - } - } - - lucene_index_close(index); - - ret2 = fts_expunge_log_read_end(&ctx); - if (ret < 0 || ret2 < 0) - return -1; - return ret2; -} - -int lucene_index_optimize(struct lucene_index *index) -{ - int ret = 0; - - if (!IndexReader::indexExists(index->path)) - return 0; - if (IndexReader::isLocked(index->path)) - IndexReader::unlock(index->path); - - IndexWriter *writer = NULL; - try { - writer = _CLNEW IndexWriter(index->path, index->default_analyzer, false); - writer->optimize(); - } catch (CLuceneError &err) { - lucene_handle_error(index, err, "IndexWriter::optimize()"); - ret = -1; - } - try { - writer->close(); - } catch (CLuceneError &err) { - lucene_handle_error(index, err, "IndexWriter::close()"); - ret = -1; - } - if (writer != NULL) - _CLDELETE(writer); - return ret; -} - -// Mostly copy&pasted from CLucene's QueryParser -static Query* getFieldQuery(Analyzer *analyzer, const TCHAR* _field, const TCHAR* queryText, bool fuzzy) { - // Use the analyzer to get all the tokens, and then build a TermQuery, - // PhraseQuery, or nothing based on the term count - - StringReader reader(queryText); - TokenStream* source = analyzer->tokenStream(_field, &reader); - - CLVector > v; - CL_NS(analysis)::Token* t = NULL; - int32_t positionCount = 0; - bool severalTokensAtSamePosition = false; - - while (true) { - t = _CLNEW Token(); - try { - Token* _t = source->next(t); - if (_t == NULL) _CLDELETE(t); - }_CLCATCH_ERR(CL_ERR_IO, _CLLDELETE(source);_CLLDELETE(t);,{ - t = NULL; - }); - if (t == NULL) - break; - v.push_back(t); - if (t->getPositionIncrement() != 0) - positionCount += t->getPositionIncrement(); - else - severalTokensAtSamePosition = true; - } - try { - source->close(); - } - _CLCATCH_ERR_CLEANUP(CL_ERR_IO, {_CLLDELETE(source);_CLLDELETE(t);} ); /* cleanup */ - _CLLDELETE(source); - - if (v.size() == 0) - return NULL; - else if (v.size() == 1) { - Term* tm = _CLNEW Term(_field, v.at(0)->termBuffer()); - Query* ret; - if (fuzzy) - ret = _CLNEW FuzzyQuery( tm ); - else - ret = _CLNEW TermQuery( tm ); - _CLDECDELETE(tm); - return ret; - } else { - if (severalTokensAtSamePosition) { - if (positionCount == 1) { - // no phrase query: - BooleanQuery* q = _CLNEW BooleanQuery(true); - for(size_t i=0; itermBuffer()); - q->add(_CLNEW TermQuery(tm), true, BooleanClause::SHOULD); - _CLDECDELETE(tm); - } - return q; - }else { - MultiPhraseQuery* mpq = _CLNEW MultiPhraseQuery(); - CLArrayList multiTerms; - int32_t position = -1; - for (size_t i = 0; i < v.size(); i++) { - t = v.at(i); - if (t->getPositionIncrement() > 0 && multiTerms.size() > 0) { - ValueArray termsArray(multiTerms.size()); - multiTerms.toArray(termsArray.values); - mpq->add(&termsArray,position); - multiTerms.clear(); - } - position += t->getPositionIncrement(); - multiTerms.push_back(_CLNEW Term(_field, t->termBuffer())); - } - ValueArray termsArray(multiTerms.size()); - multiTerms.toArray(termsArray.values); - mpq->add(&termsArray,position); - return mpq; - } - }else { - PhraseQuery* pq = _CLNEW PhraseQuery(); - int32_t position = -1; - - for (size_t i = 0; i < v.size(); i++) { - t = v.at(i); - Term* tm = _CLNEW Term(_field, t->termBuffer()); - position += t->getPositionIncrement(); - pq->add(tm,position); - _CLDECDELETE(tm); - } - return pq; - } - } -} - -static Query * -lucene_get_query_str(struct lucene_index *index, - const TCHAR *key, const char *str, bool fuzzy) -{ - const TCHAR *wvalue; - Analyzer *analyzer; - - if (index->set.use_libfts) { - const wchar_t *wstr = t_lucene_utf8_to_tchar(index, str); - Term* tm = _CLNEW Term(key, wstr); - Query* ret; - if (fuzzy) - ret = _CLNEW FuzzyQuery( tm ); - else - ret = _CLNEW TermQuery( tm ); - _CLDECDELETE(tm); - return ret; - } - - if (index->normalizer_buf != NULL) { - buffer_set_used_size(index->normalizer_buf, 0); - index->normalizer(str, strlen(str), index->normalizer_buf); - buffer_append_c(index->normalizer_buf, '\0'); - str = (const char *)index->normalizer_buf->data; - } - - wvalue = t_lucene_utf8_to_tchar(index, str); - analyzer = guess_analyzer(index, str, strlen(str)); - if (analyzer == NULL) { - analyzer = index->default_analyzer; - i_assert(analyzer != NULL); - } - - return getFieldQuery(analyzer, key, wvalue, fuzzy); -} - -static Query * -lucene_get_query(struct lucene_index *index, - const TCHAR *key, const struct mail_search_arg *arg) -{ - return lucene_get_query_str(index, key, arg->value.str, arg->fuzzy); -} - -static bool -lucene_add_definite_query(struct lucene_index *index, - ARRAY_TYPE(lucene_query) &queries, - struct mail_search_arg *arg, - enum fts_lookup_flags flags) -{ - bool and_args = (flags & FTS_LOOKUP_FLAG_AND_ARGS) != 0; - Query *q; - - if (arg->no_fts) - return false; - - if (arg->match_not && !and_args) { - /* FIXME: we could handle this by doing multiple queries.. */ - return false; - } - - switch (arg->type) { - case SEARCH_TEXT: { - Query *q1 = lucene_get_query(index, _T("hdr"), arg); - Query *q2 = lucene_get_query(index, _T("body"), arg); - - if (q1 == NULL && q2 == NULL) - q = NULL; - else { - BooleanQuery *bq = _CLNEW BooleanQuery(); - if (q1 != NULL) - bq->add(q1, true, BooleanClause::SHOULD); - if (q2 != NULL) - bq->add(q2, true, BooleanClause::SHOULD); - q = bq; - } - break; - } - case SEARCH_BODY: - q = lucene_get_query(index, _T("body"), arg); - break; - case SEARCH_HEADER: - case SEARCH_HEADER_ADDRESS: - case SEARCH_HEADER_COMPRESS_LWSP: - if (!fts_header_want_indexed(arg->hdr_field_name) || - *arg->value.str == '\0') - return false; - - q = lucene_get_query(index, - t_lucene_utf8_to_tchar(index, t_str_lcase(arg->hdr_field_name)), - arg); - break; - default: - return false; - } - - if (q == NULL) { - /* couldn't handle this search after all (e.g. trying to search - a stop word) */ - return false; - } - - struct lucene_query *lq = array_append_space(&queries); - lq->query = q; - if (!and_args) - lq->occur = BooleanClause::SHOULD; - else if (!arg->match_not) - lq->occur = BooleanClause::MUST; - else - lq->occur = BooleanClause::MUST_NOT; - return true; -} - -static bool -lucene_add_maybe_query(struct lucene_index *index, - ARRAY_TYPE(lucene_query) &queries, - struct mail_search_arg *arg, - enum fts_lookup_flags flags) -{ - bool and_args = (flags & FTS_LOOKUP_FLAG_AND_ARGS) != 0; - Query *q = NULL; - - if (arg->no_fts) - return false; - - if (arg->match_not) { - /* FIXME: we could handle this by doing multiple queries.. */ - return false; - } - - switch (arg->type) { - case SEARCH_HEADER: - case SEARCH_HEADER_ADDRESS: - case SEARCH_HEADER_COMPRESS_LWSP: - if (*arg->value.str == '\0' && !index->set.use_libfts) { - /* checking potential existence of the header name */ - q = lucene_get_query_str(index, _T("hdr"), - t_str_lcase(arg->hdr_field_name), FALSE); - break; - } - - if (fts_header_want_indexed(arg->hdr_field_name)) - return false; - - /* we can check if the search key exists in some header and - filter out the messages that have no chance of matching */ - q = lucene_get_query(index, _T("hdr"), arg); - break; - default: - return false; - } - - if (q == NULL) { - /* couldn't handle this search after all (e.g. trying to search - a stop word) */ - return false; - } - struct lucene_query *lq = array_append_space(&queries); - lq->query = q; - if (!and_args) - lq->occur = BooleanClause::SHOULD; - else if (!arg->match_not) - lq->occur = BooleanClause::MUST; - else - lq->occur = BooleanClause::MUST_NOT; - return true; -} - -static bool queries_have_non_must_nots(ARRAY_TYPE(lucene_query) &queries) -{ - const struct lucene_query *lq; - - array_foreach(&queries, lq) { - if (lq->occur != BooleanClause::MUST_NOT) - return TRUE; - } - return FALSE; -} - -static void search_query_add(BooleanQuery &query, - ARRAY_TYPE(lucene_query) &queries) -{ - BooleanQuery *search_query = _CLNEW BooleanQuery(); - const struct lucene_query *lq; - - if (queries_have_non_must_nots(queries)) { - array_foreach(&queries, lq) - search_query->add(lq->query, true, lq->occur); - query.add(search_query, true, BooleanClause::MUST); - } else { - array_foreach(&queries, lq) - search_query->add(lq->query, true, BooleanClause::SHOULD); - query.add(search_query, true, BooleanClause::MUST_NOT); - } -} - -static int -lucene_index_search(struct lucene_index *index, - ARRAY_TYPE(lucene_query) &queries, - struct fts_result *result, ARRAY_TYPE(seq_range) *uids_r) -{ - struct fts_score_map *score; - int ret = 0; - - BooleanQuery query; - search_query_add(query, queries); - - Term mailbox_term(_T("box"), index->mailbox_guid); - TermQuery mailbox_query(&mailbox_term); - query.add(&mailbox_query, BooleanClause::MUST); - - try { - Hits *hits = index->searcher->search(&query); - - uint32_t last_uid = 0; - if (result != NULL) - result->scores_sorted = true; - - for (size_t i = 0; i < hits->length(); i++) { - uint32_t uid; - - if (lucene_doc_get_uid(index, &hits->doc(i), - &uid) < 0) { - ret = -1; - break; - } - - if (seq_range_array_add(uids_r, uid)) { - /* duplicate result */ - } else if (result != NULL) { - if (uid < last_uid) - result->scores_sorted = false; - last_uid = uid; - - score = array_append_space(&result->scores); - score->uid = uid; - score->score = hits->score(i); - } - } - _CLDELETE(hits); - return ret; - } catch (CLuceneError &err) { - lucene_handle_error(index, err, "search"); - return -1; - } -} - -int lucene_index_lookup(struct lucene_index *index, - struct mail_search_arg *args, - enum fts_lookup_flags flags, - struct fts_result *result) -{ - struct mail_search_arg *arg; - - if (lucene_index_open_search(index) <= 0) - return -1; - - ARRAY_TYPE(lucene_query) def_queries; - t_array_init(&def_queries, 16); - bool have_definites = false; - - for (arg = args; arg != NULL; arg = arg->next) { - if (lucene_add_definite_query(index, def_queries, arg, flags)) { - arg->match_always = true; - have_definites = true; - } - } - - if (have_definites) { - ARRAY_TYPE(seq_range) *uids_arr = - (flags & FTS_LOOKUP_FLAG_NO_AUTO_FUZZY) == 0 ? - &result->definite_uids : &result->maybe_uids; - if (lucene_index_search(index, def_queries, result, - uids_arr) < 0) - return -1; - } - - if (have_definites) { - /* FIXME: mixing up definite + maybe queries is broken. if the - definite query matched, it'll just assume that the maybe - queries matched as well */ - return 0; - } - - ARRAY_TYPE(lucene_query) maybe_queries; - t_array_init(&maybe_queries, 16); - bool have_maybies = false; - - for (arg = args; arg != NULL; arg = arg->next) { - if (lucene_add_maybe_query(index, maybe_queries, arg, flags)) { - arg->match_always = true; - have_maybies = true; - } - } - - if (have_maybies) { - if (lucene_index_search(index, maybe_queries, NULL, - &result->maybe_uids) < 0) - return -1; - } - return 0; -} - -static int -lucene_index_search_multi(struct lucene_index *index, - HASH_TABLE_TYPE(wguid_result) guids, - ARRAY_TYPE(lucene_query) &queries, - enum fts_lookup_flags flags, - struct fts_multi_result *result) -{ - struct fts_score_map *score; - int ret = 0; - - BooleanQuery query; - search_query_add(query, queries); - - BooleanQuery mailbox_query; - struct hash_iterate_context *iter; - void *key, *value; - iter = hash_table_iterate_init(guids); - while (hash_table_iterate(iter, guids, &key, &value)) { - Term *term = _CLNEW Term(_T("box"), (wchar_t *)key); - TermQuery *q = _CLNEW TermQuery(term); - mailbox_query.add(q, true, BooleanClause::SHOULD); - } - hash_table_iterate_deinit(&iter); - - query.add(&mailbox_query, BooleanClause::MUST); - try { - Hits *hits = index->searcher->search(&query); - - for (size_t i = 0; i < hits->length(); i++) { - uint32_t uid; - - Field *field = hits->doc(i).getField(_T("box")); - const TCHAR *box_guid = field == NULL ? NULL : field->stringValue(); - if (box_guid == NULL) { - i_error("lucene: Corrupted FTS index %s: No mailbox for document", - index->path); - ret = -1; - break; - } - struct fts_result *br = - hash_table_lookup(guids, box_guid); - if (br == NULL) { - i_warning("lucene: Returned unexpected mailbox with GUID %ls", box_guid); - continue; - } - - if (lucene_doc_get_uid(index, &hits->doc(i), - &uid) < 0) { - ret = -1; - break; - } - - ARRAY_TYPE(seq_range) *uids_arr = - (flags & FTS_LOOKUP_FLAG_NO_AUTO_FUZZY) == 0 ? - &br->maybe_uids : &br->definite_uids; - if (!array_is_created(uids_arr)) { - p_array_init(uids_arr, result->pool, 32); - p_array_init(&br->scores, result->pool, 32); - } - if (seq_range_array_add(uids_arr, uid)) { - /* duplicate result */ - } else { - score = array_append_space(&br->scores); - score->uid = uid; - score->score = hits->score(i); - } - } - _CLDELETE(hits); - return ret; - } catch (CLuceneError &err) { - lucene_handle_error(index, err, "multi search"); - return -1; - } -} - -int lucene_index_lookup_multi(struct lucene_index *index, - HASH_TABLE_TYPE(wguid_result) guids, - struct mail_search_arg *args, - enum fts_lookup_flags flags, - struct fts_multi_result *result) -{ - struct mail_search_arg *arg; - - if (lucene_index_open_search(index) <= 0) - return -1; - - ARRAY_TYPE(lucene_query) def_queries; - t_array_init(&def_queries, 16); - bool have_definites = false; - - for (arg = args; arg != NULL; arg = arg->next) { - if (lucene_add_definite_query(index, def_queries, arg, flags)) { - arg->match_always = true; - have_definites = true; - } - } - - if (have_definites) { - if (lucene_index_search_multi(index, guids, def_queries, flags, - result) < 0) - return -1; - } - return 0; -} - -struct lucene_index_iter { - struct lucene_index *index; - struct lucene_index_record rec; - - Term *term; - WildcardQuery *query; - Sort *sort; - - Hits *hits; - size_t i; - bool failed; -}; - -struct lucene_index_iter * -lucene_index_iter_init(struct lucene_index *index) -{ - static const TCHAR *sort_fields[] = { _T("box"), _T("uid"), NULL }; - struct lucene_index_iter *iter; - int ret; - - iter = i_new(struct lucene_index_iter, 1); - iter->index = index; - if ((ret = lucene_index_open_search(index)) <= 0) { - if (ret < 0) - iter->failed = true; - return iter; - } - - iter->term = _CLNEW Term(_T("box"), _T("*")); - iter->query = _CLNEW WildcardQuery(iter->term); - iter->sort = _CLNEW Sort(sort_fields); - - try { - iter->hits = index->searcher->search(iter->query, iter->sort); - } catch (CLuceneError &err) { - lucene_handle_error(index, err, "rescan search"); - iter->failed = true; - } - return iter; -} - -const struct lucene_index_record * -lucene_index_iter_next(struct lucene_index_iter *iter) -{ - if (iter->hits == NULL) - return NULL; - if (iter->i == iter->hits->length()) - return NULL; - - Document *doc = &iter->hits->doc(iter->i); - iter->i++; - - memset(&iter->rec, 0, sizeof(iter->rec)); - (void)fts_lucene_get_mailbox_guid(iter->index, doc, - iter->rec.mailbox_guid); - (void)lucene_doc_get_uid(iter->index, doc, &iter->rec.uid); - iter->rec.part_num = lucene_doc_get_part(iter->index, doc); - return &iter->rec; -} - -int lucene_index_iter_deinit(struct lucene_index_iter **_iter) -{ - struct lucene_index_iter *iter = *_iter; - int ret = iter->failed ? -1 : 0; - - *_iter = NULL; - if (iter->hits != NULL) - _CLDELETE(iter->hits); - if (iter->query != NULL) { - _CLDELETE(iter->query); - _CLDELETE(iter->sort); - _CLDELETE(iter->term); - } - i_free(iter); - return ret; -} - -void lucene_shutdown(void) -{ - _lucene_shutdown(); -} diff --git a/src/plugins/fts-lucene/lucene-wrapper.h b/src/plugins/fts-lucene/lucene-wrapper.h deleted file mode 100644 index 270e9021b6..0000000000 --- a/src/plugins/fts-lucene/lucene-wrapper.h +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef LUCENE_WRAPPER_H -#define LUCENE_WRAPPER_H - -#include "fts-api-private.h" -#include "guid.h" - -struct mailbox_list; -struct fts_expunge_log; -struct fts_lucene_settings; - -#define MAILBOX_GUID_HEX_LENGTH (GUID_128_SIZE*2) - -struct lucene_index_record { - guid_128_t mailbox_guid; - uint32_t uid, part_num; -}; - -HASH_TABLE_DEFINE_TYPE(wguid_result, wchar_t *, struct fts_result *); - -struct lucene_index * -lucene_index_init(const char *path, struct mailbox_list *list, - const struct fts_lucene_settings *set) - ATTR_NULL(2, 3); -void lucene_index_deinit(struct lucene_index *index); - -void lucene_index_select_mailbox(struct lucene_index *index, - const wchar_t guid[MAILBOX_GUID_HEX_LENGTH]); -void lucene_index_unselect_mailbox(struct lucene_index *index); -int lucene_index_get_last_uid(struct lucene_index *index, uint32_t *last_uid_r); -int lucene_index_get_doc_count(struct lucene_index *index, uint32_t *count_r); - -int lucene_index_build_init(struct lucene_index *index); -int lucene_index_build_more(struct lucene_index *index, uint32_t uid, - uint32_t part_num, const unsigned char *data, - size_t size, const char *hdr_name); -int lucene_index_build_deinit(struct lucene_index *index); - -void lucene_index_close(struct lucene_index *index); -int lucene_index_rescan(struct lucene_index *index); -int lucene_index_expunge_from_log(struct lucene_index *index, - struct fts_expunge_log *log); -int lucene_index_optimize(struct lucene_index *index); - -int lucene_index_lookup(struct lucene_index *index, - struct mail_search_arg *args, - enum fts_lookup_flags flags, - struct fts_result *result); - -int lucene_index_lookup_multi(struct lucene_index *index, - HASH_TABLE_TYPE(wguid_result) guids, - struct mail_search_arg *args, - enum fts_lookup_flags flags, - struct fts_multi_result *result); - -struct lucene_index_iter * -lucene_index_iter_init(struct lucene_index *index); -const struct lucene_index_record * -lucene_index_iter_next(struct lucene_index_iter *iter); -int lucene_index_iter_deinit(struct lucene_index_iter **iter); - -/* internal: */ -void lucene_utf8_n_to_tchar(const unsigned char *src, size_t srcsize, - wchar_t *dest, size_t destsize); - -void lucene_shutdown(void); - -#endif diff --git a/src/plugins/fts-lucene/textcat.conf b/src/plugins/fts-lucene/textcat.conf deleted file mode 100644 index d75c4fe68f..0000000000 --- a/src/plugins/fts-lucene/textcat.conf +++ /dev/null @@ -1,25 +0,0 @@ -# -# A sample config file for the language models -# provided with Gertjan van Noords language guesser -# (http://odur.let.rug.nl/~vannoord/TextCat/) -# -# Notes: -# - You may consider eliminating a couple of small languages from this -# list because they cause false positives with big languages and are -# bad for performance. (Do you really want to recognize Drents?) -# - Putting the most probable languages at the top of the list -# improves performance, because this will raise the threshold for -# likely candidates more quickly. -# -LM/english.lm english -LM/italian.lm italian -LM/danish.lm danish -LM/dutch.lm dutch -LM/finnish.lm finnish -LM/french.lm french -LM/german.lm german -LM/norwegian.lm norwegian -LM/portuguese.lm portuguese -LM/russian.lm russian -LM/spanish.lm spanish -LM/swedish.lm swedish diff --git a/src/plugins/mail-crypt/Makefile.am b/src/plugins/mail-crypt/Makefile.am deleted file mode 100644 index 942dc87894..0000000000 --- a/src/plugins/mail-crypt/Makefile.am +++ /dev/null @@ -1,116 +0,0 @@ -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/doveadm-mail-crypt.c b/src/plugins/mail-crypt/doveadm-mail-crypt.c deleted file mode 100644 index a4322ed310..0000000000 --- a/src/plugins/mail-crypt/doveadm-mail-crypt.c +++ /dev/null @@ -1,1048 +0,0 @@ -/* 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 "" - -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 = ""; - 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 = ""; - 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 deleted file mode 100644 index 7432fa6572..0000000000 --- a/src/plugins/mail-crypt/fs-crypt-common.c +++ /dev/null @@ -1,368 +0,0 @@ -/* 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=:][set_prefix=:][public_key_path=:] - [private_key_path=:[password=:]] */ - 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 deleted file mode 100644 index ba70f8a977..0000000000 --- a/src/plugins/mail-crypt/fs-crypt-settings.c +++ /dev/null @@ -1,35 +0,0 @@ -/* 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 deleted file mode 100644 index df1a7b19ad..0000000000 --- a/src/plugins/mail-crypt/fs-crypt-settings.h +++ /dev/null @@ -1,11 +0,0 @@ -#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 deleted file mode 100644 index 0a81c69670..0000000000 --- a/src/plugins/mail-crypt/fs-crypt.c +++ /dev/null @@ -1,65 +0,0 @@ -/* 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 deleted file mode 100644 index b017d3f2d8..0000000000 --- a/src/plugins/mail-crypt/fs-mail-crypt.c +++ /dev/null @@ -1,72 +0,0 @@ -/* 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 deleted file mode 100644 index 71ab1e771f..0000000000 --- a/src/plugins/mail-crypt/mail-crypt-acl-plugin.c +++ /dev/null @@ -1,431 +0,0 @@ -/* 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 deleted file mode 100644 index 57e5e2fda3..0000000000 --- a/src/plugins/mail-crypt/mail-crypt-common.h +++ /dev/null @@ -1,30 +0,0 @@ -#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 deleted file mode 100644 index 01cb93759c..0000000000 --- a/src/plugins/mail-crypt/mail-crypt-global-key.c +++ /dev/null @@ -1,172 +0,0 @@ -/* 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 deleted file mode 100644 index 6f4679ac6d..0000000000 --- a/src/plugins/mail-crypt/mail-crypt-global-key.h +++ /dev/null @@ -1,38 +0,0 @@ -#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 deleted file mode 100644 index 22c86e34cf..0000000000 --- a/src/plugins/mail-crypt/mail-crypt-key.c +++ /dev/null @@ -1,1242 +0,0 @@ -/* 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 deleted file mode 100644 index f4a724a45a..0000000000 --- a/src/plugins/mail-crypt/mail-crypt-key.h +++ /dev/null @@ -1,119 +0,0 @@ -#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//.../crypt/active = digest for the active public key - that is used for encrypting new emails - shared//.../crypt/pubkeys/ = - private//.../crypt/privkeys/ = - - Similarly for users: - - shared//.../crypt/active = digest for the active public key that - is used for encrypting new folder keys - shared//.../crypt/pubkeys/ = - private//.../crypt/privkeys/ = -*/ - -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 deleted file mode 100644 index 21da3481f2..0000000000 --- a/src/plugins/mail-crypt/mail-crypt-plugin.c +++ /dev/null @@ -1,486 +0,0 @@ -/* 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 deleted file mode 100644 index 4556a92973..0000000000 --- a/src/plugins/mail-crypt/mail-crypt-plugin.h +++ /dev/null @@ -1,32 +0,0 @@ -#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 deleted file mode 100644 index 68cf94fc6f..0000000000 --- a/src/plugins/mail-crypt/mail-crypt-pluginenv.c +++ /dev/null @@ -1,106 +0,0 @@ -/* 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 deleted file mode 100644 index b152a7f01c..0000000000 --- a/src/plugins/mail-crypt/mail-crypt-userenv.c +++ /dev/null @@ -1,66 +0,0 @@ -/* 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 deleted file mode 100644 index 4c5ed38db0..0000000000 --- a/src/plugins/mail-crypt/test-mail-global-key.c +++ /dev/null @@ -1,130 +0,0 @@ -/* 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 deleted file mode 100644 index ac8983590d..0000000000 --- a/src/plugins/mail-crypt/test-mail-key.c +++ /dev/null @@ -1,424 +0,0 @@ -/* 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; -} diff --git a/src/plugins/var-expand-crypt/Makefile.am b/src/plugins/var-expand-crypt/Makefile.am deleted file mode 100644 index 6feb1a742d..0000000000 --- a/src/plugins/var-expand-crypt/Makefile.am +++ /dev/null @@ -1,39 +0,0 @@ -AM_CPPFLAGS = \ - -I$(top_srcdir)/src/lib \ - -I$(top_srcdir)/src/lib-test \ - -I$(top_srcdir)/src/lib-dcrypt - -NOPLUGIN_LDFLAGS = -lib20_var_expand_crypt_la_LDFLAGS = -module -avoid-version -lib20_auth_var_expand_crypt_la_LDFLAGS = -module -avoid-version - -auth_moduledir = $(moduledir)/auth - -module_LTLIBRARIES = \ - lib20_var_expand_crypt.la - -auth_module_LTLIBRARIES = \ - lib20_auth_var_expand_crypt.la - -lib20_auth_var_expand_crypt_la_SOURCES = \ - var-expand-crypt-plugin.c - -lib20_var_expand_crypt_la_SOURCES = \ - var-expand-crypt-plugin.c - -test_programs = test-var-expand-crypt - -test_var_expand_crypt_CFLAGS = \ - -DDCRYPT_BUILD_DIR=\"$(top_builddir)/src/lib-dcrypt\" -test_var_expand_crypt_SOURCES = \ - test-var-expand-crypt.c -test_var_expand_crypt_LDADD = \ - ../../lib-dovecot/libdovecot.la \ - lib20_auth_var_expand_crypt.la - -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/var-expand-crypt/test-var-expand-crypt.c b/src/plugins/var-expand-crypt/test-var-expand-crypt.c deleted file mode 100644 index e12a0402b6..0000000000 --- a/src/plugins/var-expand-crypt/test-var-expand-crypt.c +++ /dev/null @@ -1,102 +0,0 @@ -/* Copyright (c) 2017-2018 Dovecot authors, see the included COPYING file */ - -#include "lib.h" -#include "test-common.h" -#include "str.h" -#include "var-expand.h" -#include "randgen.h" -#include "dcrypt.h" - -struct module; - -extern void var_expand_crypt_init(struct module *module); -extern void var_expand_crypt_deinit(void); - -static void test_var_expand_crypt(void) -{ - struct var_expand_table table[] = { - { '\0', "98b3b40a48ca40f998b3b40a48ca40f9", "iv" }, - { '\0', "cc2981c8f38aea59cc2981c8f38aea59", "key" }, - { '\0', "46b58741763fe22598014be26331a082", "encrypted_noiv" }, - { '\0', "98b3b40a48ca40f998b3b40a48ca40f9$46b58741763fe22598014be26331a082$", "encrypted" }, - { '\0', "hello, world", "decrypted" }, - { '\0', NULL, "encrypted2" }, - { '\0', NULL, NULL } - }; - - static struct { - const char *input; - const char *output; - int expect_ret; - } test_cases[] = { - { "%{encrypt;algo=null:decrypted}", "", -1 }, - { "%{encrypt;algo=aes-128-cbc,iv=98b3b40a48ca40f998b3b40a48ca40f9,key=cc2981c8f38aea59cc2981c8f38aea59:decrypted}", "98b3b40a48ca40f998b3b40a48ca40f9$46b58741763fe22598014be26331a082$", 1 }, - { "%{encrypt;noiv=yes,algo=aes-128-cbc,iv=98b3b40a48ca40f998b3b40a48ca40f9,key=cc2981c8f38aea59cc2981c8f38aea59:decrypted}", "46b58741763fe22598014be26331a082", 1 }, - { "%{encrypt;algo=aes-128-cbc,iv=%{iv},key=%{key}:decrypted}", "98b3b40a48ca40f998b3b40a48ca40f9$46b58741763fe22598014be26331a082$", 1 }, - { "%{decrypt;algo=null:encrypted}", "", -1 }, - { "%{decrypt;algo=aes-128-cbc,key=%{key}:encrypted}", "hello, world", 1 }, - { "%{decrypt;algo=aes-128-cbc,iv=%{iv},key=%{key}:encrypted_noiv}", "hello, world", 1 }, - { "%{decrypt;algo=aes-128-cbc,iv=98b3b40a48ca40f998b3b40a48ca40f9,key=cc2981c8f38aea59cc2981c8f38aea59:encrypted_noiv}", "hello, world", 1 }, - }; - - unsigned int i; - - test_begin("var_expand_crypt"); - var_expand_crypt_init(NULL); - - for(i=0; i < N_ELEMENTS(test_cases); i++) T_BEGIN { - const char *error; - string_t *dest = t_str_new(32); - int ret = var_expand(dest, test_cases[i].input, table, &error); - if (ret < 0) { - if (test_cases[i].expect_ret == -1) - i_info("Expected: var_expand(%s): %s", test_cases[i].input, error); - else - i_error("var_expand(%s): %s", test_cases[i].input, error); - } - test_assert_idx(strcmp(str_c(dest), test_cases[i].output)==0, i); - test_assert_idx(ret == test_cases[i].expect_ret, i); - } T_END; - - test_end(); - - test_begin("var_expand_crypt_random"); - - string_t *input = t_str_new(32); - string_t *output = t_str_new(32); - - for(i=0;i<1000;i++) { - const char *error; - str_truncate(input, 0); - str_truncate(output, 0); - - test_assert_idx(var_expand(input, "%{encrypt;algo=aes-128-cbc,key=%{key}:decrypted}", table, &error) == 1, i); - table[5].value = str_c(input); - test_assert_idx(var_expand(output, "%{decrypt;algo=aes-128-cbc,key=%{key}:encrypted2}", table, &error) == 1, i); - test_assert_idx(strcmp(str_c(output), table[4].value)==0, i); - }; - - var_expand_crypt_deinit(); - test_end(); -} - -int main(void) -{ - int ret = 0; - static void (*const test_functions[])(void) = { - test_var_expand_crypt, - NULL - }; - struct dcrypt_settings set = { - .module_dir = DCRYPT_BUILD_DIR"/.libs" - }; - - if (!dcrypt_initialize(NULL, &set, NULL)) - return 0; - - ret = test_run(test_functions); - - dcrypt_deinitialize(); - - return ret; -} diff --git a/src/plugins/var-expand-crypt/var-expand-crypt-plugin.c b/src/plugins/var-expand-crypt/var-expand-crypt-plugin.c deleted file mode 100644 index 1f6cce7fe6..0000000000 --- a/src/plugins/var-expand-crypt/var-expand-crypt-plugin.c +++ /dev/null @@ -1,335 +0,0 @@ -/* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */ - -#include "lib.h" -#include "array.h" -#include "hex-binary.h" -#include "base64.h" -#include "str.h" -#include "strescape.h" -#include "var-expand.h" -#include "var-expand-private.h" -#include "dcrypt.h" - -#define VAR_EXPAND_CRYPT_DEFAULT_ALGO "AES-256-CBC" - -struct module; - -enum crypt_field_format { - FORMAT_HEX, - FORMAT_BASE64 -}; - -struct var_expand_crypt_context { - struct var_expand_context *ctx; - const char *algo; - string_t *iv; - string_t *enckey; - enum crypt_field_format format; - bool enc_result_only:1; -}; - -static bool var_expand_crypt_initialize(const char **error_r); - -void var_expand_crypt_init(struct module *module); -void var_expand_crypt_deinit(void); -void auth_var_expand_crypt_init(struct module *module); -void auth_var_expand_crypt_deinit(void); - -static int -var_expand_crypt_settings(struct var_expand_crypt_context *ctx, - const char *const *args, const char **error_r) -{ - while(args != NULL && *args != NULL) { - const char *k = t_strcut(*args, '='); - const char *value = strchr(*args, '='); - if (value == NULL) { - args++; - continue; - } else { - value++; - } - - if (strcmp(k, "iv") == 0) { - str_truncate(ctx->iv, 0); - if (var_expand_with_funcs(ctx->iv, value, ctx->ctx->table, - ctx->ctx->func_table, - ctx->ctx->context, error_r) < 0) { - return -1; - } - const char *hexiv = t_strdup(str_c(ctx->iv)); - /* try to decode IV */ - str_truncate(ctx->iv, 0); - hex_to_binary(hexiv, ctx->iv); - } if (strcmp(k, "noiv") == 0) { - ctx->enc_result_only = strcasecmp(value, "yes")==0; - } if (strcmp(k, "algo") == 0) { - ctx->algo = value; - } else if (strcmp(k, "key") == 0) { - str_truncate(ctx->enckey, 0); - if (var_expand_with_funcs(ctx->enckey, value, - ctx->ctx->table, - ctx->ctx->func_table, - ctx->ctx->context, - error_r) < 0) { - return -1; - } - const char *hexkey = t_strdup(str_c(ctx->enckey)); - str_truncate(ctx->enckey, 0); - hex_to_binary(hexkey, ctx->enckey); - } else if (strcmp(k, "format") == 0) { - if (strcmp(value, "hex") == 0) { - ctx->format = FORMAT_HEX; - } else if (strcmp(value, "base64") == 0) { - ctx->format = FORMAT_BASE64; - } else { - *error_r = t_strdup_printf( - "Cannot parse hash arguments:" - "'%s' is not supported format", - value); - return -1; - } - } - args++; - } - - if (ctx->algo == NULL) { - ctx->algo = "AES-256-CBC"; - } - - return 0; -} - -static int -var_expand_crypt(struct dcrypt_context_symmetric *dctx, buffer_t *key, buffer_t *iv, - const buffer_t *input, buffer_t *output, const char **error_r) -{ - /* make sure IV is correct */ - if (iv->used == 0) { - dcrypt_ctx_sym_set_key_iv_random(dctx); - /* acquire IV */ - dcrypt_ctx_sym_get_iv(dctx, iv); - } else if (dcrypt_ctx_sym_get_iv_length(dctx) != iv->used) { - *error_r = t_strdup_printf("crypt: IV length invalid (%zu != %u)", - iv->used, - dcrypt_ctx_sym_get_iv_length(dctx)); - return -1; - } else { - dcrypt_ctx_sym_set_iv(dctx, iv->data, iv->used); - } - - if (dcrypt_ctx_sym_get_key_length(dctx) != key->used) { - *error_r = t_strdup_printf("crypt: Key length invalid (%zu != %u)", - key->used, - dcrypt_ctx_sym_get_key_length(dctx)); - return -1; - } else { - dcrypt_ctx_sym_set_key(dctx, key->data, key->used); - } - - if (!dcrypt_ctx_sym_init(dctx, error_r) || - !dcrypt_ctx_sym_update(dctx, input->data, - input->used, output, error_r) || - !dcrypt_ctx_sym_final(dctx, output, error_r)) - return -1; - return 0; -} - -static int -var_expand_encrypt(struct var_expand_context *_ctx, - const char *key, const char *field, - const char **result_r, const char **error_r) -{ - if (!var_expand_crypt_initialize(error_r)) - return -1; - - const char *p = strchr(key, ';'); - const char *const *args = NULL; - const char *value; - struct var_expand_crypt_context ctx; - string_t *dest; - int ret = 0; - - memset(&ctx, 0, sizeof(ctx)); - ctx.ctx = _ctx; - ctx.format = FORMAT_HEX; - - if (p != NULL) { - args = t_strsplit(p+1, ","); - } - - string_t *field_value = t_str_new(64); - ctx.iv = t_str_new(64); - ctx.enckey = t_str_new(64); - string_t *tmp = t_str_new(128); - - if ((ret = var_expand_long(_ctx, field, strlen(field), - &value, error_r)) < 1) { - return ret; - } - - if (*value == '\0') { - *result_r = value; - return ret; - } - - if (var_expand_crypt_settings(&ctx, args, error_r) < 0) - return -1; - - str_append(field_value, value); - - struct dcrypt_context_symmetric *dctx; - if (!dcrypt_ctx_sym_create(ctx.algo, DCRYPT_MODE_ENCRYPT, &dctx, error_r)) - return -1; - - ret = var_expand_crypt(dctx, ctx.enckey, ctx.iv, field_value, tmp, error_r); - dcrypt_ctx_sym_destroy(&dctx); - - if (ret == 0) { - /* makes compiler happy */ - const char *enciv = ""; - const char *res = ""; - - switch(ctx.format) { - case FORMAT_HEX: - enciv = binary_to_hex(ctx.iv->data, ctx.iv->used); - res = binary_to_hex(tmp->data, tmp->used); - break; - case FORMAT_BASE64: - dest = t_str_new(32); - base64_encode(ctx.iv->data, ctx.iv->used, dest); - enciv = str_c(dest); - dest = t_str_new(32); - base64_encode(tmp->data, tmp->used, dest); - res = str_c(dest); - break; - default: - i_unreached(); - } - if (ctx.enc_result_only) - *result_r = t_strdup(res); - else - *result_r = t_strdup_printf("%s$%s$", enciv, res); - ret = 1; - } - - return ret; -} - -static int -var_expand_decrypt(struct var_expand_context *_ctx, - const char *key, const char *field, - const char **result_r, const char **error_r) -{ - if (!var_expand_crypt_initialize(error_r)) - return -1; - - const char *p = strchr(key, ';'); - const char *const *args = NULL; - const char *value; - struct var_expand_crypt_context ctx; - int ret = 0; - - memset(&ctx, 0, sizeof(ctx)); - ctx.ctx = _ctx; - ctx.format = FORMAT_HEX; - - if (p != NULL) { - args = t_strsplit(p+1, ","); - } - - string_t *field_value = t_str_new(64); - ctx.iv = t_str_new(64); - ctx.enckey = t_str_new(64); - string_t *tmp = t_str_new(128); - - if ((ret = var_expand_long(_ctx, field, strlen(field), - &value, error_r)) < 1) { - return ret; - } - - if (*value == '\0') { - *result_r = value; - return ret; - } - - if (var_expand_crypt_settings(&ctx, args, error_r) < 0) - return -1; - - const char *encdata = value; - const char *enciv = ""; - - /* make sure IV is correct */ - if (ctx.iv->used == 0 && (p = strchr(encdata, '$')) != NULL) { - /* see if IV can be taken from data */ - enciv = t_strcut(encdata, '$'); - encdata = t_strcut(p+1,'$'); - } - - str_truncate(field_value, 0); - - /* try to decode iv and encdata */ - switch(ctx.format) { - case FORMAT_HEX: - if (ctx.iv->used == 0) - hex_to_binary(enciv, ctx.iv); - hex_to_binary(encdata, field_value); - break; - case FORMAT_BASE64: - if (ctx.iv->used == 0) - str_append_str(ctx.iv, t_base64_decode_str(enciv)); - str_append_str(field_value, t_base64_decode_str(encdata)); - break; - } - - if (ctx.iv->used == 0) { - *error_r = t_strdup_printf("decrypt: IV missing"); - return -1; - } - - struct dcrypt_context_symmetric *dctx; - if (!dcrypt_ctx_sym_create(ctx.algo, DCRYPT_MODE_DECRYPT, &dctx, error_r)) - return -1; - ret = var_expand_crypt(dctx, ctx.enckey, ctx.iv, field_value, tmp, error_r); - dcrypt_ctx_sym_destroy(&dctx); - - if (ret == 0) { - *result_r = str_c(tmp); - ret = 1; - } - - return ret; -} - -static const struct var_expand_extension_func_table funcs[] = { - { "encrypt", var_expand_encrypt }, - { "decrypt", var_expand_decrypt }, - { NULL, NULL, } -}; - -static bool var_expand_crypt_initialize(const char **error_r) -{ - return dcrypt_initialize(NULL, NULL, error_r); -} - -void var_expand_crypt_init(struct module *module ATTR_UNUSED) -{ - var_expand_register_func_array(funcs); - /* do not initialize dcrypt here - saves alot of memory - to not load openssl every time. Only load it if - needed */ -} - -void var_expand_crypt_deinit(void) -{ - var_expand_unregister_func_array(funcs); -} - -void auth_var_expand_crypt_init(struct module *module) -{ - var_expand_crypt_init(module); -} - -void auth_var_expand_crypt_deinit(void) -{ - var_expand_crypt_deinit(); -}