diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 17:36:47 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 17:36:47 +0000 |
commit | 0441d265f2bb9da249c7abf333f0f771fadb4ab5 (patch) | |
tree | 3f3789daa2f6db22da6e55e92bee0062a7d613fe /src/lib-imap-urlauth | |
parent | Initial commit. (diff) | |
download | dovecot-0441d265f2bb9da249c7abf333f0f771fadb4ab5.tar.xz dovecot-0441d265f2bb9da249c7abf333f0f771fadb4ab5.zip |
Adding upstream version 1:2.3.21+dfsg1.upstream/1%2.3.21+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/lib-imap-urlauth')
-rw-r--r-- | src/lib-imap-urlauth/Makefile.am | 29 | ||||
-rw-r--r-- | src/lib-imap-urlauth/Makefile.in | 835 | ||||
-rw-r--r-- | src/lib-imap-urlauth/imap-urlauth-backend.c | 174 | ||||
-rw-r--r-- | src/lib-imap-urlauth/imap-urlauth-backend.h | 16 | ||||
-rw-r--r-- | src/lib-imap-urlauth/imap-urlauth-connection.c | 1027 | ||||
-rw-r--r-- | src/lib-imap-urlauth/imap-urlauth-connection.h | 42 | ||||
-rw-r--r-- | src/lib-imap-urlauth/imap-urlauth-fetch.c | 530 | ||||
-rw-r--r-- | src/lib-imap-urlauth/imap-urlauth-fetch.h | 56 | ||||
-rw-r--r-- | src/lib-imap-urlauth/imap-urlauth-private.h | 20 | ||||
-rw-r--r-- | src/lib-imap-urlauth/imap-urlauth.c | 486 | ||||
-rw-r--r-- | src/lib-imap-urlauth/imap-urlauth.h | 55 |
11 files changed, 3270 insertions, 0 deletions
diff --git a/src/lib-imap-urlauth/Makefile.am b/src/lib-imap-urlauth/Makefile.am new file mode 100644 index 0000000..6734b72 --- /dev/null +++ b/src/lib-imap-urlauth/Makefile.am @@ -0,0 +1,29 @@ +noinst_LTLIBRARIES = libimap-urlauth.la + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/lib-test \ + -I$(top_srcdir)/src/lib-charset \ + -I$(top_srcdir)/src/lib-dict \ + -I$(top_srcdir)/src/lib-mail \ + -I$(top_srcdir)/src/lib-index \ + -I$(top_srcdir)/src/lib-storage \ + -I$(top_srcdir)/src/lib-imap \ + -I$(top_srcdir)/src/lib-imap-storage + +libimap_urlauth_la_SOURCES = \ + imap-urlauth.c \ + imap-urlauth-fetch.c \ + imap-urlauth-backend.c \ + imap-urlauth-connection.c + +headers = \ + imap-urlauth.h \ + imap-urlauth-private.h \ + imap-urlauth-fetch.h \ + imap-urlauth-backend.h \ + imap-urlauth-connection.h + +pkginc_libdir=$(pkgincludedir) +pkginc_lib_HEADERS = $(headers) + diff --git a/src/lib-imap-urlauth/Makefile.in b/src/lib-imap-urlauth/Makefile.in new file mode 100644 index 0000000..9ff6efa --- /dev/null +++ b/src/lib-imap-urlauth/Makefile.in @@ -0,0 +1,835 @@ +# Makefile.in generated by automake 1.16.3 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2020 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src/lib-imap-urlauth +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ac_checktype2.m4 \ + $(top_srcdir)/m4/ac_typeof.m4 $(top_srcdir)/m4/arc4random.m4 \ + $(top_srcdir)/m4/blockdev.m4 $(top_srcdir)/m4/c99_vsnprintf.m4 \ + $(top_srcdir)/m4/clock_gettime.m4 $(top_srcdir)/m4/crypt.m4 \ + $(top_srcdir)/m4/crypt_xpg6.m4 $(top_srcdir)/m4/dbqlk.m4 \ + $(top_srcdir)/m4/dirent_dtype.m4 $(top_srcdir)/m4/dovecot.m4 \ + $(top_srcdir)/m4/fd_passing.m4 $(top_srcdir)/m4/fdatasync.m4 \ + $(top_srcdir)/m4/flexible_array_member.m4 \ + $(top_srcdir)/m4/glibc.m4 $(top_srcdir)/m4/gmtime_max.m4 \ + $(top_srcdir)/m4/gmtime_tm_gmtoff.m4 \ + $(top_srcdir)/m4/ioloop.m4 $(top_srcdir)/m4/iovec.m4 \ + $(top_srcdir)/m4/ipv6.m4 $(top_srcdir)/m4/libcap.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libwrap.m4 \ + $(top_srcdir)/m4/linux_mremap.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/mmap_write.m4 \ + $(top_srcdir)/m4/mntctl.m4 $(top_srcdir)/m4/modules.m4 \ + $(top_srcdir)/m4/notify.m4 $(top_srcdir)/m4/nsl.m4 \ + $(top_srcdir)/m4/off_t_max.m4 $(top_srcdir)/m4/pkg.m4 \ + $(top_srcdir)/m4/pr_set_dumpable.m4 \ + $(top_srcdir)/m4/q_quotactl.m4 $(top_srcdir)/m4/quota.m4 \ + $(top_srcdir)/m4/random.m4 $(top_srcdir)/m4/rlimit.m4 \ + $(top_srcdir)/m4/sendfile.m4 $(top_srcdir)/m4/size_t_signed.m4 \ + $(top_srcdir)/m4/sockpeercred.m4 $(top_srcdir)/m4/sql.m4 \ + $(top_srcdir)/m4/ssl.m4 $(top_srcdir)/m4/st_tim.m4 \ + $(top_srcdir)/m4/static_array.m4 $(top_srcdir)/m4/test_with.m4 \ + $(top_srcdir)/m4/time_t.m4 $(top_srcdir)/m4/typeof.m4 \ + $(top_srcdir)/m4/typeof_dev_t.m4 \ + $(top_srcdir)/m4/uoff_t_max.m4 $(top_srcdir)/m4/vararg.m4 \ + $(top_srcdir)/m4/want_apparmor.m4 \ + $(top_srcdir)/m4/want_bsdauth.m4 \ + $(top_srcdir)/m4/want_bzlib.m4 \ + $(top_srcdir)/m4/want_cassandra.m4 \ + $(top_srcdir)/m4/want_cdb.m4 \ + $(top_srcdir)/m4/want_checkpassword.m4 \ + $(top_srcdir)/m4/want_clucene.m4 $(top_srcdir)/m4/want_db.m4 \ + $(top_srcdir)/m4/want_gssapi.m4 $(top_srcdir)/m4/want_icu.m4 \ + $(top_srcdir)/m4/want_ldap.m4 $(top_srcdir)/m4/want_lua.m4 \ + $(top_srcdir)/m4/want_lz4.m4 $(top_srcdir)/m4/want_lzma.m4 \ + $(top_srcdir)/m4/want_mysql.m4 $(top_srcdir)/m4/want_pam.m4 \ + $(top_srcdir)/m4/want_passwd.m4 $(top_srcdir)/m4/want_pgsql.m4 \ + $(top_srcdir)/m4/want_prefetch.m4 \ + $(top_srcdir)/m4/want_shadow.m4 \ + $(top_srcdir)/m4/want_sodium.m4 $(top_srcdir)/m4/want_solr.m4 \ + $(top_srcdir)/m4/want_sqlite.m4 \ + $(top_srcdir)/m4/want_stemmer.m4 \ + $(top_srcdir)/m4/want_systemd.m4 \ + $(top_srcdir)/m4/want_textcat.m4 \ + $(top_srcdir)/m4/want_unwind.m4 $(top_srcdir)/m4/want_zlib.m4 \ + $(top_srcdir)/m4/want_zstd.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \ + $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +libimap_urlauth_la_LIBADD = +am_libimap_urlauth_la_OBJECTS = imap-urlauth.lo imap-urlauth-fetch.lo \ + imap-urlauth-backend.lo imap-urlauth-connection.lo +libimap_urlauth_la_OBJECTS = $(am_libimap_urlauth_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/imap-urlauth-backend.Plo \ + ./$(DEPDIR)/imap-urlauth-connection.Plo \ + ./$(DEPDIR)/imap-urlauth-fetch.Plo \ + ./$(DEPDIR)/imap-urlauth.Plo +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libimap_urlauth_la_SOURCES) +DIST_SOURCES = $(libimap_urlauth_la_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(pkginc_libdir)" +HEADERS = $(pkginc_lib_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPARMOR_LIBS = @APPARMOR_LIBS@ +AR = @AR@ +AUTH_CFLAGS = @AUTH_CFLAGS@ +AUTH_LIBS = @AUTH_LIBS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BINARY_CFLAGS = @BINARY_CFLAGS@ +BINARY_LDFLAGS = @BINARY_LDFLAGS@ +BISON = @BISON@ +CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ +CASSANDRA_LIBS = @CASSANDRA_LIBS@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CDB_LIBS = @CDB_LIBS@ +CFLAGS = @CFLAGS@ +CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ +CLUCENE_LIBS = @CLUCENE_LIBS@ +COMPRESS_LIBS = @COMPRESS_LIBS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CRYPT_LIBS = @CRYPT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DICT_LIBS = @DICT_LIBS@ +DLLIB = @DLLIB@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FLEX = @FLEX@ +FUZZER_CPPFLAGS = @FUZZER_CPPFLAGS@ +FUZZER_LDFLAGS = @FUZZER_LDFLAGS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +KRB5CONFIG = @KRB5CONFIG@ +KRB5_CFLAGS = @KRB5_CFLAGS@ +KRB5_LIBS = @KRB5_LIBS@ +LD = @LD@ +LDAP_LIBS = @LDAP_LIBS@ +LDFLAGS = @LDFLAGS@ +LD_NO_WHOLE_ARCHIVE = @LD_NO_WHOLE_ARCHIVE@ +LD_WHOLE_ARCHIVE = @LD_WHOLE_ARCHIVE@ +LIBCAP = @LIBCAP@ +LIBDOVECOT = @LIBDOVECOT@ +LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ +LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ +LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ +LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ +LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ +LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ +LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ +LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ +LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ +LIBDOVECOT_LUA = @LIBDOVECOT_LUA@ +LIBDOVECOT_LUA_DEPS = @LIBDOVECOT_LUA_DEPS@ +LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ +LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ +LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ +LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ +LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ +LIBICONV = @LIBICONV@ +LIBICU_CFLAGS = @LIBICU_CFLAGS@ +LIBICU_LIBS = @LIBICU_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSODIUM_CFLAGS = @LIBSODIUM_CFLAGS@ +LIBSODIUM_LIBS = @LIBSODIUM_LIBS@ +LIBTIRPC_CFLAGS = @LIBTIRPC_CFLAGS@ +LIBTIRPC_LIBS = @LIBTIRPC_LIBS@ +LIBTOOL = @LIBTOOL@ +LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@ +LIBUNWIND_LIBS = @LIBUNWIND_LIBS@ +LIBWRAP_LIBS = @LIBWRAP_LIBS@ +LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBICONV = @LTLIBICONV@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +LUA_CFLAGS = @LUA_CFLAGS@ +LUA_LIBS = @LUA_LIBS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MODULE_LIBS = @MODULE_LIBS@ +MODULE_SUFFIX = @MODULE_SUFFIX@ +MYSQL_CFLAGS = @MYSQL_CFLAGS@ +MYSQL_CONFIG = @MYSQL_CONFIG@ +MYSQL_LIBS = @MYSQL_LIBS@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PANDOC = @PANDOC@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PGSQL_CFLAGS = @PGSQL_CFLAGS@ +PGSQL_LIBS = @PGSQL_LIBS@ +PG_CONFIG = @PG_CONFIG@ +PIE_CFLAGS = @PIE_CFLAGS@ +PIE_LDFLAGS = @PIE_LDFLAGS@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +QUOTA_LIBS = @QUOTA_LIBS@ +RANLIB = @RANLIB@ +RELRO_LDFLAGS = @RELRO_LDFLAGS@ +RPCGEN = @RPCGEN@ +RUN_TEST = @RUN_TEST@ +SED = @SED@ +SETTING_FILES = @SETTING_FILES@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SQLITE_CFLAGS = @SQLITE_CFLAGS@ +SQLITE_LIBS = @SQLITE_LIBS@ +SQL_CFLAGS = @SQL_CFLAGS@ +SQL_LIBS = @SQL_LIBS@ +SSL_CFLAGS = @SSL_CFLAGS@ +SSL_LIBS = @SSL_LIBS@ +STRIP = @STRIP@ +SYSTEMD_CFLAGS = @SYSTEMD_CFLAGS@ +SYSTEMD_LIBS = @SYSTEMD_LIBS@ +VALGRIND = @VALGRIND@ +VERSION = @VERSION@ +ZSTD_CFLAGS = @ZSTD_CFLAGS@ +ZSTD_LIBS = @ZSTD_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +dict_drivers = @dict_drivers@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +moduledir = @moduledir@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +rundir = @rundir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sql_drivers = @sql_drivers@ +srcdir = @srcdir@ +ssldir = @ssldir@ +statedir = @statedir@ +sysconfdir = @sysconfdir@ +systemdservicetype = @systemdservicetype@ +systemdsystemunitdir = @systemdsystemunitdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +noinst_LTLIBRARIES = libimap-urlauth.la +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/lib-test \ + -I$(top_srcdir)/src/lib-charset \ + -I$(top_srcdir)/src/lib-dict \ + -I$(top_srcdir)/src/lib-mail \ + -I$(top_srcdir)/src/lib-index \ + -I$(top_srcdir)/src/lib-storage \ + -I$(top_srcdir)/src/lib-imap \ + -I$(top_srcdir)/src/lib-imap-storage + +libimap_urlauth_la_SOURCES = \ + imap-urlauth.c \ + imap-urlauth-fetch.c \ + imap-urlauth-backend.c \ + imap-urlauth-connection.c + +headers = \ + imap-urlauth.h \ + imap-urlauth-private.h \ + imap-urlauth-fetch.h \ + imap-urlauth-backend.h \ + imap-urlauth-connection.h + +pkginc_libdir = $(pkgincludedir) +pkginc_lib_HEADERS = $(headers) +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-imap-urlauth/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib-imap-urlauth/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libimap-urlauth.la: $(libimap_urlauth_la_OBJECTS) $(libimap_urlauth_la_DEPENDENCIES) $(EXTRA_libimap_urlauth_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libimap_urlauth_la_OBJECTS) $(libimap_urlauth_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-urlauth-backend.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-urlauth-connection.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-urlauth-fetch.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap-urlauth.Plo@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) + @$(NORMAL_INSTALL) + @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ + done + +uninstall-pkginc_libHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(pkginc_libdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/imap-urlauth-backend.Plo + -rm -f ./$(DEPDIR)/imap-urlauth-connection.Plo + -rm -f ./$(DEPDIR)/imap-urlauth-fetch.Plo + -rm -f ./$(DEPDIR)/imap-urlauth.Plo + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-pkginc_libHEADERS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/imap-urlauth-backend.Plo + -rm -f ./$(DEPDIR)/imap-urlauth-connection.Plo + -rm -f ./$(DEPDIR)/imap-urlauth-fetch.Plo + -rm -f ./$(DEPDIR)/imap-urlauth.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-pkginc_libHEADERS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libtool clean-noinstLTLIBRARIES \ + cscopelist-am ctags ctags-am distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-pkginc_libHEADERS install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am uninstall-pkginc_libHEADERS + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/lib-imap-urlauth/imap-urlauth-backend.c b/src/lib-imap-urlauth/imap-urlauth-backend.c new file mode 100644 index 0000000..de2b9c9 --- /dev/null +++ b/src/lib-imap-urlauth/imap-urlauth-backend.c @@ -0,0 +1,174 @@ +/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "buffer.h" +#include "hex-binary.h" +#include "randgen.h" +#include "mail-user.h" +#include "mail-storage.h" +#include "mailbox-list-iter.h" +#include "imap-urlauth-private.h" +#include "imap-urlauth-backend.h" + +#define IMAP_URLAUTH_KEY MAILBOX_ATTRIBUTE_PREFIX_DOVECOT"imap-urlauth" + +static int +imap_urlauth_backend_trans_set_mailbox_key(struct mailbox *box, + unsigned char mailbox_key_r[IMAP_URLAUTH_KEY_LEN], + const char **client_error_r, + enum mail_error *error_code_r) +{ + struct mail_attribute_value urlauth_key; + const char *mailbox_key_hex = NULL; + int ret; + + if (mailbox_open(box) < 0) { + *client_error_r = mailbox_get_last_error(box, error_code_r); + return -1; + } + + struct mailbox_transaction_context *t = + mailbox_transaction_begin(box, + MAILBOX_TRANSACTION_FLAG_EXTERNAL, + __func__); + + /* create new key */ + random_fill(mailbox_key_r, IMAP_URLAUTH_KEY_LEN); + mailbox_key_hex = binary_to_hex(mailbox_key_r, + IMAP_URLAUTH_KEY_LEN); + i_zero(&urlauth_key); + urlauth_key.value = mailbox_key_hex; + ret = mailbox_attribute_set(t, MAIL_ATTRIBUTE_TYPE_PRIVATE, + IMAP_URLAUTH_KEY, &urlauth_key); + + if (mailbox_transaction_commit(&t) < 0) { + *client_error_r = mailbox_get_last_error(box, error_code_r); + ret = -1; + } + + return ret; +} + +static int +imap_urlauth_backend_trans_get_mailbox_key(struct mailbox *box, + bool create, + unsigned char mailbox_key_r[IMAP_URLAUTH_KEY_LEN], + const char **client_error_r, + enum mail_error *error_code_r) +{ + struct mail_user *user = mail_storage_get_user(mailbox_get_storage(box)); + struct mail_attribute_value urlauth_key; + const char *mailbox_key_hex = NULL; + buffer_t key_buf; + int ret; + + *client_error_r = "Internal server error"; + *error_code_r = MAIL_ERROR_TEMP; + + ret = mailbox_attribute_get(box, MAIL_ATTRIBUTE_TYPE_PRIVATE, + IMAP_URLAUTH_KEY, &urlauth_key); + if (ret < 0) + return -1; + + e_debug(user->event, "imap-urlauth: %skey found for mailbox %s", + (ret > 0 ? "" : "no "), mailbox_get_vname(box)); + + if (ret == 0) { + if (!create) + return 0; + + ret = imap_urlauth_backend_trans_set_mailbox_key(box, + mailbox_key_r, + client_error_r, + error_code_r); + + if (ret < 0) + return -1; + e_debug(user->event, "imap-urlauth: created key for mailbox %s", + mailbox_get_vname(box)); + } else { + /* read existing key */ + buffer_create_from_data(&key_buf, mailbox_key_r, + IMAP_URLAUTH_KEY_LEN); + mailbox_key_hex = urlauth_key.value; + if (strlen(mailbox_key_hex) != 2*IMAP_URLAUTH_KEY_LEN || + hex_to_binary(mailbox_key_hex, &key_buf) < 0 || + key_buf.used != IMAP_URLAUTH_KEY_LEN) { + i_error("imap-urlauth: key found for mailbox %s is invalid", + mailbox_get_vname(box)); + return -1; + } + } + return 1; +} + +int imap_urlauth_backend_get_mailbox_key(struct mailbox *box, bool create, + unsigned char mailbox_key_r[IMAP_URLAUTH_KEY_LEN], + const char **client_error_r, + enum mail_error *error_code_r) +{ + int ret; + + ret = imap_urlauth_backend_trans_get_mailbox_key(box, create, + mailbox_key_r, + client_error_r, + error_code_r); + return ret; +} + +int imap_urlauth_backend_reset_mailbox_key(struct mailbox *box) +{ + struct mailbox_transaction_context *t; + int ret; + + t = mailbox_transaction_begin(box, MAILBOX_TRANSACTION_FLAG_EXTERNAL, + __func__); + ret = mailbox_attribute_unset(t, MAIL_ATTRIBUTE_TYPE_PRIVATE, + IMAP_URLAUTH_KEY); + if (mailbox_transaction_commit(&t) < 0) + ret = -1; + return ret; +} + +static int imap_urlauth_backend_mailbox_reset_key(struct mailbox *box) +{ + const char *errstr; + enum mail_error error; + + if (mailbox_open(box) < 0) { + errstr = mailbox_get_last_internal_error(box, &error); + if (error == MAIL_ERROR_NOTFOUND || error == MAIL_ERROR_PERM) + return 0; + i_error("urlauth key reset: Couldn't open mailbox %s: %s", + mailbox_get_vname(box), errstr); + return -1; + } + return imap_urlauth_backend_reset_mailbox_key(box); +} + +int imap_urlauth_backend_reset_all_keys(struct mail_user *user) +{ + const char *const patterns[] = { "*", NULL }; + struct mailbox_list_iterate_context *iter; + const struct mailbox_info *info; + struct mailbox *box; + int ret = 0; + + iter = mailbox_list_iter_init_namespaces(user->namespaces, patterns, + MAIL_NAMESPACE_TYPE_MASK_ALL, + MAILBOX_LIST_ITER_NO_AUTO_BOXES | + MAILBOX_LIST_ITER_SKIP_ALIASES | + MAILBOX_LIST_ITER_RETURN_NO_FLAGS); + while ((info = mailbox_list_iter_next(iter)) != NULL) { + box = mailbox_alloc(info->ns->list, info->vname, 0); + if (imap_urlauth_backend_mailbox_reset_key(box) < 0) + ret = -1; + mailbox_free(&box); + } + if (mailbox_list_iter_deinit(&iter) < 0) { + i_error("urlauth key reset: Couldn't iterate mailboxes: %s", + mailbox_list_get_last_internal_error(user->namespaces->list, NULL)); + ret = -1; + } + return ret; +} diff --git a/src/lib-imap-urlauth/imap-urlauth-backend.h b/src/lib-imap-urlauth/imap-urlauth-backend.h new file mode 100644 index 0000000..b76ecc5 --- /dev/null +++ b/src/lib-imap-urlauth/imap-urlauth-backend.h @@ -0,0 +1,16 @@ +#ifndef IMAP_URLAUTH_BACKEND_H +#define IMAP_URLAUTH_BACKEND_H + +#define IMAP_URLAUTH_KEY_LEN 64 + +struct imap_urlauth_backend; + +int imap_urlauth_backend_get_mailbox_key(struct mailbox *box, bool create, + unsigned char mailbox_key_r[IMAP_URLAUTH_KEY_LEN], + const char **client_error_r, + enum mail_error *error_code_r); +int imap_urlauth_backend_reset_mailbox_key(struct mailbox *box); +int imap_urlauth_backend_reset_all_keys(struct mail_user *user); + +#endif + diff --git a/src/lib-imap-urlauth/imap-urlauth-connection.c b/src/lib-imap-urlauth/imap-urlauth-connection.c new file mode 100644 index 0000000..5fce6f7 --- /dev/null +++ b/src/lib-imap-urlauth/imap-urlauth-connection.c @@ -0,0 +1,1027 @@ +/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "llist.h" +#include "str.h" +#include "str-sanitize.h" +#include "strescape.h" +#include "ioloop.h" +#include "safe-mkstemp.h" +#include "hostpid.h" +#include "net.h" +#include "istream.h" +#include "ostream.h" +#include "write-full.h" +#include "array.h" +#include "aqueue.h" +#include "mail-user.h" +#include "imap-urlauth-fetch.h" + +#include "imap-urlauth-connection.h" + +enum imap_urlauth_state { + IMAP_URLAUTH_STATE_DISCONNECTED = 0, + IMAP_URLAUTH_STATE_AUTHENTICATING, + IMAP_URLAUTH_STATE_AUTHENTICATED, + IMAP_URLAUTH_STATE_SELECTING_TARGET, + IMAP_URLAUTH_STATE_UNSELECTING_TARGET, + IMAP_URLAUTH_STATE_READY, + IMAP_URLAUTH_STATE_REQUEST_PENDING, + IMAP_URLAUTH_STATE_REQUEST_WAIT, +}; + +struct imap_urlauth_request { + struct imap_urlauth_target *target; + struct imap_urlauth_request *prev, *next; + + char *url; + enum imap_urlauth_fetch_flags flags; + + char *bodypartstruct; + + imap_urlauth_request_callback_t *callback; + void *context; + + bool binary_has_nuls; +}; + +struct imap_urlauth_target { + struct imap_urlauth_target *prev, *next; + + char *userid; + + struct imap_urlauth_request *requests_head, *requests_tail; +}; + +struct imap_urlauth_connection { + int refcount; + + char *path, *service, *session_id; + struct mail_user *user; + + int fd; + struct istream *input; + struct ostream *output; + struct io *io; + + struct timeout *to_reconnect, *to_idle, *to_response; + time_t last_reconnect; + unsigned int reconnect_attempts; + unsigned int idle_timeout_msecs; + + char *literal_temp_path; + int literal_fd; + buffer_t *literal_buf; + uoff_t literal_size, literal_bytes_left; + + enum imap_urlauth_state state; + + /* userid => target struct */ + struct imap_urlauth_target *targets_head, *targets_tail; + + bool reading_literal:1; +}; + +#define IMAP_URLAUTH_RECONNECT_MIN_SECS 2 +#define IMAP_URLAUTH_RECONNECT_MAX_ATTEMPTS 3 + +#define IMAP_URLAUTH_RESPONSE_TIMEOUT_MSECS 2*60*1000 + +#define IMAP_URLAUTH_HANDSHAKE "VERSION\timap-urlauth\t2\t0\n" + +#define IMAP_URLAUTH_MAX_INLINE_LITERAL_SIZE (1024*32) + +static void imap_urlauth_connection_disconnect + (struct imap_urlauth_connection *conn, const char *reason); +static void imap_urlauth_connection_abort + (struct imap_urlauth_connection *conn, const char *reason); +static void imap_urlauth_connection_reconnect + (struct imap_urlauth_connection *conn); +static void imap_urlauth_connection_idle_disconnect + (struct imap_urlauth_connection *conn); +static void imap_urlauth_connection_timeout_abort + (struct imap_urlauth_connection *conn); +static void imap_urlauth_connection_fail + (struct imap_urlauth_connection *conn); + +struct imap_urlauth_connection * +imap_urlauth_connection_init(const char *path, const char *service, + struct mail_user *user, const char *session_id, + unsigned int idle_timeout_msecs) +{ + struct imap_urlauth_connection *conn; + + conn = i_new(struct imap_urlauth_connection, 1); + conn->refcount = 1; + conn->service = i_strdup(service); + conn->path = i_strdup(path); + if (session_id != NULL) + conn->session_id = i_strdup(session_id); + conn->user = user; + conn->fd = -1; + conn->literal_fd = -1; + conn->idle_timeout_msecs = idle_timeout_msecs; + return conn; +} + +void imap_urlauth_connection_deinit(struct imap_urlauth_connection **_conn) +{ + struct imap_urlauth_connection *conn = *_conn; + + *_conn = NULL; + + imap_urlauth_connection_abort(conn, NULL); + + i_free(conn->path); + i_free(conn->service); + if (conn->session_id != NULL) + i_free(conn->session_id); + + i_assert(conn->to_idle == NULL); + i_assert(conn->to_reconnect == NULL); + i_assert(conn->to_response == NULL); + + i_free(conn); +} + +static void +imap_urlauth_stop_response_timeout(struct imap_urlauth_connection *conn) +{ + timeout_remove(&conn->to_response); +} + +static void +imap_urlauth_start_response_timeout(struct imap_urlauth_connection *conn) +{ + imap_urlauth_stop_response_timeout(conn); + conn->to_response = timeout_add(IMAP_URLAUTH_RESPONSE_TIMEOUT_MSECS, + imap_urlauth_connection_timeout_abort, conn); +} + +static struct imap_urlauth_target * +imap_urlauth_connection_get_target(struct imap_urlauth_connection *conn, + const char *target_user) +{ + struct imap_urlauth_target *target = conn->targets_head; + + while (target != NULL) { + if (strcmp(target->userid, target_user) == 0) + return target; + target = target->next; + } + + target = i_new(struct imap_urlauth_target, 1); + target->userid = i_strdup(target_user); + DLLIST2_APPEND(&conn->targets_head, &conn->targets_tail, target); + return target; +} + +static void +imap_urlauth_target_free(struct imap_urlauth_connection *conn, + struct imap_urlauth_target *target) +{ + DLLIST2_REMOVE(&conn->targets_head, &conn->targets_tail, target); + i_free(target->userid); + i_free(target); +} + +static void +imap_urlauth_connection_select_target(struct imap_urlauth_connection *conn) +{ + struct imap_urlauth_target *target = conn->targets_head; + const char *cmd; + + if (target == NULL || conn->state != IMAP_URLAUTH_STATE_AUTHENTICATED) + return; + + e_debug(conn->user->event, + "imap-urlauth: Selecting target user `%s'", target->userid); + + conn->state = IMAP_URLAUTH_STATE_SELECTING_TARGET; + cmd = t_strdup_printf("USER\t%s\n", str_tabescape(target->userid)); + if (o_stream_send_str(conn->output, cmd) < 0) { + i_warning("Error sending USER request to imap-urlauth server: %m"); + imap_urlauth_connection_fail(conn); + } + + imap_urlauth_start_response_timeout(conn); +} + +static void +imap_urlauth_connection_send_request(struct imap_urlauth_connection *conn) +{ + struct imap_urlauth_request *urlreq; + string_t *cmd; + + if (conn->targets_head == NULL || + (conn->targets_head->requests_head == NULL && + conn->targets_head->next == NULL && + conn->state == IMAP_URLAUTH_STATE_READY)) { + e_debug(conn->user->event, + "imap-urlauth: No more requests pending; scheduling disconnect"); + timeout_remove(&conn->to_idle); + if (conn->idle_timeout_msecs > 0) { + conn->to_idle = timeout_add(conn->idle_timeout_msecs, + imap_urlauth_connection_idle_disconnect, conn); + } + return; + } + + if (conn->state == IMAP_URLAUTH_STATE_AUTHENTICATED) { + imap_urlauth_connection_select_target(conn); + return; + } + + if (conn->state != IMAP_URLAUTH_STATE_READY) + return; + + urlreq = conn->targets_head->requests_head; + if (urlreq == NULL) { + if (conn->targets_head->next == NULL) + return; + + conn->state = IMAP_URLAUTH_STATE_UNSELECTING_TARGET; + imap_urlauth_target_free(conn, conn->targets_head); + + if (o_stream_send_str(conn->output, "END\n") < 0) { + i_warning("Error sending END request to imap-urlauth server: %m"); + imap_urlauth_connection_fail(conn); + } + imap_urlauth_start_response_timeout(conn); + return; + } + + e_debug(conn->user->event, + "imap-urlauth: Fetching URL `%s'", urlreq->url); + + cmd = t_str_new(128); + str_append(cmd, "URL\t"); + str_append_tabescaped(cmd, urlreq->url); + if ((urlreq->flags & IMAP_URLAUTH_FETCH_FLAG_BODYPARTSTRUCTURE) != 0) + str_append(cmd, "\tbpstruct"); + if ((urlreq->flags & IMAP_URLAUTH_FETCH_FLAG_BINARY) != 0) + str_append(cmd, "\tbinary"); + else if ((urlreq->flags & IMAP_URLAUTH_FETCH_FLAG_BODY) != 0) + str_append(cmd, "\tbody"); + str_append_c(cmd, '\n'); + + conn->state = IMAP_URLAUTH_STATE_REQUEST_PENDING; + if (o_stream_send(conn->output, str_data(cmd), str_len(cmd)) < 0) { + i_warning("Error sending URL request to imap-urlauth server: %m"); + imap_urlauth_connection_fail(conn); + } + + imap_urlauth_start_response_timeout(conn); +} + +struct imap_urlauth_request * +imap_urlauth_request_new(struct imap_urlauth_connection *conn, + const char *target_user, const char *url, + enum imap_urlauth_fetch_flags flags, + imap_urlauth_request_callback_t *callback, + void *context) +{ + struct imap_urlauth_request *urlreq; + struct imap_urlauth_target *target; + + target = imap_urlauth_connection_get_target(conn, target_user); + + urlreq = i_new(struct imap_urlauth_request, 1); + urlreq->url = i_strdup(url); + urlreq->flags = flags; + urlreq->target = target; + urlreq->callback = callback; + urlreq->context = context; + + DLLIST2_APPEND(&target->requests_head, &target->requests_tail, urlreq); + + timeout_remove(&conn->to_idle); + + e_debug(conn->user->event, + "imap-urlauth: Added request for URL `%s' from user `%s'", + url, target_user); + + imap_urlauth_connection_send_request(conn); + return urlreq; +} + +static void imap_urlauth_request_free(struct imap_urlauth_request *urlreq) +{ + struct imap_urlauth_target *target = urlreq->target; + + DLLIST2_REMOVE(&target->requests_head, &target->requests_tail, urlreq); + i_free(urlreq->url); + i_free(urlreq->bodypartstruct); + i_free(urlreq); +} + +static void imap_urlauth_request_drop(struct imap_urlauth_connection *conn, + struct imap_urlauth_request *urlreq) +{ + if ((conn->state == IMAP_URLAUTH_STATE_REQUEST_PENDING || + conn->state == IMAP_URLAUTH_STATE_REQUEST_WAIT) && + conn->targets_head != NULL && + conn->targets_head->requests_head == urlreq) { + /* cannot just drop pending request without breaking + protocol state */ + return; + } + imap_urlauth_request_free(urlreq); + +} + +void imap_urlauth_request_abort(struct imap_urlauth_connection *conn, + struct imap_urlauth_request *urlreq) +{ + imap_urlauth_request_callback_t *callback; + + callback = urlreq->callback; + urlreq->callback = NULL; + if (callback != NULL) { + T_BEGIN { + callback(NULL, urlreq->context); + } T_END; + } + + imap_urlauth_request_drop(conn, urlreq); +} + +static void +imap_urlauth_request_fail(struct imap_urlauth_connection *conn, + struct imap_urlauth_request *urlreq, + const char *error) +{ + struct imap_urlauth_fetch_reply reply; + imap_urlauth_request_callback_t *callback; + int ret = 1; + + callback = urlreq->callback; + urlreq->callback = NULL; + if (callback != NULL) { + i_zero(&reply); + reply.url = urlreq->url; + reply.flags = urlreq->flags; + reply.succeeded = FALSE; + reply.error = error; + + T_BEGIN { + ret = callback(&reply, urlreq->context); + } T_END; + } + + void *urlreq_context = urlreq->context; + imap_urlauth_request_drop(conn, urlreq); + + if (ret < 0) { + /* Drop any related requests upon error */ + imap_urlauth_request_abort_by_context(conn, urlreq_context); + } + + if (ret != 0) + imap_urlauth_connection_continue(conn); +} + +static void +imap_urlauth_target_abort(struct imap_urlauth_connection *conn, + struct imap_urlauth_target *target) +{ + struct imap_urlauth_request *urlreq, *next; + + urlreq = target->requests_head; + while (urlreq != NULL) { + next = urlreq->next; + imap_urlauth_request_abort(conn, urlreq); + urlreq = next; + } + + imap_urlauth_target_free(conn, target); +} + +static void +imap_urlauth_target_fail(struct imap_urlauth_connection *conn, + struct imap_urlauth_target *target, const char *error) +{ + struct imap_urlauth_request *urlreq, *next; + + urlreq = target->requests_head; + while (urlreq != NULL) { + next = urlreq->next; + imap_urlauth_request_fail(conn, urlreq, error); + urlreq = next; + } + + imap_urlauth_target_free(conn, target); +} + +static void +imap_urlauth_target_abort_by_context(struct imap_urlauth_connection *conn, + struct imap_urlauth_target *target, + void *context) +{ + struct imap_urlauth_request *urlreq, *next; + + /* abort all matching requests */ + urlreq = target->requests_head; + while (urlreq != NULL) { + next = urlreq->next; + if (urlreq->context == context) + imap_urlauth_request_abort(conn, urlreq); + urlreq = next; + } + + if (target->requests_head == NULL) + imap_urlauth_target_free(conn, target); +} + +static void +imap_urlauth_connection_abort(struct imap_urlauth_connection *conn, + const char *reason) +{ + struct imap_urlauth_target *target, *next; + + if (reason == NULL) + reason = "Aborting due to error"; + imap_urlauth_connection_disconnect(conn, reason); + + /* abort all requests */ + target = conn->targets_head; + while (target != NULL) { + next = target->next; + imap_urlauth_target_abort(conn, target); + target = next; + } +} + +void imap_urlauth_request_abort_by_context(struct imap_urlauth_connection *conn, + void *context) +{ + struct imap_urlauth_target *target, *next; + + /* abort all matching requests */ + target = conn->targets_head; + while (target != NULL) { + next = target->next; + imap_urlauth_target_abort_by_context(conn, target, context); + target = next; + } +} + +static void imap_urlauth_connection_fail(struct imap_urlauth_connection *conn) +{ + if (conn->reconnect_attempts > IMAP_URLAUTH_RECONNECT_MAX_ATTEMPTS) { + imap_urlauth_connection_abort(conn, + "Connection failed and connection attempts exhausted"); + } else { + imap_urlauth_connection_reconnect(conn); + } +} + +static int +imap_urlauth_connection_create_temp_fd(struct imap_urlauth_connection *conn, + const char **path_r) +{ + string_t *path; + int fd; + + path = t_str_new(128); + mail_user_set_get_temp_prefix(path, conn->user->set); + fd = safe_mkstemp(path, 0600, (uid_t)-1, (gid_t)-1); + if (fd == -1) { + i_error("safe_mkstemp(%s) failed: %m", str_c(path)); + return -1; + } + + /* we just want the fd, unlink it */ + if (i_unlink(str_c(path)) < 0) { + /* shouldn't happen.. */ + i_close_fd(&fd); + return -1; + } + + *path_r = str_c(path); + return fd; +} + +static int +imap_urlauth_connection_read_literal_init(struct imap_urlauth_connection *conn, + uoff_t size) +{ + const char *path; + + i_assert(conn->literal_fd == -1 && conn->literal_buf == NULL); + + if (size <= IMAP_URLAUTH_MAX_INLINE_LITERAL_SIZE) { + /* read the literal directly */ + if (size > 0) { + conn->literal_buf = + buffer_create_dynamic(default_pool, size); + } + } else { + /* read it into a file */ + conn->literal_fd = + imap_urlauth_connection_create_temp_fd(conn, &path); + if (conn->literal_fd == -1) + return -1; + conn->literal_temp_path = i_strdup(path); + } + + conn->literal_size = size; + conn->literal_bytes_left = size; + conn->reading_literal = TRUE; + return 1; +} + +void imap_urlauth_connection_continue(struct imap_urlauth_connection *conn) +{ + i_assert(conn->targets_head != NULL); + i_assert(conn->targets_head->requests_head != NULL); + + if (conn->state != IMAP_URLAUTH_STATE_REQUEST_WAIT) + return; + + conn->state = IMAP_URLAUTH_STATE_READY; + imap_urlauth_request_free(conn->targets_head->requests_head); + + imap_urlauth_connection_send_request(conn); +} + +static int +imap_urlauth_connection_read_literal_data(struct imap_urlauth_connection *conn) +{ + const unsigned char *data; + size_t size; + + /* read data */ + data = i_stream_get_data(conn->input, &size); + if (size > conn->literal_bytes_left) + size = conn->literal_bytes_left; + + /* write to buffer or file */ + if (size > 0) { + if (conn->literal_fd >= 0) { + if (write_full(conn->literal_fd, data, size) < 0) { + i_error("imap-urlauth: write(%s) failed: %m", + conn->literal_temp_path); + return -1; + } + } else { + i_assert(conn->literal_buf != NULL); + buffer_append(conn->literal_buf, data, size); + } + i_stream_skip(conn->input, size); + conn->literal_bytes_left -= size; + } + + /* exit if not finished */ + if (conn->literal_bytes_left > 0) + return 0; + + /* read LF guard */ + data = i_stream_get_data(conn->input, &size); + if (size < 1) + return 0; + + /* check LF guard */ + if (data[0] != '\n') { + i_error("imap-urlauth: no LF at end of literal (found 0x%x)", + data[0]); + return -1; + } + i_stream_skip(conn->input, 1); + return 1; +} + +static void literal_stream_destroy(buffer_t *buffer) +{ + buffer_free(&buffer); +} + +static int +imap_urlauth_fetch_reply_set_literal_stream(struct imap_urlauth_connection *conn, + struct imap_urlauth_fetch_reply *reply) +{ + const unsigned char *data; + size_t size; + uoff_t fd_size; + + if (conn->literal_fd != -1) { + reply->input = i_stream_create_fd_autoclose(&conn->literal_fd, + SIZE_MAX); + if (i_stream_get_size(reply->input, TRUE, &fd_size) < 1 || + fd_size != conn->literal_size) { + i_stream_unref(&reply->input); + i_error("imap-urlauth: Failed to obtain proper size from literal stream"); + imap_urlauth_connection_abort(conn, + "Failed during literal transfer"); + return -1; + } + } else { + data = buffer_get_data(conn->literal_buf, &size); + i_assert(size == conn->literal_size); + reply->input = i_stream_create_from_data(data, size); + i_stream_add_destroy_callback(reply->input, + literal_stream_destroy, + conn->literal_buf); + } + reply->size = conn->literal_size; + return 0; +} + +static int +imap_urlauth_connection_read_literal(struct imap_urlauth_connection *conn) +{ + struct imap_urlauth_request *urlreq = conn->targets_head->requests_head; + struct imap_urlauth_fetch_reply reply; + imap_urlauth_request_callback_t *callback; + int ret; + + i_assert(conn->reading_literal); + i_assert(urlreq != NULL); + + if (conn->literal_size > 0) { + ret = imap_urlauth_connection_read_literal_data(conn); + if (ret <= 0) + return ret; + } + i_assert(conn->literal_bytes_left == 0); + + /* reply */ + i_zero(&reply); + reply.url = urlreq->url; + reply.flags = urlreq->flags; + reply.bodypartstruct = urlreq->bodypartstruct; + reply.binary_has_nuls = urlreq->binary_has_nuls ? 1 : 0; + + if (conn->literal_size > 0) { + if (imap_urlauth_fetch_reply_set_literal_stream(conn, &reply) < 0) + return -1; + } + reply.succeeded = TRUE; + + ret = 1; + callback = urlreq->callback; + urlreq->callback = NULL; + if (callback != NULL) T_BEGIN { + ret = callback(&reply, urlreq->context); + } T_END; + + if (reply.input != NULL) + i_stream_unref(&reply.input); + + if (ret < 0) { + /* Drop any related requests upon error */ + imap_urlauth_request_abort_by_context(conn, urlreq->context); + } + + conn->state = IMAP_URLAUTH_STATE_REQUEST_WAIT; + if (ret != 0) + imap_urlauth_connection_continue(conn); + + /* finished */ + i_free_and_null(conn->literal_temp_path); + conn->literal_fd = -1; + conn->literal_buf = NULL; + conn->reading_literal = FALSE; + return 1; +} + +static int imap_urlauth_input_pending(struct imap_urlauth_connection *conn) +{ + struct imap_urlauth_request *urlreq; + const char *response, *const *args, *bpstruct = NULL; + uoff_t literal_size; + + i_assert(conn->targets_head != NULL); + i_assert(conn->targets_head->requests_head != NULL); + urlreq = conn->targets_head->requests_head; + + if (conn->reading_literal) { + /* Read pending literal; may callback */ + return imap_urlauth_connection_read_literal(conn); + } + + /* "OK"[<metadata-items>]"\t"<literal-size>"\n" or + "NO"["\terror="<error>]"\n" */ + if ((response = i_stream_next_line(conn->input)) == NULL) + return 0; + imap_urlauth_stop_response_timeout(conn); + + args = t_strsplit_tabescaped(response); + if (args[0] == NULL) { + i_error("imap-urlauth: Empty URL response: %s", + str_sanitize(response, 80)); + return -1; + } + + if (strcmp(args[0], "OK") != 0 || args[1] == NULL) { + if (strcmp(args[0], "NO") == 0) { + const char *param = args[1], *error = NULL; + + if (param != NULL && + strncasecmp(param, "error=", 6) == 0 && + param[6] != '\0') { + error = param+6; + } + conn->state = IMAP_URLAUTH_STATE_REQUEST_WAIT; + imap_urlauth_request_fail(conn, + conn->targets_head->requests_head, error); + return 1; + } + i_error("imap-urlauth: Unexpected URL response: %s", + str_sanitize(response, 80)); + return -1; + } + + /* read metadata */ + args++; + for (; args[1] != NULL; args++) { + const char *param = args[0]; + + if (strcasecmp(param, "hasnuls") == 0) { + urlreq->binary_has_nuls = TRUE; + } else if (strncasecmp(param, "bpstruct=", 9) == 0 && + param[9] != '\0') { + bpstruct = param+9; + } + } + + /* read literal size */ + if (str_to_uoff(args[0], &literal_size) < 0) { + i_error("imap-urlauth: " + "Overflowing unsigned integer value for literal size: %s", + args[1]); + return -1; + } + + /* read literal */ + if (imap_urlauth_connection_read_literal_init(conn, literal_size) < 0) + return -1; + + urlreq->bodypartstruct = i_strdup(bpstruct); + return imap_urlauth_connection_read_literal(conn); +} + +static int imap_urlauth_input_next(struct imap_urlauth_connection *conn) +{ + const char *response; + int ret; + + switch (conn->state) { + case IMAP_URLAUTH_STATE_AUTHENTICATING: + case IMAP_URLAUTH_STATE_UNSELECTING_TARGET: + if ((response = i_stream_next_line(conn->input)) == NULL) + return 0; + imap_urlauth_stop_response_timeout(conn); + + if (strcasecmp(response, "OK") != 0) { + if (conn->state == IMAP_URLAUTH_STATE_AUTHENTICATING) + i_error("imap-urlauth: Failed to authenticate to service: " + "Got unexpected response: %s", str_sanitize(response, 80)); + else + i_error("imap-urlauth: Failed to unselect target user: " + "Got unexpected response: %s", str_sanitize(response, 80)); + imap_urlauth_connection_abort(conn, NULL); + return -1; + } + + if (conn->state == IMAP_URLAUTH_STATE_AUTHENTICATING) { + e_debug(conn->user->event, + "imap-urlauth: Successfully authenticated to service"); + } else { + e_debug(conn->user->event, + "imap-urlauth: Successfully unselected target user"); + } + + conn->state = IMAP_URLAUTH_STATE_AUTHENTICATED; + imap_urlauth_connection_select_target(conn); + return 0; + case IMAP_URLAUTH_STATE_SELECTING_TARGET: + if ((response = i_stream_next_line(conn->input)) == NULL) + return 0; + imap_urlauth_stop_response_timeout(conn); + + i_assert(conn->targets_head != NULL); + + if (strcasecmp(response, "NO") == 0) { + e_debug(conn->user->event, + "imap-urlauth: Failed to select target user %s", + conn->targets_head->userid); + imap_urlauth_target_fail(conn, conn->targets_head, NULL); + + conn->state = IMAP_URLAUTH_STATE_AUTHENTICATED; + imap_urlauth_connection_select_target(conn); + return 0; + } + if (strcasecmp(response, "OK") != 0) { + i_error("imap-urlauth: Failed to select target user %s: " + "Got unexpected response: %s", conn->targets_head->userid, + str_sanitize(response, 80)); + imap_urlauth_connection_abort(conn, NULL); + return -1; + } + + e_debug(conn->user->event, + "imap-urlauth: Successfully selected target user %s", + conn->targets_head->userid); + conn->state = IMAP_URLAUTH_STATE_READY; + imap_urlauth_connection_send_request(conn); + return 0; + case IMAP_URLAUTH_STATE_AUTHENTICATED: + case IMAP_URLAUTH_STATE_READY: + case IMAP_URLAUTH_STATE_REQUEST_WAIT: + if ((response = i_stream_next_line(conn->input)) == NULL) + return 0; + + i_error("imap-urlauth: Received input while no requests were pending: %s", + str_sanitize(response, 80)); + imap_urlauth_connection_abort(conn, NULL); + return -1; + case IMAP_URLAUTH_STATE_REQUEST_PENDING: + if ((ret = imap_urlauth_input_pending(conn)) < 0) + imap_urlauth_connection_fail(conn); + return ret; + case IMAP_URLAUTH_STATE_DISCONNECTED: + break; + } + i_unreached(); +} + +static void imap_urlauth_input(struct imap_urlauth_connection *conn) +{ + i_assert(conn->state != IMAP_URLAUTH_STATE_DISCONNECTED); + + if (conn->input->closed) { + /* disconnected */ + i_error("imap-urlauth: Service disconnected unexpectedly"); + imap_urlauth_connection_fail(conn); + return; + } + + switch (i_stream_read(conn->input)) { + case -1: + /* disconnected */ + i_error("imap-urlauth: Service disconnected unexpectedly"); + imap_urlauth_connection_fail(conn); + return; + case -2: + /* input buffer full */ + i_error("imap-urlauth: Service sent too large input"); + imap_urlauth_connection_abort(conn, NULL); + return; + } + + while (!conn->input->closed) { + if (imap_urlauth_input_next(conn) <= 0) + break; + } +} + +static int +imap_urlauth_connection_do_connect(struct imap_urlauth_connection *conn) +{ + string_t *str; + int fd; + + if (conn->state != IMAP_URLAUTH_STATE_DISCONNECTED) { + imap_urlauth_connection_send_request(conn); + return 1; + } + + if (conn->user->auth_token == NULL) { + i_error("imap-urlauth: cannot authenticate because no auth token " + "is available for this session (running standalone?)."); + imap_urlauth_connection_abort(conn, NULL); + return -1; + } + + e_debug(conn->user->event, "imap-urlauth: Connecting to service at %s", conn->path); + + i_assert(conn->fd == -1); + fd = net_connect_unix(conn->path); + if (fd == -1) { + i_error("imap-urlauth: net_connect_unix(%s) failed: %m", + conn->path); + imap_urlauth_connection_abort(conn, NULL); + return -1; + } + + timeout_remove(&conn->to_reconnect); + + conn->fd = fd; + conn->input = i_stream_create_fd(fd, SIZE_MAX); + conn->output = o_stream_create_fd(fd, SIZE_MAX); + conn->io = io_add(fd, IO_READ, imap_urlauth_input, conn); + conn->state = IMAP_URLAUTH_STATE_AUTHENTICATING; + + str = t_str_new(128); + str_printfa(str, IMAP_URLAUTH_HANDSHAKE"AUTH\t%s\t%s\t", + conn->service, my_pid); + str_append_tabescaped(str, conn->user->username); + str_append_c(str, '\t'); + if (conn->session_id != NULL) + str_append_tabescaped(str, conn->session_id); + str_append_c(str, '\t'); + str_append_tabescaped(str, conn->user->auth_token); + str_append_c(str, '\n'); + if (o_stream_send(conn->output, str_data(str), str_len(str)) < 0) { + i_warning("Error sending handshake to imap-urlauth server: %m"); + imap_urlauth_connection_abort(conn, NULL); + return -1; + } + + imap_urlauth_start_response_timeout(conn); + return 0; +} + +int imap_urlauth_connection_connect(struct imap_urlauth_connection *conn) +{ + conn->reconnect_attempts = 0; + + if (conn->to_reconnect == NULL) + return imap_urlauth_connection_do_connect(conn); + return 0; +} + +static void imap_urlauth_connection_disconnect +(struct imap_urlauth_connection *conn, const char *reason) +{ + conn->state = IMAP_URLAUTH_STATE_DISCONNECTED; + + if (conn->fd != -1) { + if (reason == NULL) + e_debug(conn->user->event, "imap-urlauth: Disconnecting from service"); + else + e_debug(conn->user->event, "imap-urlauth: Disconnected: %s", reason); + + io_remove(&conn->io); + i_stream_destroy(&conn->input); + o_stream_destroy(&conn->output); + net_disconnect(conn->fd); + conn->fd = -1; + } + conn->reading_literal = FALSE; + + if (conn->literal_fd != -1) { + if (close(conn->literal_fd) < 0) + i_error("imap-urlauth: close(%s) failed: %m", conn->literal_temp_path); + + i_free_and_null(conn->literal_temp_path); + conn->literal_fd = -1; + } + + buffer_free(&conn->literal_buf); + timeout_remove(&conn->to_reconnect); + timeout_remove(&conn->to_idle); + imap_urlauth_stop_response_timeout(conn); +} + +static void +imap_urlauth_connection_do_reconnect(struct imap_urlauth_connection *conn) +{ + if (conn->reconnect_attempts >= IMAP_URLAUTH_RECONNECT_MAX_ATTEMPTS) { + imap_urlauth_connection_abort(conn, + "Connection failed and connection attempts exhausted"); + return; + } + + if (ioloop_time - conn->last_reconnect < IMAP_URLAUTH_RECONNECT_MIN_SECS) { + e_debug(conn->user->event, "imap-urlauth: Scheduling reconnect"); + timeout_remove(&conn->to_reconnect); + conn->to_reconnect = + timeout_add(IMAP_URLAUTH_RECONNECT_MIN_SECS*1000, + imap_urlauth_connection_do_reconnect, conn); + } else { + conn->reconnect_attempts++; + conn->last_reconnect = ioloop_time; + (void)imap_urlauth_connection_do_connect(conn); + } +} + +static void +imap_urlauth_connection_reconnect(struct imap_urlauth_connection *conn) +{ + imap_urlauth_connection_disconnect(conn, NULL); + + /* don't reconnect if there are no requests */ + if (conn->targets_head == NULL) + return; + + imap_urlauth_connection_do_reconnect(conn); +} + +static void +imap_urlauth_connection_idle_disconnect(struct imap_urlauth_connection *conn) +{ + imap_urlauth_connection_disconnect(conn, "Idle timeout"); +} + +static void +imap_urlauth_connection_timeout_abort(struct imap_urlauth_connection *conn) +{ + imap_urlauth_connection_abort(conn, "Service is not responding"); +} + +bool imap_urlauth_connection_is_connected(struct imap_urlauth_connection *conn) +{ + return conn->fd != -1 && conn->state != IMAP_URLAUTH_STATE_DISCONNECTED; +} diff --git a/src/lib-imap-urlauth/imap-urlauth-connection.h b/src/lib-imap-urlauth/imap-urlauth-connection.h new file mode 100644 index 0000000..b60186a --- /dev/null +++ b/src/lib-imap-urlauth/imap-urlauth-connection.h @@ -0,0 +1,42 @@ +#ifndef IMAP_URLAUTH_CONNECTION_H +#define IMAP_URLAUTH_CONNECTION_H + +struct imap_urlauth_request; +struct imap_urlauth_fetch_reply; + +typedef int +imap_urlauth_request_callback_t(struct imap_urlauth_fetch_reply *reply, + void *context); + +/* If reconnect_callback is specified, it's called when connection is lost. + If the callback returns FALSE, reconnection isn't attempted. */ +struct imap_urlauth_connection * +imap_urlauth_connection_init(const char *path, const char *service, + struct mail_user *user, const char *session_id, + unsigned int idle_timeout_msecs); +void imap_urlauth_connection_deinit(struct imap_urlauth_connection **conn); + +/* Connect to imap-urlauth (even if failed for previous requests). */ +int imap_urlauth_connection_connect(struct imap_urlauth_connection *conn); + +/* Continue after request callback returned 0 */ +void imap_urlauth_connection_continue(struct imap_urlauth_connection *conn); + +/* Create a new URL fetch request */ +struct imap_urlauth_request * +imap_urlauth_request_new(struct imap_urlauth_connection *conn, + const char *target_user, const char *url, + enum imap_urlauth_fetch_flags flags, + imap_urlauth_request_callback_t *callback, + void *context); +/* Abort request */ +void imap_urlauth_request_abort(struct imap_urlauth_connection *conn, + struct imap_urlauth_request *urlreq); +/* Abort all requests with matching context value */ +void imap_urlauth_request_abort_by_context(struct imap_urlauth_connection *conn, + void *context); + +/* Returns TRUE if currently connected imap-urlauth service. */ +bool imap_urlauth_connection_is_connected(struct imap_urlauth_connection *conn); + +#endif diff --git a/src/lib-imap-urlauth/imap-urlauth-fetch.c b/src/lib-imap-urlauth/imap-urlauth-fetch.c new file mode 100644 index 0000000..d5746f1 --- /dev/null +++ b/src/lib-imap-urlauth/imap-urlauth-fetch.c @@ -0,0 +1,530 @@ +/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "llist.h" +#include "array.h" +#include "net.h" +#include "istream.h" +#include "mail-user.h" +#include "mail-error.h" +#include "mail-storage.h" +#include "imap-url.h" +#include "imap-msgpart-url.h" +#include "imap-urlauth-private.h" +#include "imap-urlauth-fetch.h" +#include "imap-urlauth-connection.h" + +struct imap_urlauth_fetch_url { + struct imap_urlauth_fetch_url *prev, *next; + + char *url; + enum imap_urlauth_fetch_flags flags; +}; + +struct imap_urlauth_fetch { + unsigned int refcount; + struct imap_urlauth_context *uctx; + + imap_urlauth_fetch_callback_t *callback; + void *context; + + /* local urls */ + struct imap_urlauth_fetch_url *local_urls_head, *local_urls_tail; + struct imap_msgpart_url *local_url; + + unsigned int pending_requests; + + struct { + char *url; + enum imap_urlauth_fetch_flags flags; + + struct istream *input; + uoff_t size; + + char *bodypartstruct; + char *error; + + bool succeeded:1; + bool binary_has_nuls:1; + } pending_reply; + + bool failed:1; + bool waiting_local:1; + bool waiting_service:1; +}; + +static void imap_urlauth_fetch_abort_local(struct imap_urlauth_fetch *ufetch) +{ + struct imap_urlauth_fetch_url *url, *url_next; + + if (ufetch->local_url != NULL) { + ufetch->pending_requests--; + imap_msgpart_url_free(&ufetch->local_url); + } + + i_free_and_null(ufetch->pending_reply.url); + i_free_and_null(ufetch->pending_reply.bodypartstruct); + i_free_and_null(ufetch->pending_reply.error); + i_stream_unref(&ufetch->pending_reply.input); + + url = ufetch->local_urls_head; + for (; url != NULL; url = url_next) { + url_next = url->next; + i_free(url->url); + i_free(url); + ufetch->pending_requests--; + } + ufetch->local_urls_head = ufetch->local_urls_tail = NULL; +} + +static void imap_urlauth_fetch_abort(struct imap_urlauth_fetch *ufetch) +{ + if (ufetch->pending_requests > 0) + imap_urlauth_request_abort_by_context(ufetch->uctx->conn, ufetch); + + imap_urlauth_fetch_abort_local(ufetch); + + i_assert(ufetch->pending_requests == 0); +} + +static void imap_urlauth_fetch_fail(struct imap_urlauth_fetch *ufetch) +{ + imap_urlauth_fetch_abort(ufetch); + ufetch->failed = TRUE; +} + +struct imap_urlauth_fetch * +imap_urlauth_fetch_init(struct imap_urlauth_context *uctx, + imap_urlauth_fetch_callback_t *callback, void *context) +{ + struct imap_urlauth_fetch *ufetch; + + ufetch = i_new(struct imap_urlauth_fetch, 1); + ufetch->refcount = 1; + ufetch->uctx = uctx; + ufetch->callback = callback; + ufetch->context = context; + return ufetch; +} + +static void imap_urlauth_fetch_ref(struct imap_urlauth_fetch *ufetch) +{ + i_assert(ufetch->refcount > 0); + ufetch->refcount++; +} + +static void imap_urlauth_fetch_unref(struct imap_urlauth_fetch **_ufetch) +{ + struct imap_urlauth_fetch *ufetch = *_ufetch; + + i_assert(ufetch->refcount > 0); + + *_ufetch = NULL; + if (--ufetch->refcount > 0) + return; + + ufetch->refcount++; + imap_urlauth_fetch_abort(ufetch); + ufetch->refcount--; + i_assert(ufetch->refcount == 0); + + /* dont leave the connection in limbo; make sure continue is called */ + if (ufetch->waiting_service) + imap_urlauth_connection_continue(ufetch->uctx->conn); + i_free(ufetch); +} + +void imap_urlauth_fetch_deinit(struct imap_urlauth_fetch **_ufetch) +{ + imap_urlauth_fetch_unref(_ufetch); +} + +static void +imap_urlauth_fetch_error(struct imap_urlauth_fetch *ufetch, const char *url, + enum imap_urlauth_fetch_flags url_flags, + const char *error) +{ + struct imap_urlauth_fetch_reply reply; + int ret; + + ufetch->pending_requests--; + + i_zero(&reply); + reply.url = url; + reply.flags = url_flags; + reply.succeeded = FALSE; + reply.error = error; + + T_BEGIN { + ret = ufetch->callback(&reply, ufetch->pending_requests == 0, + ufetch->context); + } T_END; + + if (ret == 0) { + ufetch->waiting_local = TRUE; + ufetch->pending_requests++; + } else if (ret < 0) { + imap_urlauth_fetch_fail(ufetch); + } +} + +static void +imap_urlauth_fetch_local(struct imap_urlauth_fetch *ufetch, const char *url, + enum imap_urlauth_fetch_flags url_flags, + struct imap_url *imap_url) +{ + struct imap_urlauth_fetch_reply reply; + struct imap_msgpart_open_result mpresult; + const char *error, *errormsg = NULL, *bpstruct = NULL; + bool debug = ufetch->uctx->user->mail_debug, success; + enum mail_error error_code; + struct imap_msgpart_url *mpurl = NULL; + int ret; + + success = TRUE; + + if (debug) + i_debug("Fetching local URLAUTH %s", url); + + if (url_flags == 0) + url_flags = IMAP_URLAUTH_FETCH_FLAG_BODY; + + /* fetch URL */ + if (imap_url == NULL) { + ret = imap_urlauth_fetch(ufetch->uctx, url, + &mpurl, &error_code, &error); + } else { + ret = imap_urlauth_fetch_parsed(ufetch->uctx, imap_url, + &mpurl, &error_code, &error); + } + if (ret <= 0) { + if (ret == 0) { + errormsg = t_strdup_printf("Failed to fetch URLAUTH \"%s\": %s", + url, error); + if (debug) + i_debug("%s", errormsg); + } + success = FALSE; + } + + /* fetch metadata */ + if (success && (url_flags & IMAP_URLAUTH_FETCH_FLAG_BINARY) != 0) + imap_msgpart_url_set_decode_to_binary(mpurl); + if (success && + (url_flags & IMAP_URLAUTH_FETCH_FLAG_BODYPARTSTRUCTURE) != 0) { + ret = imap_msgpart_url_get_bodypartstructure(mpurl, &bpstruct, &error); + if (ret <= 0) { + if (ret == 0) { + errormsg = t_strdup_printf + ("Failed to read URLAUTH \"%s\": %s", url, error); + if (debug) + i_debug("%s", errormsg); + } + success = FALSE; + } + } + + /* if requested, read the message part the URL points to */ + i_zero(&mpresult); + if (success && ((url_flags & IMAP_URLAUTH_FETCH_FLAG_BODY) != 0 || + (url_flags & IMAP_URLAUTH_FETCH_FLAG_BINARY) != 0)) { + ret = imap_msgpart_url_read_part(mpurl, &mpresult, &error); + if (ret <= 0) { + if (ret == 0) { + errormsg = t_strdup_printf + ("Failed to read URLAUTH \"%s\": %s", url, error); + if (debug) + i_debug("%s", errormsg); + } + success = FALSE; + } + } + + if (debug && success) { + if (bpstruct != NULL) + i_debug("Fetched URLAUTH yielded BODYPARTSTRUCTURE (%s)", bpstruct); + if (mpresult.size == 0 || mpresult.input == NULL) + i_debug("Fetched URLAUTH yielded empty result"); + else { + i_debug("Fetched URLAUTH yielded %"PRIuUOFF_T" bytes " + "of %smessage data", mpresult.size, + (mpresult.binary_decoded_input_has_nuls ? "binary " : "")); + } + } + + ufetch->pending_requests--; + + if (!success && ret < 0) { + if (mpurl != NULL) + imap_msgpart_url_free(&mpurl); + (void)ufetch->callback(NULL, TRUE, ufetch->context); + imap_urlauth_fetch_fail(ufetch); + return; + } + + i_zero(&reply); + reply.url = url; + reply.flags = url_flags; + reply.error = errormsg; + reply.succeeded = success; + + reply.bodypartstruct = bpstruct; + reply.binary_has_nuls = mpresult.binary_decoded_input_has_nuls; + reply.size = mpresult.size; + reply.input = mpresult.input; + + ret = ufetch->callback(&reply, ufetch->pending_requests == 0, + ufetch->context); + if (ret == 0) { + ufetch->local_url = mpurl; + ufetch->waiting_local = TRUE; + ufetch->pending_requests++; + } else { + + if (mpurl != NULL) + imap_msgpart_url_free(&mpurl); + if (ret < 0) + imap_urlauth_fetch_fail(ufetch); + } +} + +static int +imap_urlauth_fetch_request_callback(struct imap_urlauth_fetch_reply *reply, + void *context) +{ + struct imap_urlauth_fetch *ufetch = + (struct imap_urlauth_fetch *)context; + int ret = 1; + + if (ufetch->waiting_local && reply != NULL) { + i_assert(ufetch->pending_reply.url == NULL); + ufetch->pending_reply.url = i_strdup(reply->url); + ufetch->pending_reply.flags = reply->flags; + ufetch->pending_reply.bodypartstruct = + i_strdup(reply->bodypartstruct); + ufetch->pending_reply.error = i_strdup(reply->error); + if (reply->input != NULL) { + ufetch->pending_reply.input = reply->input; + i_stream_ref(ufetch->pending_reply.input); + } + ufetch->pending_reply.size = reply->size; + ufetch->pending_reply.succeeded = reply->succeeded; + ufetch->pending_reply.binary_has_nuls = reply->binary_has_nuls; + ufetch->waiting_service = TRUE; + return 0; + } + + ufetch->waiting_local = FALSE; + ufetch->pending_requests--; + + imap_urlauth_fetch_ref(ufetch); + + if (!ufetch->failed) { + bool last = ufetch->pending_requests == 0 || reply == NULL; + ret = ufetch->callback(reply, last, ufetch->context); + } + + /* report failure only once */ + if (ret < 0 || reply == NULL) { + if (!ufetch->failed) + imap_urlauth_fetch_abort_local(ufetch); + ufetch->failed = TRUE; + } else if (ret == 0) { + ufetch->waiting_service = TRUE; + ufetch->pending_requests++; + } + + imap_urlauth_fetch_unref(&ufetch); + return ret; +} + +int imap_urlauth_fetch_url(struct imap_urlauth_fetch *ufetch, const char *url, + enum imap_urlauth_fetch_flags url_flags) +{ + struct imap_urlauth_context *uctx = ufetch->uctx; + enum imap_url_parse_flags url_parse_flags = + IMAP_URL_PARSE_ALLOW_URLAUTH; + struct mail_user *mail_user = uctx->user; + struct imap_url *imap_url; + const char *error, *errormsg; + + /* parse the url */ + if (imap_url_parse(url, NULL, url_parse_flags, &imap_url, &error) < 0) { + errormsg = t_strdup_printf( + "Failed to fetch URLAUTH \"%s\": %s", url, error); + e_debug(mail_user->event, "%s", errormsg); + ufetch->pending_requests++; + imap_urlauth_fetch_ref(ufetch); + imap_urlauth_fetch_error(ufetch, url, url_flags, errormsg); + imap_urlauth_fetch_unref(&ufetch); + return 1; + } + + return imap_urlauth_fetch_url_parsed(ufetch, url, imap_url, url_flags); +} + +int imap_urlauth_fetch_url_parsed(struct imap_urlauth_fetch *ufetch, + const char *url, struct imap_url *imap_url, + enum imap_urlauth_fetch_flags url_flags) +{ + struct imap_urlauth_context *uctx = ufetch->uctx; + struct mail_user *mail_user = uctx->user; + const char *error, *errormsg; + int ret = 0; + + ufetch->failed = FALSE; + ufetch->pending_requests++; + + imap_urlauth_fetch_ref(ufetch); + + /* if access user and target user match, handle fetch request locally */ + if (imap_url->userid != NULL && + strcmp(mail_user->username, imap_url->userid) == 0) { + + if (ufetch->waiting_local) { + struct imap_urlauth_fetch_url *url_local; + + url_local = i_new(struct imap_urlauth_fetch_url, 1); + url_local->url = i_strdup(url); + url_local->flags = url_flags; + + DLLIST2_APPEND(&ufetch->local_urls_head, + &ufetch->local_urls_tail, url_local); + } else T_BEGIN { + imap_urlauth_fetch_local(ufetch, url, + url_flags, imap_url); + } T_END; + imap_url = NULL; + /* don't try to fetch remote URLs that are already known to fail access */ + } else if (!imap_urlauth_check(uctx, imap_url, TRUE, &error)) { + errormsg = t_strdup_printf( + "Failed to fetch URLAUTH \"%s\": %s", url, error); + e_debug(mail_user->event, "%s", errormsg); + imap_urlauth_fetch_error(ufetch, url, url_flags, errormsg); + imap_url = NULL; + } + + /* create request for url */ + if (imap_url != NULL && imap_url->userid != NULL) { + i_assert(uctx->conn != NULL); + (void)imap_urlauth_request_new(uctx->conn, imap_url->userid, + url, url_flags, + imap_urlauth_fetch_request_callback, ufetch); + i_assert(uctx->conn != NULL); + if (imap_urlauth_connection_connect(uctx->conn) < 0) + ret = -1; + } + if (ret >= 0) + ret = (ufetch->pending_requests > 0 ? 0 : 1); + + imap_urlauth_fetch_unref(&ufetch); + return ret; +} + +static bool imap_urlauth_fetch_do_continue(struct imap_urlauth_fetch *ufetch) +{ + struct imap_urlauth_fetch_url *url, *url_next; + int ret; + + if (ufetch->failed) + return FALSE; + + if (!ufetch->waiting_local && !ufetch->waiting_service) { + /* not currently waiting for anything */ + return ufetch->pending_requests > 0; + } + + /* we finished a request */ + ufetch->pending_requests--; + + if (!ufetch->waiting_local) { + /* not waiting for local request handling */ + ufetch->waiting_service = FALSE; + imap_urlauth_connection_continue(ufetch->uctx->conn); + return ufetch->pending_requests > 0; + } + + /* finished local request */ + if (ufetch->local_url != NULL) { + imap_msgpart_url_free(&ufetch->local_url); + } + ufetch->waiting_local = FALSE; + + /* handle pending remote reply */ + if (ufetch->pending_reply.url != NULL) { + struct imap_urlauth_fetch_reply reply; + + ufetch->pending_requests--; + + i_zero(&reply); + reply.url = ufetch->pending_reply.url; + reply.flags = ufetch->pending_reply.flags; + reply.bodypartstruct = ufetch->pending_reply.bodypartstruct; + reply.error = ufetch->pending_reply.error; + reply.input = ufetch->pending_reply.input; + reply.size = ufetch->pending_reply.size; + reply.succeeded = ufetch->pending_reply.succeeded; + reply.binary_has_nuls = ufetch->pending_reply.binary_has_nuls; + + ret = ufetch->callback(&reply, ufetch->pending_requests == 0, + ufetch->context); + + if (ufetch->pending_reply.url != NULL) + i_free(ufetch->pending_reply.url); + if (ufetch->pending_reply.input != NULL) + i_stream_unref(&ufetch->pending_reply.input); + if (ufetch->pending_reply.bodypartstruct != NULL) + i_free(ufetch->pending_reply.bodypartstruct); + if (ufetch->pending_reply.error != NULL) + i_free(ufetch->pending_reply.error); + + if (ret < 0) { + imap_urlauth_fetch_fail(ufetch); + return FALSE; + } + + if (ret == 0) { + ufetch->waiting_service = TRUE; + ufetch->pending_requests++; + return TRUE; + } + + ufetch->waiting_service = FALSE; + imap_urlauth_connection_continue(ufetch->uctx->conn); + } + + /* handle pending local urls */ + url = ufetch->local_urls_head; + while (url != NULL) { + url_next = url->next; + T_BEGIN { + imap_urlauth_fetch_local(ufetch, url->url, + url->flags, NULL); + } T_END; + DLLIST2_REMOVE(&ufetch->local_urls_head, + &ufetch->local_urls_tail, url); + i_free(url->url); + i_free(url); + if (ufetch->waiting_local) + return TRUE; + url = url_next; + } + + return ufetch->pending_requests > 0; +} + +bool imap_urlauth_fetch_continue(struct imap_urlauth_fetch *ufetch) +{ + bool pending; + + imap_urlauth_fetch_ref(ufetch); + pending = imap_urlauth_fetch_do_continue(ufetch); + imap_urlauth_fetch_unref(&ufetch); + + return pending; +} + +bool imap_urlauth_fetch_is_pending(struct imap_urlauth_fetch *ufetch) +{ + return ufetch->pending_requests > 0; +} diff --git a/src/lib-imap-urlauth/imap-urlauth-fetch.h b/src/lib-imap-urlauth/imap-urlauth-fetch.h new file mode 100644 index 0000000..628a95e --- /dev/null +++ b/src/lib-imap-urlauth/imap-urlauth-fetch.h @@ -0,0 +1,56 @@ +#ifndef IMAP_URLAUTH_FETCH_H +#define IMAP_URLAUTH_FETCH_H + +struct imap_url; +struct imap_urlauth_context; +struct imap_urlauth_fetch; + +enum imap_urlauth_fetch_flags { + /* Indicates that this is an extended request */ + IMAP_URLAUTH_FETCH_FLAG_EXTENDED = 0x01, + /* Fetch body part unmodified */ + IMAP_URLAUTH_FETCH_FLAG_BODY = 0x02, + /* Fetch body part as binary, i.e. without content encoding */ + IMAP_URLAUTH_FETCH_FLAG_BINARY = 0x04, + /* Fetch IMAP bodypartstructure */ + IMAP_URLAUTH_FETCH_FLAG_BODYPARTSTRUCTURE = 0x08, +}; + +struct imap_urlauth_fetch_reply { + const char *url; + enum imap_urlauth_fetch_flags flags; + + struct istream *input; + uoff_t size; + + const char *bodypartstruct; + const char *error; + + bool succeeded:1; + bool binary_has_nuls:1; +}; + +/* Callback to handle fetch reply. Returns 1 if handled completely and ready + for next reply, 0 if not all data was processed, and -1 for error. If a + callback returns 0, imap_urlauth_fetch_continue() must be called once + new replies may be processed. If this is the last request to yield a reply, + argument last is TRUE. */ +typedef int +imap_urlauth_fetch_callback_t(struct imap_urlauth_fetch_reply *reply, + bool last, void *context); + +struct imap_urlauth_fetch * +imap_urlauth_fetch_init(struct imap_urlauth_context *uctx, + imap_urlauth_fetch_callback_t *callback, void *context); +void imap_urlauth_fetch_deinit(struct imap_urlauth_fetch **ufetch); + +int imap_urlauth_fetch_url(struct imap_urlauth_fetch *ufetch, const char *url, + enum imap_urlauth_fetch_flags url_flags); +int imap_urlauth_fetch_url_parsed(struct imap_urlauth_fetch *ufetch, + const char *url, struct imap_url *imap_url, + enum imap_urlauth_fetch_flags url_flags); + +bool imap_urlauth_fetch_continue(struct imap_urlauth_fetch *ufetch); +bool imap_urlauth_fetch_is_pending(struct imap_urlauth_fetch *ufetch); + +#endif diff --git a/src/lib-imap-urlauth/imap-urlauth-private.h b/src/lib-imap-urlauth/imap-urlauth-private.h new file mode 100644 index 0000000..3b3622c --- /dev/null +++ b/src/lib-imap-urlauth/imap-urlauth-private.h @@ -0,0 +1,20 @@ +#ifndef IMAP_URLAUTH_PRIVATE_H +#define IMAP_URLAUTH_PRIVATE_H + +#include "imap-urlauth.h" + +struct imap_urlauth_context { + struct mail_user *user; + struct imap_urlauth_connection *conn; + + char *url_host; + in_port_t url_port; + + char *access_user; + char *access_service; + const char **access_applications; + + bool access_anonymous:1; +}; + +#endif diff --git a/src/lib-imap-urlauth/imap-urlauth.c b/src/lib-imap-urlauth/imap-urlauth.c new file mode 100644 index 0000000..a9c2673 --- /dev/null +++ b/src/lib-imap-urlauth/imap-urlauth.c @@ -0,0 +1,486 @@ +/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "hostpid.h" +#include "var-expand.h" +#include "hmac.h" +#include "sha1.h" +#include "randgen.h" +#include "safe-memset.h" +#include "mail-storage.h" +#include "mail-storage-service.h" +#include "mail-namespace.h" +#include "mail-user.h" +#include "imap-url.h" +#include "imap-msgpart-url.h" +#include "imap-urlauth-backend.h" +#include "imap-urlauth-fetch.h" +#include "imap-urlauth-connection.h" + +#include "imap-urlauth-private.h" + +#include <time.h> + +#define IMAP_URLAUTH_MECH_INTERNAL_VERSION 0x01 + +#define IMAP_URLAUTH_NORMAL_TIMEOUT_MSECS 5*1000 +#define IMAP_URLAUTH_SPECIAL_TIMEOUT_MSECS 3*60*1000 + +#define URL_HOST_ALLOW_ANY "*" + +struct imap_urlauth_context * +imap_urlauth_init(struct mail_user *user, + const struct imap_urlauth_config *config) +{ + struct imap_urlauth_context *uctx; + unsigned int timeout; + + i_assert(*config->url_host != '\0'); + + uctx = i_new(struct imap_urlauth_context, 1); + uctx->user = user; + uctx->url_host = i_strdup(config->url_host); + uctx->url_port = config->url_port; + + if (config->access_anonymous) + uctx->access_user = i_strdup("anonymous"); + else + uctx->access_user = i_strdup(config->access_user); + uctx->access_service = i_strdup(config->access_service); + uctx->access_anonymous = config->access_anonymous; + if (config->access_applications != NULL && + *config->access_applications != NULL) { + uctx->access_applications = + p_strarray_dup(default_pool, + config->access_applications); + timeout = IMAP_URLAUTH_SPECIAL_TIMEOUT_MSECS; + } else { + timeout = IMAP_URLAUTH_NORMAL_TIMEOUT_MSECS; + } + + if (config->socket_path != NULL) { + uctx->conn = imap_urlauth_connection_init(config->socket_path, + config->access_service, user, config->session_id, timeout); + } + return uctx; +} + +void imap_urlauth_deinit(struct imap_urlauth_context **_uctx) +{ + struct imap_urlauth_context *uctx = *_uctx; + + *_uctx = NULL; + + if (uctx->conn != NULL) + imap_urlauth_connection_deinit(&uctx->conn); + i_free(uctx->url_host); + i_free(uctx->access_user); + i_free(uctx->access_service); + i_free(uctx->access_applications); + i_free(uctx); +} + +static const unsigned char * +imap_urlauth_internal_generate(const char *rumpurl, + const unsigned char mailbox_key[IMAP_URLAUTH_KEY_LEN], + size_t *token_len_r) +{ + struct hmac_context hmac; + unsigned char *token; + + token = t_new(unsigned char, SHA1_RESULTLEN + 1); + token[0] = IMAP_URLAUTH_MECH_INTERNAL_VERSION; + + hmac_init(&hmac, mailbox_key, IMAP_URLAUTH_KEY_LEN, &hash_method_sha1); + hmac_update(&hmac, rumpurl, strlen(rumpurl)); + hmac_final(&hmac, token+1); + + *token_len_r = SHA1_RESULTLEN + 1; + return token; +} + +static bool +imap_urlauth_internal_verify(const char *rumpurl, + const unsigned char mailbox_key[IMAP_URLAUTH_KEY_LEN], + const unsigned char *token, size_t token_len) +{ + const unsigned char *valtoken; + size_t valtoken_len; + + if (rumpurl == NULL || token == NULL) + return FALSE; + + valtoken = imap_urlauth_internal_generate(rumpurl, mailbox_key, + &valtoken_len); + /* Note: the token length has timing leak here in any case */ + if (token_len != valtoken_len) + return FALSE; + + return mem_equals_timing_safe(token, valtoken, valtoken_len); +} + +static bool +access_applications_have_access(struct imap_urlauth_context *uctx, + struct imap_url *url, const char *const *access_applications) +{ + const char *const *application; + + if (access_applications == NULL) + return FALSE; + + application = access_applications; + for (; *application != NULL; application++) { + const char *app = *application; + bool have_userid = FALSE; + size_t len = strlen(app); + + if (app[len-1] == '+') + have_userid = TRUE; + + if (strncasecmp(url->uauth_access_application, app, len-1) == 0) { + if (!have_userid) { + /* this access application must have no userid */ + return url->uauth_access_user == NULL; + } + + /* this access application must have a userid */ + return (!uctx->access_anonymous && url->uauth_access_user != NULL); + } + } + return FALSE; +} + +static bool +imap_urlauth_check_access(struct imap_urlauth_context *uctx, + struct imap_url *url, bool ignore_unknown, + const char **error_r) +{ + const char *userid; + + if (url->uauth_access_application == NULL) { + *error_r = "URL is missing URLAUTH"; + return FALSE; + } + + if (strcmp(uctx->access_service, "imap") == 0) { + /* these access types are only allowed if URL is accessed through imap */ + if (strcasecmp(url->uauth_access_application, "user") == 0) { + /* user+<access_user> */ + if (url->uauth_access_user == NULL) { + *error_r = "URLAUTH `user' access is missing userid"; + return FALSE; + } + if (!uctx->access_anonymous || + strcasecmp(url->uauth_access_user, uctx->access_user) == 0) + return TRUE; + } else if (strcasecmp(url->uauth_access_application, "authuser") == 0) { + /* authuser */ + if (!uctx->access_anonymous) + return TRUE; + } else if (strcasecmp(url->uauth_access_application, "anonymous") == 0) { + /* anonymous */ + return TRUE; + } else if (ignore_unknown || access_applications_have_access + (uctx, url, uctx->access_applications)) { + return TRUE; + } + } else if (strcmp(uctx->access_service, "submission") == 0) { + /* accessed directly through submission service */ + + if (strcasecmp(url->uauth_access_application, "submit") != 0) { + userid = url->uauth_access_user == NULL ? "" : + t_strdup_printf("+%s", url->uauth_access_user); + *error_r = t_strdup_printf( + "No '%s%s' access allowed for submission service", + url->uauth_access_application, userid); + return FALSE; + } else if (url->uauth_access_user == NULL) { + *error_r = "URLAUTH `submit' access is missing userid"; + return FALSE; + } else if (!uctx->access_anonymous && + strcasecmp(url->uauth_access_user, uctx->access_user) == 0) { + return TRUE; + } + } + + userid = url->uauth_access_user == NULL ? "" : + t_strdup_printf("+%s", url->uauth_access_user); + + if (uctx->access_anonymous) { + *error_r = t_strdup_printf( + "No '%s%s' access allowed for anonymous user", + url->uauth_access_application, userid); + } else { + *error_r = t_strdup_printf( + "No '%s%s' access allowed for user %s", + url->uauth_access_application, userid, uctx->access_user); + } + return FALSE; +} + +static bool +imap_urlauth_check_hostport(struct imap_urlauth_context *uctx, + struct imap_url *url, const char **client_error_r) +{ + /* validate host */ + /* FIXME: allow host ip/ip6 as well? */ + if (strcmp(uctx->url_host, URL_HOST_ALLOW_ANY) != 0 && + strcmp(url->host.name, uctx->url_host) != 0) { + *client_error_r = "Invalid URL: Inappropriate host name"; + return FALSE; + } + + /* validate port */ + if ((url->port == 0 && uctx->url_port != 143) || + (url->port != 0 && uctx->url_port != url->port)) { + *client_error_r = "Invalid URL: Inappropriate server port"; + return FALSE; + } + return TRUE; +} + +int imap_urlauth_generate(struct imap_urlauth_context *uctx, + const char *mechanism, const char *rumpurl, + const char **urlauth_r, const char **client_error_r) +{ + struct mail_user *user = uctx->user; + enum imap_url_parse_flags url_flags = + IMAP_URL_PARSE_ALLOW_URLAUTH; + struct imap_url *url; + struct imap_msgpart_url *mpurl = NULL; + struct mailbox *box; + const char *error; + enum mail_error error_code; + unsigned char mailbox_key[IMAP_URLAUTH_KEY_LEN]; + const unsigned char *token; + size_t token_len; + int ret; + + /* validate mechanism */ + if (strcasecmp(mechanism, "INTERNAL") != 0) { + *client_error_r = t_strdup_printf("Unsupported URLAUTH mechanism: %s", mechanism); + return 0; + } + + /* validate URL */ + if (imap_url_parse(rumpurl, NULL, url_flags, &url, &error) < 0) { + *client_error_r = t_strdup_printf("Invalid URL: %s", error); + return 0; + } + + if (url->mailbox == NULL || url->uid == 0 || url->search_program != NULL || + url->uauth_rumpurl == NULL || url->uauth_mechanism != NULL) { + *client_error_r = "Invalid URL: Must be an URLAUTH rump URL"; + return 0; + } + + /* validate expiry time */ + if (url->uauth_expire != (time_t)-1) { + time_t now = time(NULL); + + if (now > url->uauth_expire) { + *client_error_r = t_strdup_printf("URLAUTH has already expired"); + return 0; + } + } + + /* validate user */ + if (url->userid == NULL) { + *client_error_r = "Invalid URL: Missing user name"; + return 0; + } + if (user->anonymous || strcmp(url->userid, user->username) != 0) { + *client_error_r = t_strdup_printf( + "Not permitted to generate URLAUTH for user %s", + url->userid); + return 0; + } + + /* validate host:port */ + if (!imap_urlauth_check_hostport(uctx, url, client_error_r)) + return 0; + + /* validate mailbox */ + if ((ret = imap_msgpart_url_create(user, url, &mpurl, &error)) < 0 || + imap_msgpart_url_verify(mpurl, &error) <= 0) { + *client_error_r = t_strdup_printf("Invalid URL: %s", error); + if (mpurl != NULL) + imap_msgpart_url_free(&mpurl); + return ret; + } + box = imap_msgpart_url_get_mailbox(mpurl); + + /* obtain mailbox key */ + ret = imap_urlauth_backend_get_mailbox_key(box, TRUE, mailbox_key, + client_error_r, &error_code); + if (ret < 0) { + imap_msgpart_url_free(&mpurl); + return ret; + } + + token = imap_urlauth_internal_generate(rumpurl, mailbox_key, &token_len); + imap_msgpart_url_free(&mpurl); + + *urlauth_r = imap_url_add_urlauth(rumpurl, mechanism, token, token_len); + return 1; +} + +bool imap_urlauth_check(struct imap_urlauth_context *uctx, + struct imap_url *url, bool ignore_unknown_access, + const char **error_r) +{ + /* validate URL fields */ + if (url->mailbox == NULL || url->uid == 0 || + url->search_program != NULL || url->uauth_rumpurl == NULL || + url->uauth_mechanism == NULL) { + *error_r = "Invalid URL: Must be a full URLAUTH URL"; + return FALSE; + } + + /* check presence of userid */ + if (url->userid == NULL) { + *error_r = "Invalid URLAUTH: Missing user name"; + return FALSE; + } + + /* validate mechanism */ + if (strcasecmp(url->uauth_mechanism, "INTERNAL") != 0) { + *error_r = t_strdup_printf("Unsupported URLAUTH mechanism: %s", + url->uauth_mechanism); + return FALSE; + } + + /* validate expiry time */ + if (url->uauth_expire != (time_t)-1) { + time_t now = time(NULL); + + if (now > url->uauth_expire) { + *error_r = t_strdup_printf("URLAUTH has expired"); + return FALSE; + } + } + + /* validate access */ + if (!imap_urlauth_check_access(uctx, url, ignore_unknown_access, + error_r)) + return FALSE; + /* validate host:port */ + if (!imap_urlauth_check_hostport(uctx, url, error_r)) + return FALSE; + return TRUE; +} + +int imap_urlauth_fetch_parsed(struct imap_urlauth_context *uctx, + struct imap_url *url, + struct imap_msgpart_url **mpurl_r, + enum mail_error *error_code_r, + const char **error_r) +{ + struct mail_user *user = uctx->user; + struct imap_msgpart_url *mpurl; + struct mailbox *box; + const char *error; + unsigned char mailbox_key[IMAP_URLAUTH_KEY_LEN]; + int ret; + + *mpurl_r = NULL; + *error_r = NULL; + *error_code_r = MAIL_ERROR_NONE; + + /* check urlauth mechanism, access, userid and authority */ + if (!imap_urlauth_check(uctx, url, FALSE, error_r)) { + *error_code_r = MAIL_ERROR_PARAMS; + return 0; + } + + /* validate target user */ + if (user->anonymous || strcmp(url->userid, user->username) != 0) { + *error_r = t_strdup_printf("Not permitted to fetch URLAUTH for user %s", + url->userid); + *error_code_r = MAIL_ERROR_PARAMS; + return 0; + } + + /* validate mailbox */ + if ((ret = imap_msgpart_url_create(user, url, &mpurl, &error)) < 0) { + *error_r = t_strdup_printf("Invalid URLAUTH: %s", error); + *error_code_r = MAIL_ERROR_PARAMS; + return ret; + } + + if ((ret = imap_msgpart_url_open_mailbox(mpurl, &box, error_code_r, + &error)) < 0) { + *error_r = "Internal server error"; + imap_msgpart_url_free(&mpurl); + return -1; + } + + if (ret == 0) { + /* RFC says: `If the mailbox cannot be identified, an + authorization token is calculated on the rump URL, using + random "plausible" keys (selected by the server) as needed, + before returning a validation failure. This prevents timing + attacks aimed at identifying mailbox names.' */ + random_fill(mailbox_key, sizeof(mailbox_key)); + (void)imap_urlauth_internal_verify(url->uauth_rumpurl, + mailbox_key, url->uauth_token, url->uauth_token_size); + + *error_r = t_strdup_printf("Invalid URLAUTH: %s", error); + imap_msgpart_url_free(&mpurl); + return 0; + } + + /* obtain mailbox key */ + ret = imap_urlauth_backend_get_mailbox_key(box, FALSE, mailbox_key, + error_r, error_code_r); + if (ret < 0) { + imap_msgpart_url_free(&mpurl); + return -1; + } + + if (ret == 0 || + !imap_urlauth_internal_verify(url->uauth_rumpurl, mailbox_key, + url->uauth_token, + url->uauth_token_size)) { + *error_r = "URLAUTH verification failed"; + *error_code_r = MAIL_ERROR_PERM; + imap_msgpart_url_free(&mpurl); + ret = 0; + } else { + ret = 1; + } + + safe_memset(mailbox_key, 0, sizeof(mailbox_key)); + *mpurl_r = mpurl; + return ret; +} + +int imap_urlauth_fetch(struct imap_urlauth_context *uctx, + const char *urlauth, struct imap_msgpart_url **mpurl_r, + enum mail_error *error_code_r, const char **error_r) +{ + struct imap_url *url; + enum imap_url_parse_flags url_flags = IMAP_URL_PARSE_ALLOW_URLAUTH; + const char *error; + + /* validate URL */ + if (imap_url_parse(urlauth, NULL, url_flags, &url, &error) < 0) { + *error_r = t_strdup_printf("Invalid URLAUTH: %s", error); + *error_code_r = MAIL_ERROR_PARAMS; + return 0; + } + + return imap_urlauth_fetch_parsed(uctx, url, mpurl_r, + error_code_r, error_r); +} + +int imap_urlauth_reset_mailbox_key(struct imap_urlauth_context *uctx ATTR_UNUSED, + struct mailbox *box) +{ + return imap_urlauth_backend_reset_mailbox_key(box); +} + +int imap_urlauth_reset_all_keys(struct imap_urlauth_context *uctx) +{ + return imap_urlauth_backend_reset_all_keys(uctx->user); +} diff --git a/src/lib-imap-urlauth/imap-urlauth.h b/src/lib-imap-urlauth/imap-urlauth.h new file mode 100644 index 0000000..9c7c30f --- /dev/null +++ b/src/lib-imap-urlauth/imap-urlauth.h @@ -0,0 +1,55 @@ +#ifndef IMAP_URLAUTH_H +#define IMAP_URLAUTH_H + +#include "net.h" + +#define IMAP_URLAUTH_SOCKET_NAME "imap-urlauth" + +struct imap_url; +struct imap_msgpart_url; +struct imap_urlauth_context; + +struct imap_urlauth_config { + const char *url_host; + in_port_t url_port; + + const char *socket_path; + const char *session_id; + + /* the user who is requesting access to URLAUTHs */ + const char *access_user; + /* ... is using this service (i.e. imap or submission) */ + const char *access_service; + /* ... represents these applications */ + const char *const *access_applications; + /* ... is anonymous? */ + bool access_anonymous; +}; + +struct imap_urlauth_context * +imap_urlauth_init(struct mail_user *user, + const struct imap_urlauth_config *config); +void imap_urlauth_deinit(struct imap_urlauth_context **_uctx); + +int imap_urlauth_generate(struct imap_urlauth_context *uctx, + const char *mechanism, const char *rumpurl, + const char **urlauth_r, const char **client_error_r); + +bool imap_urlauth_check(struct imap_urlauth_context *uctx, + struct imap_url *url, bool ignore_unknown_access, + const char **error_r); + +int imap_urlauth_fetch_parsed(struct imap_urlauth_context *uctx, + struct imap_url *url, + struct imap_msgpart_url **mpurl_r, + enum mail_error *error_code_r, + const char **error_r); +int imap_urlauth_fetch(struct imap_urlauth_context *uctx, + const char *urlauth, struct imap_msgpart_url **mpurl_r, + enum mail_error *error_code_r, const char **error_r); + +int imap_urlauth_reset_mailbox_key(struct imap_urlauth_context *uctx, + struct mailbox *box); +int imap_urlauth_reset_all_keys(struct imap_urlauth_context *uctx); + +#endif |