aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore5
-rw-r--r--Changelog170
-rw-r--r--Makefile130
-rw-r--r--README93
-rw-r--r--benchmark/dovecot.conf29
-rwxr-xr-xbenchmark/run44
-rw-r--r--doc/benchmark.md4
-rw-r--r--doc/build.md73
-rw-r--r--doc/development.md77
-rw-r--r--doc/getting-started.md26
-rw-r--r--doc/interimap.1.md101
-rw-r--r--doc/multi-account.md25
-rw-r--r--doc/pullimap.1.md102
-rw-r--r--doc/template.html2
-rwxr-xr-xinterimap19
-rw-r--r--interimap.sample8
-rw-r--r--interimap.service3
-rw-r--r--interimap@.service3
-rw-r--r--lib/Net/IMAP/InterIMAP.pm289
-rwxr-xr-xpullimap15
-rw-r--r--pullimap.sample4
-rw-r--r--pullimap@.service3
-rw-r--r--tests/auth-login/t2
-rw-r--r--tests/auth-logindisabled/remote.conf4
-rw-r--r--tests/auth-logindisabled/t2
-rw-r--r--tests/auth-noplaintext/t2
-rw-r--r--tests/auth-sasl-plain-no-ir/t2
-rw-r--r--tests/auth-sasl-plain/t2
-rw-r--r--tests/certs/.gitignore4
-rwxr-xr-xtests/certs/generate57
-rw-r--r--tests/certs/openssl.cnf4
-rw-r--r--tests/compress/t2
-rw-r--r--tests/condstore/t6
-rw-r--r--tests/config/dovecot/dhparams.pem (renamed from tests/snippets/dovecot/dhparams.pem)0
-rw-r--r--tests/config/dovecot/imapd.conf (renamed from tests/snippets/dovecot/imapd.conf)7
-rw-r--r--tests/config/dovecot/interimap-required-capabilities.conf (renamed from tests/snippets/dovecot/interimap-required-capabilities.conf)0
-rw-r--r--tests/config/dovecot/lmtpd.conf (renamed from tests/snippets/dovecot/lmtpd.conf)4
-rw-r--r--tests/config/dovecot/ssl.conf6
-rw-r--r--tests/db-exclusive-lock/t6
-rw-r--r--tests/db-migration-0-1-foreign-key-violation/t2
-rw-r--r--tests/db-no-create--watch/t4
-rw-r--r--tests/db-upgrade-0-1-delim-mismatch/t2
-rw-r--r--tests/db-upgrade-0-1/t6
-rw-r--r--tests/delete/t16
-rw-r--r--tests/delimiter-change/t6
-rw-r--r--tests/ignore-mailbox/t10
-rw-r--r--tests/interimap.list (renamed from tests/list)6
-rw-r--r--tests/largeint/t38
-rw-r--r--tests/list-mailbox/t4
-rw-r--r--tests/list-reference/t8
-rw-r--r--tests/list-select-opts/t18
-rwxr-xr-xtests/preauth-plaintext/imapd4
-rw-r--r--tests/preauth-plaintext/t4
-rw-r--r--tests/pullimap.list2
-rw-r--r--tests/pullimap/t70
-rw-r--r--tests/rename-exists-db/t10
-rw-r--r--tests/rename-exists-local/t8
-rw-r--r--tests/rename-exists-remote/t8
-rw-r--r--tests/rename-inferiors/t6
-rw-r--r--tests/rename-simple/t4
-rw-r--r--tests/repair/t28
-rw-r--r--tests/resume/t26
-rwxr-xr-xtests/run59
-rwxr-xr-xtests/run-all3
-rw-r--r--tests/snippets/dovecot/dovecot.key5
-rw-r--r--tests/snippets/dovecot/dovecot.pem11
-rw-r--r--tests/snippets/dovecot/ssl.conf4
-rw-r--r--tests/split-set/t10
-rwxr-xr-xtests/starttls-injection/imapd24
-rw-r--r--tests/starttls-injection/remote.conf4
-rw-r--r--tests/starttls-injection/t2
-rw-r--r--tests/starttls-logindisabled/remote.conf4
-rw-r--r--tests/starttls-logindisabled/t2
-rw-r--r--tests/starttls/t11
-rw-r--r--tests/sync-live-multi/local.conf40
-rw-r--r--tests/sync-live-multi/t24
-rw-r--r--tests/sync-live/t12
-rw-r--r--tests/sync-mailbox-list/t22
l---------tests/tls-ciphers/interimap.remote1
l---------tests/tls-ciphers/remote.conf1
-rw-r--r--tests/tls-ciphers/t31
-rw-r--r--tests/tls-pin-fingerprint/t53
-rw-r--r--tests/tls-protocols/openssl.cnf14
-rw-r--r--[l---------]tests/tls-protocols/remote.conf5
-rw-r--r--tests/tls-protocols/t78
l---------tests/tls-rsa+ecdsa/interimap.remote1
-rw-r--r--tests/tls-rsa+ecdsa/remote.conf5
-rw-r--r--tests/tls-rsa+ecdsa/t59
-rw-r--r--tests/tls-sni/interimap.remote3
-rw-r--r--tests/tls-sni/remote.conf7
-rw-r--r--tests/tls-sni/t66
-rw-r--r--tests/tls-verify-peer/interimap.remote1
-rw-r--r--tests/tls-verify-peer/t105
-rw-r--r--tests/tls/t11
94 files changed, 1608 insertions, 695 deletions
diff --git a/.gitignore b/.gitignore
index 9dae7e6..e8f355e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,4 @@
*~
-/doc/*.1
-/doc/*.html
-!/doc/template.html
+*.bak
+/build/
/.pc/
diff --git a/Changelog b/Changelog
index 6ee44fc..25bba98 100644
--- a/Changelog
+++ b/Changelog
@@ -1,3 +1,163 @@
+interimap (0.5.8) upstream;
+
+ + Port tests and documentation to Dovecot 2.4. Running the test suite
+ now require Dovecot 2.3 or later.
+ - Makefile: Replace '$(dir $@)' with '$(@D)'.
+ - tests/*/t: Replace filetype=sh with filetype=bash in vim's hints.
+ - tests/certs/generate: Generate X.509 version 3 CA, and pass CA:TRUE
+ as basic constraint. This fixes the test suite with OpenSSL 3.2 with
+ defaults to X.509v3 and CA:FALSE.
+ - tests: Explicitly pass `-in /dev/stdin` to openssl(1).
+ - tests: Check that that pullimap locks its statefile.
+
+ -- Guilhem Moulin <guilhem@fripost.org> Sat, 26 Apr 2025 18:03:28 +0200
+
+interimap (0.5.7) upstream;
+
+ * interimap: create database with mode 0600 (but don't change mode of
+ existing databases). The file was previously created with mode 0644
+ minus umask restrictions, which for permissive umask(2)s is too open.
+ That being said its parent directory is created with restricted mode
+ 0700 so the impact is limited. pullimap, on the other hand, already
+ used mode 0600 for state file creation.
+ * Major Makefile refactoring: improve DESTDIR= handling, add new
+ targets 'all-nodoc', 'install-nodoc', and 'installcheck'. Also,
+ rename 'test' target to 'check'.
+ * `make install` now installs Net/IMAP/InterIMAP.pm to
+ /usr/local/lib/site_perl by default rather than /usr/local/share/perl5
+ (which is not in @INC as of perl 5.34.0-3 from Debian sid). The
+ installation directory is configurable with sitelib=.
+ * Refactor test harness so one can check the source with `tests/run
+ foo`; what's been built with `INTERIMAP_I=./lib INTERIMAP_PATH=./build
+ ./tests/run foo`, and what's installed with `INTERIMAP_I=""
+ INTERIMAP_PATH=/usr/bin tests/run foo`.
+ * Split interimap and pullimap test suites.
+ + Improve message for missing untagged UIDNEXT responses, which we
+ require but are omitted from some servers.
+ + tests/tls-protocols: downgrade OpenSSL security level to 0, which is
+ required to test TLS version <1.2 on systems with higher security
+ levels, see SSL_CTX_set_security_level(3ssl). Adapted from a patch
+ from <xnox> for Unbuntu.
+ + tests/tls-*: bump Dovecot's ssl_min_protocol to TLSv1.2, which is the
+ default as of dovecot 1:2.3.18+dfsg1-1 from Debian sid.
+ + `make clean` now cleans test certificates and key material.
+ + Add 'use lib "./lib";' to interimap and pullimap, so the programs can
+ be run directly from the source directory. The directory is
+ substituted with $(sitelib) at compile time (and the line is commented
+ out if $(sitelib) is found in @INC).
+ + doc/build.md: update documentation, and add a new section for how to
+ install without root privileges.
+ + Add Documentation=https://guilhem.org/interimap/... URIs to .service
+ files.
+ - Don't hardcode path to interimap/pullimap in .service files, and
+ instead use $(bindir) (expanded at compile time).
+ - tests/certs/generate: redirect known error output to the standard
+ output.
+ - tests/certs/generate: use custom openssl.cnf to avoid depending on
+ the system default.
+ - tests/pullimap: allow easy exclusion of --idle'ing tests.
+
+ -- Guilhem Moulin <guilhem@fripost.org> Sun, 27 Feb 2022 16:24:31 +0100
+
+interimap (0.5.6) upstream;
+
+ - Bump required Net::SSLeay version to 1.86_06 as it's when get_version()
+ was introduced.
+ - doc/template.html: remove type attribute from <style/> element.
+
+ -- Guilhem Moulin <guilhem@fripost.org> Fri, 01 Jan 2021 16:05:53 +0100
+
+interimap (0.5.5) upstream;
+
+ * libinterimap: remove default SSL_protocols value "!SSLv2 !SSLv3
+ !TLSv1 !TLSv1.1" and use the system default instead. As of Debian
+ Buster (OpenSSL 1.1.1) this does not make a difference, however using
+ the system default provides better compatibility with future libssl
+ versions.
+ * libinterimap: deprecate SSL_protocols, obsoleted by new settings
+ SSL_protocol_{min,max}. Using the libssl interface simplifies our
+ protocol black/whitelist greatly; this only allows simple min/max
+ bounds, but holes are arguably not very useful here.
+ * libinterimap: use default locations for trusted CA certificates when
+ neither CAfile nor CApath are set. In particular, OpenSSL's default
+ locations can be overridden by the SSL_CERT_FILE resp. SSL_CERT_DIR
+ environment variables, see SSL_CTX_load_verify_locations(3ssl).
+ * libinterimap: _start_ssl() now fails immediately with OpenSSL <1.1.0.
+ It could in principle still work with earlier versions if the new
+ settings SSL_protocol_{min,max} are not used, however it's cumbersome
+ to do individual checks for specific settings, let alone maintain
+ test coverage with multiple OpenSSL versions.
+ * libinterimap: new option SSL_ciphersuites to set the TLSv1.3
+ ciphersuites; also, clarify that SSL_cipherlist only applies to
+ TLSv1.2 and below, see SSL_CTX_set_cipher_list(3ssl).
+ + `make release`: also bump libinterimap version and pin it in 'use'
+ declarations.
+ + Make error messages more uniform and consistent.
+ - libinterimap: use Net::SSLeay::get_version() to get the protocol
+ version string.
+ - test suite: `mv tests/snippets tests/config`
+ - tests/tls-protocols: use custom OpenSSL configuration file with
+ MinProtocol=None so we can test TLSv1 as well, not just TLSv1.2 and
+ later.
+ - test suite: explicitly set ssl_min_protocol=TLSv1 in the Dovecot
+ configuration file (the default as of 2.3.11.3), hence running TLS
+ tests now require Dovecot 2.3 or later.
+ - documentation: simplify SSL options in the sample configuration files.
+ - README: suggest 'restrict,command="/usr/bin/doveadm exec imap"' as
+ authorized_keys(5) options.
+ - README: suggest ControlPath=$XDG_RUNTIME_DIR/ssh-imap-%C for the SSH
+ transport (note that variable expansion is only available in OpenSSH
+ 8.4 and later).
+ - test suite: ensure we haven't started speaking IMAP when the SSL/TLS
+ handshake is aborted (unless STARTTLS is used to upgrade to
+ connection).
+ - documentation: clarify that known TLS protocol versions depend on the
+ OpenSSL version used.
+
+ -- Guilhem Moulin <guilhem@fripost.org> Sat, 26 Dec 2020 23:11:10 +0100
+
+interimap (0.5.4) upstream;
+
+ * libinterimap: make SSL_verify also checks that the certificate
+ Subject Alternative Name (SAN) or Subject CommonName (CN) matches the
+ hostname or IP literal specified by the 'host' option. Previously it
+ was only checking the chain of trust. This bumps the minimum
+ Net::SSLeay version to 1.83 and OpenSSL version to 1.0.2 (when
+ SSL_verify is used).
+ * libinterimap: add support for the TLS SNI (Server Name Indication)
+ extension, controlled by the new 'SSL_hostname' option. The default
+ value of that option is the value of the 'host' option when it is
+ hostname, and the empty string (which disables SNI) when it is an IP
+ literal.
+ + libinterimap: show the matching pinned SPKI in --debug mode.
+ + test suite: always generate new certificates on `make test`.
+ + test suite: sign all test certificates with the same root CA.
+ + libinterimap: factor out hostname/IP parsing.
+ + document that enclosing 'host' value in square brackets forces its
+ interpretation as an IP literal (hence skips name resolution).
+ + Makefile: new 'release' target; also, change the tag format from
+ upstream/$VERSION to v$VERSION.
+ - documentation: replace example.org with example.net for consistency.
+ - rename 'debian' branch to 'debian/latest' for DEP-14 compliance.
+
+ -- Guilhem Moulin <guilhem@fripost.org> Fri, 11 Dec 2020 11:21:17 +0100
+
+interimap (0.5.3) upstream;
+
+ * libinterimap: SSL_fingerprint now supports a space-separated list of
+ digests to pin, and succeeds if, and only if, the peer certificate
+ SPKI matches one of the pinned digest values. Specifying multiple
+ digest values can key useful in key rollover scenarios and/or when
+ the server supports certificates of different types (for instance
+ RSA+ECDSA).
+ - libinterimap: 'null-stderr' is now ignored when the 'debug' flag is
+ set (the standard error is never sent to /dev/null).
+ - test suite: use a RSA certificate rather than ECDSA.
+ - test suite: new test with a server offering both RSA+ECDSA
+ certificates. This test requires dovecot-imapd 2.2.31 or later.
+
+ -- Guilhem Moulin <guilhem@fripost.org> Wed, 09 Dec 2020 15:32:01 +0100
+
interimap (0.5.2) upstream;
- Makefile: remove 'smart' extension from pandoc call to generate
@@ -33,7 +193,7 @@ interimap (0.5) upstream;
(regardless of the hierarchy delimiter in use).
Other changes:
- * interimap: the space-speparated list of names and/or patterns in
+ * interimap: the space-separated list of names and/or patterns in
'list-mailbox' can now contain C-style escape sequences (backslash
and hexadecimal escape).
* interimap: fail when two non-INBOX LIST replies return different
@@ -41,7 +201,7 @@ interimap (0.5) upstream;
happen if mailboxes from different namespaces are being listed. The
workaround here is to run a new interimap instance for each
namespace.
- * libinterimap: in tunnel mode, use a socketpair rather than two pipes
+ * libinterimap: in tunnel mode, use a socket pair rather than two pipes
for IPC between the interimap and the IMAP server. Also, use
SOCK_CLOEXEC to save an fcntl() call when setting the close-on-exec
flag on the socket.
@@ -104,7 +264,7 @@ interimap (0.5) upstream;
- libinterimap: use directories relative to $HOME for the XDG
environment variables default values. Previously getpwuid() was
called to determine the user's home directory, while the XDG
- specification explicitely mentions $HOME. Conveniently our docs
+ specification explicitly mentions $HOME. Conveniently our docs
always mentioned ~/, which on POSIX-compliant systems expands to the
value of the variable HOME. (Cf. Shell and Utilities volume of
POSIX.1-2017, sec. 2.6.1.)
@@ -118,7 +278,7 @@ interimap (0.5) upstream;
- libinterimap: push_flag_updates(): ignore UIDs for which no untagged
FETCH response was received.
- libinterimap: push_flag_updates(): don't ignores received updates (by
- another client) to a superset of the desigred flag list.
+ another client) to a superset of the desired flag list.
- libinterimap: avoid sending large UID EXPUNGE|FETCH|STORE commands as
they might exceed the server's max acceptable command size; these
commands are now split into multiple (sequential) commands when their
@@ -128,7 +288,7 @@ interimap (0.5) upstream;
This is a also a workaround for a bug in Dovecot 2.3.4:
https://dovecot.org/pipermail/dovecot/2019-November/117522.html
- interimap: for the reason explained above, limit number of messages
- to 128 per APPEND command (only on servers advertizing MULTIAPPEND,
+ to 128 per APPEND command (only on servers advertising MULTIAPPEND,
for other servers the number remains 1).
- interimap: gracefully ignore messages with a NIL RFC822 attribute.
- pullimap: treat messages with a NIL RFC822 attribute as empty.
diff --git a/Makefile b/Makefile
index 3d60dfb..5ecff73 100644
--- a/Makefile
+++ b/Makefile
@@ -1,61 +1,117 @@
-DESTDIR ?= /usr/local
-BUILD_DOCDIR ?= ./doc
+srcdir ?= .
+builddir ?= build
+prefix ?= /usr/local
+exec_prefix ?= $(prefix)
+bindir ?= $(exec_prefix)/bin
+libdir ?= $(exec_prefix)/lib
+sitelib ?= $(libdir)/site_perl
+datarootdir ?= $(prefix)/share
+mandir ?= $(datarootdir)/man
+man1dir ?= $(mandir)/man1
+systemd_userunitdir ?= $(libdir)/systemd/user
+
CSS ?= /usr/share/javascript/bootstrap4/css/bootstrap.css
-HTML_TEMPLATE ?= ./doc/template.html
+HTML_TEMPLATE ?= $(srcdir)/doc/template.html
-HTML_FILES = $(addprefix $(BUILD_DOCDIR)/,$(patsubst ./doc/%.md,%.html,$(wildcard ./doc/*.md)))
-MANUAL_FILES = $(addprefix $(BUILD_DOCDIR)/,$(patsubst ./doc/%.md,%,$(wildcard ./doc/*.[1-9].md)))
+PROGRAMS = $(addprefix $(builddir)/,interimap pullimap)
+HTML_FILES = $(patsubst $(srcdir)/doc/%.md,$(builddir)/doc/%.html,$(wildcard $(srcdir)/doc/*.md))
+MANUAL_FILES = $(patsubst $(srcdir)/doc/%.md,$(builddir)/doc/%,$(wildcard $(srcdir)/doc/*.[1-9].md))
+SERVICE_FILES = $(patsubst $(srcdir)/%.service,$(builddir)/%.service,$(wildcard $(srcdir)/*.service))
-all: manual
+all: all-nodoc manual
+all-nodoc: $(PROGRAMS) $(SERVICE_FILES)
+doc: manual html
manual: $(MANUAL_FILES)
html: $(HTML_FILES)
+$(PROGRAMS): $(builddir)/%: $(srcdir)/%
+ @mkdir -vp -- $(@D)
+ perl -Te "print \"\$$_\\0\" foreach @INC;" | grep -Fxzq -e "$(sitelib)" && prefix="#" || prefix=""; \
+ sed -r "0,/^(use\\s+\lib\\s+)([\"'])[^\"']*\\2\\s*;/ s||$$prefix\\1\"$(sitelib)\";|" <"$<" >"$@"
+ chmod --reference="$<" -- "$@"
+
# upper case the headers and remove the links
-$(MANUAL_FILES): $(BUILD_DOCDIR)/%: ./doc/%.md
- pandoc -f markdown -t json -- "$<" | ./pandoc2man.jq | pandoc -s -f json -t man -o "$@"
+$(MANUAL_FILES): $(builddir)/doc/%: $(srcdir)/doc/%.md
+ @mkdir -vp -- $(@D)
+ pandoc -f markdown -t json -- "$<" | $(srcdir)/pandoc2man.jq | pandoc -s -f json -t man -o "$@"
+
+$(SERVICE_FILES): $(builddir)/%.service: $(srcdir)/%.service
+ @mkdir -vp -- $(@D)
+ sed "s|@bindir@|$(bindir)|" <"$<" >"$@"
-test:
- @./tests/run-all
+testcerts:
+ $(srcdir)/tests/certs/generate
-## make html CSS="https://guilhem.org/static/css/bootstrap.min.css" BUILD_DOCDIR="$XDG_RUNTIME_DIR/Downloads"
-$(HTML_FILES): $(BUILD_DOCDIR)/%.html: ./doc/%.md $(HTML_TEMPLATE)
- mtime="$$(git --no-pager log -1 --pretty="format:%ct" -- "$<" 2>/dev/null)"; \
+check: check-interimap check-pullimap
+check-interimap: $(builddir)/interimap testcerts
+ INTERIMAP_I=$(srcdir)/lib INTERIMAP_PATH=$(builddir) $(srcdir)/tests/run-all interimap.list
+check-pullimap: $(builddir)/pullimap testcerts
+ INTERIMAP_I=$(srcdir)/lib INTERIMAP_PATH=$(builddir) $(srcdir)/tests/run-all pullimap.list
+
+installcheck: installcheck-interimap installcheck-pullimap
+installcheck-interimap: testcerts
+ INTERIMAP_I="" INTERIMAP_PATH=$(bindir) $(srcdir)/tests/run-all interimap.list
+installcheck-pullimap: testcerts
+ INTERIMAP_I="" INTERIMAP_PATH=$(bindir) $(srcdir)/tests/run-all pullimap.list
+
+release:
+ @if ! git -C $(srcdir) diff --quiet HEAD -- Changelog interimap pullimap lib/Net/IMAP/InterIMAP.pm; then \
+ echo "Dirty state, refusing to release!" >&2; \
+ exit 1; \
+ fi
+ VERS=$$(dpkg-parsechangelog -l $(srcdir)/Changelog -SVersion 2>/dev/null) && \
+ if git -C $(srcdir) rev-parse -q --verify "refs/tags/v$$VERS" >/dev/null; then echo "tag exists" 2>/dev/null; exit 1; fi && \
+ sed -ri "0,/^( -- .*) .*/ s//\\1 $(shell date -R)/" $(srcdir)/Changelog && \
+ sed -ri "0,/^(our\\s+\\\$$VERSION\\s*=\\s*)'[0-9.]+'\\s*;/ s//\\1'$$VERS';/" \
+ -- $(srcdir)/interimap $(srcdir)/pullimap && \
+ sed -ri "0,/^(package\\s+Net::IMAP::InterIMAP\\s+)v[0-9.]+\\s*;/ s//\\1v$$VERS;/" \
+ -- $(srcdir)/lib/Net/IMAP/InterIMAP.pm && \
+ sed -ri "0,/^(use\\s+Net::IMAP::InterIMAP\\s+)[0-9.]+(\\s|\\$$)/ s//\\1$$VERS\\2/" \
+ -- $(srcdir)/interimap $(srcdir)/pullimap && \
+ git -C $(srcdir) commit -m "Prepare new release v$$VERS." \
+ -- Changelog interimap pullimap lib/Net/IMAP/InterIMAP.pm && \
+ git -C $(srcdir) tag -sm "Release version $$VERS" "v$$VERS"
+
+$(HTML_FILES): $(builddir)/doc/%.html: $(srcdir)/doc/%.md $(HTML_TEMPLATE)
+ @mkdir -vp -- $(@D)
+ mtime="$$(git -C $(srcdir) --no-pager log -1 --pretty="format:%ct" -- "$<" 2>/dev/null)"; \
[ -n "$$mtime" ] || mtime="$$(date +%s -r "$<")"; \
- [ "$<" = "doc/index.md" ] && parent="" || parent="./index.html"; \
pandoc -sp -f markdown -t html+smart --css=$(CSS) --template=$(HTML_TEMPLATE) \
--variable=date:"$$(LC_TIME=C date +"Last modified on %a, %d %b %Y at %T %z" -d @"$$mtime")" \
--variable=keywords:"interimap" \
--variable=lang:"en" \
- --variable=parent:"$$parent" \
+ --variable=parent:"$(if $(filter $@,$(builddir)/doc/index.html),,./index.html)" \
--output="$@" -- "$<"
-doc: manual html
+INSTALL ?= install
+INSTALL_PROGRAM ?= $(INSTALL)
+INSTALL_DATA ?= $(INSTALL) -m0644
-prefix ?= $(DESTDIR)
-exec_prefix ?= $(prefix)
-bindir ?= $(exec_prefix)/bin
-libdir ?= $(exec_prefix)/lib
-datarootdir ?= $(prefix)/share
-mandir ?= $(datarootdir)/man
-man1dir ?= $(mandir)/man1
+install: install-nodoc
+ -$(INSTALL_DATA) -vDt $(DESTDIR)$(man1dir) $(builddir)/doc/interimap.1 $(builddir)/doc/pullimap.1
+ $(INSTALL_DATA) -vDt $(DESTDIR)$(datarootdir)/doc/pullimap $(srcdir)/pullimap.sample
+ $(INSTALL_DATA) -vDt $(DESTDIR)$(datarootdir)/doc/interimap $(srcdir)/interimap.sample \
+ $(srcdir)/doc/getting-started.md $(srcdir)/doc/multi-account.md $(srcdir)/README
-install: all
- install -m0755 -vDt $(bindir) ./interimap ./pullimap
- install -m0644 -vDT ./lib/Net/IMAP/InterIMAP.pm $(datarootdir)/perl5/Net/IMAP/InterIMAP.pm
- install -m0644 -vDt $(man1dir) $(BUILD_DOCDIR)/interimap.1 $(BUILD_DOCDIR)/pullimap.1
- install -m0644 -vDt $(datarootdir)/doc/pullimap ./pullimap.sample
- install -m0644 -vDt $(datarootdir)/doc/interimap ./interimap.sample ./doc/getting-started.md ./doc/multi-account.md README
- install -m0644 -vDt $(libdir)/systemd/user ./*.service
+install-nodoc: all-nodoc
+ $(INSTALL_PROGRAM) -vDt $(DESTDIR)$(bindir) $(builddir)/interimap $(builddir)/pullimap
+ $(INSTALL_DATA) -vDT $(srcdir)/lib/Net/IMAP/InterIMAP.pm $(DESTDIR)$(sitelib)/Net/IMAP/InterIMAP.pm
+ $(INSTALL_DATA) -vDt $(DESTDIR)$(systemd_userunitdir) $(SERVICE_FILES)
uninstall:
- rm -vf -- $(bindir)/interimap $(man1dir)/interimap.1 $(libdir)/systemd/user/interimap*.service
- rm -vf -- $(bindir)/pullimap $(man1dir)/pullimap.1 $(libdir)/systemd/user/pullimap*.service
- rm -vf -- $(datarootdir)/perl5/Net/IMAP/InterIMAP.pm
- rm -rvf -- $(datarootdir)/doc/interimap $(datarootdir)/doc/pullimap
- rm -vf -- $(BUILD_DOCDIR)/interimap.1 $(BUILD_DOCDIR)/pullimap.1
+ rm -vf -- $(DESTDIR)$(bindir)/interimap $(DESTDIR)$(man1dir)/interimap.1 $(DESTDIR)$(systemd_userunitdir)/interimap*.service
+ rm -vf -- $(DESTDIR)$(bindir)/pullimap $(DESTDIR)$(man1dir)/pullimap.1 $(DESTDIR)$(systemd_userunitdir)/pullimap*.service
+ rm -vf -- $(DESTDIR)$(sitelib)/Net/IMAP/InterIMAP.pm
+ rm -rvf -- $(DESTDIR)$(datarootdir)/doc/interimap $(DESTDIR)$(datarootdir)/doc/pullimap
clean:
- rm -vf -- $(MANUAL_FILES) $(HTML_FILES)
+ rm -vf -- $(PROGRAMS) $(MANUAL_FILES) $(HTML_FILES) $(SERVICE_FILES)
+ rm -vf -- $(srcdir)/tests/certs/*.key $(srcdir)/tests/certs/*.crt $(srcdir)/tests/certs/*.pem
+ -rmdir -vp --ignore-fail-on-non-empty -- $(builddir)/doc
-.PHONY: all manual html doc test install uninstall clean
+.PHONY: all all-nodoc manual html doc release testcerts \
+ check check-interimap check-pullimap \
+ install install-nodoc \
+ installcheck installcheck-interimap installcheck-pullimap \
+ uninstall clean
diff --git a/README b/README
index fbc4ed7..85562d4 100644
--- a/README
+++ b/README
@@ -1,54 +1,51 @@
InterIMAP is a fast bidirectional synchronization program for QRESYNC-capable
IMAP4rev1 servers. PullIMAP retrieves messages a remote IMAP mailbox and
-deliver them to an SMTP session. Visit https://guilhem.org/interimap
-for more information.
+deliver them to an SMTP session. Visit https://guilhem.org/interimap for more
+information.
-_______________________________________________________________________
+______________________________________________________________________________
-Compared to IMAP-to-Maildir synchronization solutions like OfflineIMAP,
-adding an IMAP server between the Maildir storage and the MUA saves
-loads of readdir(2) system calls and other File System quirks; moreover
-the abstraction layer offered by the IMAP server makes the MUA and
-synchronization program agnostic to the storage backend (Maildir, mbox,
-dbox,...) in use.
+Compared to IMAP-to-Maildir synchronization solutions like OfflineIMAP, adding
+an IMAP server between the Maildir storage and the MUA saves loads of
+readdir(2) system calls and other File System quirks; moreover the abstraction
+layer offered by the IMAP server makes the MUA and synchronization program
+agnostic to the storage backend (Maildir, mbox, dbox,...) in use.
IMAP synchronization of a mailbox is usually two-folds: 1/ detect and
-propagate changes (flag updates and message deletions) to existing
-messages, then 2/ copy the new messages. The naive way to perform the
-first step is to issue a FETCH command to list all messages in the
-mailbox along with their flags and UIDs, causing heavy network usage.
-Instead, InterIMAP takes advantage of the QRESYNC extension from
-[RFC7162] to perform stateful synchronization: querying changes since
-the last synchronization only gives a phenomenal performance boost and
-drastically reduces the network traffic.
+propagate changes (flag updates and message deletions) to existing messages,
+then 2/ copy the new messages. The naive way to perform the first step is to
+issue a FETCH command to list all messages in the mailbox along with their
+flags and UIDs, causing heavy network usage. Instead, InterIMAP takes
+advantage of the QRESYNC extension from [RFC7162] to perform stateful
+synchronization: querying changes since the last synchronization only gives a
+phenomenal performance boost and drastically reduces the network traffic.
-For convenience reasons servers must also support LIST-EXTENDED
-[RFC5258], LIST-STATUS [RFC5819] and UIDPLUS [RFC4315]. Other supported
-extensions are:
- * LITERAL+ [RFC2088] non-synchronizing literals (recommended),
- * MULTIAPPEND [RFC3502] (recommended),
- * COMPRESS=DEFLATE [RFC4978] (recommended),
- * SASL-IR [RFC4959] SASL Initial Client Response, and
+For convenience reasons servers must also support LIST-EXTENDED [RFC5258],
+LIST-STATUS [RFC5819] and UIDPLUS [RFC4315]. Other supported extensions are:
+
+ * LITERAL+ [RFC2088] non-synchronizing literals (recommended);
+ * MULTIAPPEND [RFC3502] (recommended);
+ * COMPRESS=DEFLATE [RFC4978] (recommended);
+ * SASL-IR [RFC4959] SASL Initial Client Response; and
* UNSELECT [RFC3691].
-_______________________________________________________________________
+______________________________________________________________________________
-IMAP traffic is mostly text (beside message bodies perhaps) hence
-compresses pretty well: enabling compression can save a great amount of
-network resources.
+IMAP traffic is mostly text (beside message bodies perhaps) hence compresses
+pretty well: enabling compression can save a great amount of network
+resources.
However establishing an SSL/TLS connection (type=imaps, or type=imap and
STARTTLS=YES) yields a small overhead due to the SSL/TLS handshake.
On the other hand if SSH access is allowed on the remote server, one can
-tunnel the IMAP traffic through SSH and use OpenSSH's ControlPersist
-feature to save most of the cryptographic overhead (at the expense of a
-local 'ssh' process and a remote 'imap' process). Moreover if the IMAP
-user is a valid UNIX user it is possible to use pre-authentication on
-the remote server as well, which saves the extra round trip caused by
-the AUTHENTICATE command. For instance the following configuration
-snippet saves bandwidth and brings a significant speed gain compared to
-type=imaps.
+tunnel the IMAP traffic through SSH and use OpenSSH's ControlPersist feature
+to save most of the cryptographic overhead (at the expense of a local 'ssh'
+process and a remote 'imap' process). Moreover if the IMAP user is a valid
+UNIX user it is possible to use pre-authentication on the remote server as
+well, which saves the extra round trip caused by the AUTHENTICATE command.
+For instance the following configuration snippet saves bandwidth and brings a
+significant speed gain compared to type=imaps.
local: $XDG_CONFIG_HOME/interimap/config:
[remote]
@@ -59,7 +56,7 @@ type=imaps.
Host imap.example.net
IdentityFile ~/.ssh/id-interimap
IdentitiesOnly yes
- ControlPath /run/shm/%u@%n
+ ControlPath ${XDG_RUNTIME_DIR}/ssh-imap-%C
ControlMaster auto
ControlPersist 10m
StrictHostKeyChecking yes
@@ -69,17 +66,17 @@ type=imaps.
Compression yes
remote: ~user/.ssh/authorized_keys:
- command="/usr/lib/dovecot/imap",no-agent-forwarding,no-port-forwarding,no-pty,no-user-rc,no-X11-forwarding ssh-... id-interimap
+ restrict,command="/usr/bin/doveadm exec imap" ssh-[…] id-interimap
-However for long-lived connections (using the --watch command-line
-option), the TLS overhead becomes negligible hence the advantage offered
-by the OpenSSH ControlPersist feature is not obvious. Furthermore if
-the remote server supports the IMAP COMPRESS extension [RFC4978], adding
-compress=DEFLATE to the configuration can also greatly reduce bandwidth
-usage with regular INET sockets (type=imaps or type=imap).
+However for long-lived connections (using the --watch command-line option),
+the TLS overhead becomes negligible hence the advantage offered by the OpenSSH
+ControlPersist feature is not obvious. Furthermore if the remote server
+supports the IMAP COMPRESS extension [RFC4978], adding compress=DEFLATE to the
+configuration can also greatly reduce bandwidth usage with regular INET
+sockets (type=imaps or type=imap).
-_______________________________________________________________________
+______________________________________________________________________________
-InterIMAP is Copyright© 2015-2018 Guilhem Moulin ⟨guilhem@fripost.org⟩,
-and licensed for use under the GNU General Public License version 3 or
-later. See ‘COPYING’ for specific terms and distribution information.
+InterIMAP is Copyright © 2015-2022 Guilhem Moulin ⟨guilhem@fripost.org⟩, and
+is licensed for use under the GNU General Public License version 3 or later.
+See ‘COPYING’ for specific terms and distribution information.
diff --git a/benchmark/dovecot.conf b/benchmark/dovecot.conf
index 55301d9..68cc40e 100644
--- a/benchmark/dovecot.conf
+++ b/benchmark/dovecot.conf
@@ -1,15 +1,20 @@
+dovecot_config_version = 2.4.0
+dovecot_storage_version = 2.4.0
+
log_path = /dev/shm/mail.log
-mail_home = /dev/shm/vmail/%u
-mail_location = mdbox:~/mail
+mail_home = /dev/shm/vmail/%{user | username | lower}
+mail_driver = mdbox
+mail_path = ~/mail
+mailbox_list_index = yes
ssl = no
listen = 127.0.0.1
-namespace {
+namespace inbox {
inbox = yes
separator = /
}
-# https://wiki.dovecot.org/HowTo/Rootless
+# https://doc.dovecot.org/latest/core/config/rootless.html#rootless-installation
base_dir = /dev/shm/dovecot/run
default_internal_user = nobody
default_internal_group = nogroup
@@ -25,20 +30,18 @@ service stats {
chroot =
}
-passdb {
- args = scheme=PLAIN username_format=%u /dev/shm/dovecot/users
+passdb passwd-file {
driver = passwd-file
+ default_password_scheme = plain
+ passwd_file_path = /dev/shm/dovecot/users
}
-userdb {
- args = username_format=%u /dev/shm/dovecot/users
+userdb passwd-file {
driver = passwd-file
+ passwd_file_path = /dev/shm/dovecot/users
}
-protocols = imap
-
-mail_plugins = zlib
-protocol imap {
- mail_plugins = imap_zlib
+protocols {
+ imap = yes
}
service imap-login {
diff --git a/benchmark/run b/benchmark/run
index 4a83c68..dd30bdc 100755
--- a/benchmark/run
+++ b/benchmark/run
@@ -140,15 +140,19 @@ jail /usr/sbin/dovecot -c"/dev/shm/dovecot/config"
install -onobody -gnogroup -m0600 /dev/null \
"$ROOTDIR/dev/shm/dovecot/users"
PASSWORD="$(xxd -l16 -p </dev/urandom)"
-printf "%s:%s:::::\\n" "user" "$PASSWORD" \
+printf "%s:%s::::%s:\\n" "user" "$PASSWORD" "/dev/shm/vmail/user" \
>"$ROOTDIR/dev/shm/dovecot/users"
# install user configuration for Dovecot, interimap, and offlineimap
cat >"$ROOTDIR/dev/shm/nobody/.dovecot.conf" <<-EOF
+ dovecot_config_version = 2.4.0
+ dovecot_storage_version = 2.4.0
log_path = /dev/null
- mail_home = /dev/shm/nobody
- mail_location = maildir:~/Maildir
ssl = no
+ mail_home = /dev/shm/nobody
+ mail_driver = maildir
+ mail_path = ~/Maildir
+ mailbox_list_index = yes
EOF
install -onobody -gnogroup -Dm0700 --directory \
@@ -198,9 +202,10 @@ cat >"$ROOTDIR/dev/shm/nobody/.offlineimaprc" <<-EOF
EOF
# install interimap's development version
-install -oroot -groot -m0755 -Dt "/$ROOTDIR/usr/bin" \
+install -oroot -groot -m0755 -Dt "$ROOTDIR/usr/bin" \
./interimap ./benchmark/random_maildir.pl
-install -oroot -groot -Dm0644 \
+sed -ri "0,/^(use\\s+\lib\\s+)([\"'])[^\"']*\\2\\s*;/ s||#&|" -- "$ROOTDIR/usr/bin/interimap"
+install -oroot -groot -m0644 -DT \
./lib/Net/IMAP/InterIMAP.pm "$ROOTDIR/usr/share/perl5/Net/IMAP/InterIMAP.pm"
# create a random mail store at mdbox:~/mail.back
@@ -221,18 +226,19 @@ prepare() {
done
# convert to mdbox
- jail doveadm -c"/dev/shm/dovecot/config" -omail_location="maildir:~/maildir" \
- sync "mdbox:~/mail.back"
+ jail doveadm -c"/dev/shm/dovecot/config" -omail_driver="maildir" -omail_path="~/maildir" \
+ sync --no-userdb-lookup "mdbox:~/mail.back"
jail rm -rf -- "$maildir"
# expunge 20% and purge
for m in "${!MAILBOXES[@]}"; do
n="${MAILBOXES["$m"]}"
seqs="$(shuf -n $((n/5)) -i"1-$n")"
- jail doveadm -c"/dev/shm/dovecot/config" -omail_location="mdbox:~/mail.back" \
- expunge mailbox "$m" "${seqs//$'\n'/,}"
+ jail doveadm -c"/dev/shm/dovecot/config" -omail_driver="mdbox" -omail_path="~/mail.back" \
+ expunge --no-userdb-lookup mailbox "$m" "${seqs//$'\n'/,}"
done
- jail doveadm -c"/dev/shm/dovecot/config" -omail_location="mdbox:~/mail.back" purge
+ jail doveadm -c"/dev/shm/dovecot/config" -omail_driver="mdbox" -omail_path="~/mail.back" \
+ purge --no-userdb-lookup
}
# populate a clientn from backup mailstore mdbox:~/mail.back (copied to
@@ -249,8 +255,8 @@ populate() {
# force dovecot to index and compute the state, otherwise the first
# thing to query might be disadvantaged
- jail doveadm -c"/dev/shm/dovecot/config" index "INBOX"
- jail doveadm -c"/dev/shm/dovecot/config" mailbox status "all" "*" >/dev/null
+ jail doveadm -c"/dev/shm/dovecot/config" index --no-userdb-lookup "INBOX" 2>/dev/null
+ jail doveadm -c"/dev/shm/dovecot/config" mailbox status --no-userdb-lookup "all" "*" >/dev/null
u="local"
# initial configuration
@@ -394,9 +400,9 @@ activity2() {
deliver "mailbox2" <"$ROOTDIR/tmp/msg2"
deliver "mailbox3" <"$ROOTDIR/tmp/msg3"
# intentionally modify the remote only because not all local backend speak IMAP
- jail doveadm -c"/dev/shm/dovecot/config" expunge mailbox "mailbox3" "1:2"
- jail doveadm -c"/dev/shm/dovecot/config" expunge mailbox "mailbox4" "1,3"
- jail doveadm -c"/dev/shm/dovecot/config" expunge mailbox "mailbox5" "*"
+ jail doveadm -c"/dev/shm/dovecot/config" expunge --no-userdb-lookup mailbox "mailbox3" "1:2"
+ jail doveadm -c"/dev/shm/dovecot/config" expunge --no-userdb-lookup mailbox "mailbox4" "1,3"
+ jail doveadm -c"/dev/shm/dovecot/config" expunge --no-userdb-lookup mailbox "mailbox5" "*"
}
for n in 100 1000 10000; do
@@ -488,14 +494,14 @@ while [ $(date +%s) -lt $timeout ]; do
n="$(shuf -n1 -i1-100)"
if [ $n -le 5 ]; then
# expunge a random message on the remote
- read guid uid < <(jail doveadm -c"/dev/shm/dovecot/config" search all | shuf -n1)
- jail doveadm -c"/dev/shm/dovecot/config" expunge mailbox-guid "$guid" uid "$uid"
+ read guid uid < <(jail doveadm -c"/dev/shm/dovecot/config" search --no-userdb-lookup all | shuf -n1)
+ jail doveadm -c"/dev/shm/dovecot/config" expunge --no-userdb-lookup mailbox-guid "$guid" uid "$uid"
fi
n="$(shuf -n1 -i1-100)"
if [ $n -le 10 ]; then
# mark a random message as seen
- read guid uid < <(jail doveadm -c"/dev/shm/dovecot/config" search all | shuf -n1)
- jail doveadm -c"/dev/shm/dovecot/config" flags add "\\Seen" mailbox-guid "$guid" uid "$uid"
+ read guid uid < <(jail doveadm -c"/dev/shm/dovecot/config" search --no-userdb-lookup all | shuf -n1)
+ jail doveadm -c"/dev/shm/dovecot/config" flags add --no-userdb-lookup "\\Seen" mailbox-guid "$guid" uid "$uid"
fi
sleep $step
done
diff --git a/doc/benchmark.md b/doc/benchmark.md
index 72f51a4..3b58e10 100644
--- a/doc/benchmark.md
+++ b/doc/benchmark.md
@@ -48,8 +48,8 @@ by placing packet counters on the interface.
[OfflineIMAP]: https://www.offlineimap.org/
[benchmark-script]: https://git.guilhem.org/interimap/plain/benchmark/run
[Dovecot]: https://dovecot.org
-[dbox]: https://wiki.dovecot.org/MailboxFormat/dbox
-[maildir]: https://wiki.dovecot.org/MailboxFormat/Maildir
+[dbox]: https://doc.dovecot.org/latest/core/config/mailbox_formats/dbox.html
+[maildir]: https://doc.dovecot.org/latest/core/config/mailbox_formats/maildir.html
-----------------------------------------------------------------------
diff --git a/doc/build.md b/doc/build.md
index 4a4f80d..b40760e 100644
--- a/doc/build.md
+++ b/doc/build.md
@@ -1,7 +1,7 @@
% Build instructions
% [Guilhem Moulin](mailto:guilhem@fripost.org)
-On Debian 9 (codename *Stretch*) and later, installing [`interimap`(1)]
+On Debian 10 (codename *Buster*) and later, installing [`interimap`(1)]
is a single command away:
$ sudo apt install interimap
@@ -24,7 +24,7 @@ following Perl modules:
* [`Getopt::Long`](https://perldoc.perl.org/Getopt/Long.html) (*core module*)
* [`MIME::Base64`](https://perldoc.perl.org/MIME/Base64.html) (*core module*) — if authentication is required
* [`List::Util`](https://perldoc.perl.org/List/Util.html) (*core module*)
- * [`Net::SSLeay`](https://metacpan.org/pod/Net::SSLeay) ≥1.73
+ * [`Net::SSLeay`](https://metacpan.org/pod/Net::SSLeay) ≥1.86_06
* [`POSIX`](https://perldoc.perl.org/POSIX.html) (*core module*)
* [`Socket`](https://perldoc.perl.org/Socket.html) (*core module*)
* [`Time::HiRes`](https://perldoc.perl.org/Time/HiRes.html) (*core module*) — if `logfile` is set
@@ -38,10 +38,10 @@ Additional packages are required in order to run the test suite:
$ sudo apt install dovecot-imapd dovecot-lmtpd openssl procps sqlite3 xxd
<!-- -->
- $ make test
+ $ make check
-(The test suite also needs to bind to TCP ports 10024, 10143 and 10993
-on the loopback interface.)
+(Note also that the test suite needs to bind to TCP ports 10024, 10143
+and 10993 on the loopback interface.)
Generate documentation
======================
@@ -51,32 +51,53 @@ Yet another set of packages is needed to generate the documentation:
$ sudo apt install jq pandoc
Run `` `make manual` `` (or just `` `make` ``) in order to generate the
-manpages. You'll find them at `doc/*.[1-9]`. Use for instance `` `man
--l doc/interimap.1` `` in order to read your copy of the [`interimap`(1)]
-manpage.
+manuals. You'll find them at `build/doc/*.[1-9]`. Then use for
+instance `` `man -l build/doc/interimap.1` `` to read your local copy of
+the [`interimap`(1)] manual.
-The HTML documentation can be built with `` `make html` ``. HTML files
-are generated alongside their Markdown source by default, but you can
-choose another target directory using the `HTML_ROOTDIR` environment
-variable (the value of which defaults to `./doc`). Moreover the
-[`libjs-bootstrap`](https://tracker.debian.org/libjs-bootstrap) is
-needed by default for the local CSS file; this can be controlled with
-the `CSS` environment variable (the value of which defaults to
-`/usr/share/javascript/bootstrap/css/bootstrap.min.css`).
+The HTML documentation can be built with `` `make html` ``. By default
+HTML files are generated at `build/doc/*.html`, and a local CSS file is
+used — namely `/usr/share/javascript/bootstrap4/css/bootstrap.css`,
+shipped the by [`libjs-bootstrap`](https://tracker.debian.org/libjs-bootstrap)
+package. But this is configurable: use for instance
-For instance, use
+ $ make html builddir="$XDG_RUNTIME_DIR/interimap" \
+ CSS="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css"
- $ env CSS="https://guilhem.org/static/css/bootstrap.min.css" \
- HTML_ROOTDIR="$XDG_RUNTIME_DIR/interimap" \
- make html
+to generate the HTML documentation under `$XDG_RUNTIME_DIR/interimap/doc`
+using a remote CSS file.
-to generate the HTML documentation under directory `$XDG_RUNTIME_DIR/interimap`
-(which needs to exist) using a remote CSS file.
-
-The `doc` target generates all documentation, manpages as well as HTML
+The `doc` target generates all documentation, manuals as well as HTML
pages.
+Installation without root privileges
+====================================
+
+By default `` `make install` `` installs [`interimap`(1)] under
+`/usr/local`, hence requires root privileges. However another prefix
+can be used in order to perform the (un)installation as an unprivileged
+user. For instance
+
+ $ install -m0700 -vd ${XDG_DATA_HOME:-~/.local/share}/interimap
+ $ make install-nodoc \
+ prefix=$HOME/.local \
+ sitelib=${XDG_DATA_HOME:-~/.local/share}/interimap/lib \
+ systemd_userunitdir=${XDG_DATA_HOME:-~/.local/share}/systemd/user
+
+skips documentation and installs
+
+ * executables into `~/.local/bin` (instead of `/usr/local/bin`);
+ * libraries into `$XDG_DATA_HOME/interimap/lib` or `~/.local/share/interimap/lib`
+ (instead of `/usr/local/lib/site_perl`); and
+ * [systemd user unit files][`systemd.unit`(5)] into `$XDG_DATA_HOME/systemd/user`
+ or `~/.local/share/systemd/user` (instead of `/usr/local/lib/systemd/user`).
+
+Note that for uninstallation one must call `` `make uninstall prefix=…` ``
+with the very same assignment(s) used for installation.
+
+[`systemd.unit`(5)]: https://www.freedesktop.org/software/systemd/man/systemd.unit.html
+
Build custom Debian packages
============================
@@ -84,12 +105,12 @@ Debian GNU/Linux users can also use [`gbp`(1)] from
[`git-buildpackage`](https://tracker.debian.org/pkg/git-buildpackage) in
order to build their own packages:
- $ git checkout debian
+ $ git checkout debian/latest
$ gbp buildpackage
Alternatively, for the development version:
- $ git checkout debian
+ $ git checkout debian/latest
$ git merge master
$ gbp buildpackage --git-force-create --git-upstream-tree=BRANCH
diff --git a/doc/development.md b/doc/development.md
index f4578b9..f739dc0 100644
--- a/doc/development.md
+++ b/doc/development.md
@@ -15,14 +15,18 @@ Dovecot configuration
=====================
Create a file `$BASEDIR/dovecot.conf`, which will be used as
-configuration for the various Dovecot commands (the system configuration
-will be skipped).
+configuration for the various Dovecot 2.4 commands (the system
+configuration will be skipped).
$ cat >"$BASEDIR/dovecot.conf" <<-EOF
- log_path = "$BASEDIR/dovecot.log"
- ssl = no
- mail_home = "$BASEDIR/%u"
- mail_location = maildir:~/Mail
+ dovecot_config_version = 2.4.0
+ dovecot_storage_version = 2.4.0
+ log_path = "$BASEDIR/dovecot.log"
+ ssl = no
+
+ mail_home = "$BASEDIR/%{user | username | lower}"
+ mail_driver = maildir
+ mail_path = ~/Mail
EOF
Here are some details on the above:
@@ -36,18 +40,17 @@ Here are some details on the above:
`mail_home`
: Dovecot needs the name of the user to (pre-)authenticate. It is shown
- in the greeting line, and also used in [`%`-variable] expansion.
+ in the greeting line, and also used in [settings variables] expansion.
Several [`doveadm`(1)] sub-commands have a `-u` (or `-d`) option which
- can be used to determine the username. When this option is not set,
- the username is taken from the `USER` environment variable. If that
- environment variable is unset as well, then the return string of
- [`getlogin`(3)] is used.
+ can be used to determine the username. To avoid performing the userdb
+ lookup one can pass `--no-userdb-lookup` instead, in which case the
+ username is taken from the `USER` environment variable.
Similarly, the user's home directory is used in (`~`- and)
- [`%`-variable] expansion. It's taken from the `HOME` environment
- variable when the `mail_home` setting is left unset in the Dovecot
- configuration (and not overridden by the [user database][User
- Databases].
+ [settings variables] expansion. It's taken from the `HOME`
+ environment variable when the `mail_home` setting is left unset in
+ the Dovecot configuration (and not overridden by the
+ [user database][User Databases].
`mail_home` can therefore be left unset if the `HOME` environment
variable is consistently set to `$BASEDIR/$USER`. However it's
@@ -55,14 +58,17 @@ Here are some details on the above:
command run in a non-curated environment might mess up with your own
mail storage…
-`mail_location`
+`mail_driver`
- : The user's mail storage resides — in [Maildir] format — in a directory
- `Mail` under their home directory. This is enough if you're fine with
- the default IMAP hierarchy delimiter (which depends on the mail format)
- is used, and if you need a single [IMAP namespace][RFC 2342]. For more
- complex setups you'll need one or more [`namespace {…}` block][Dovecot
- Namespaces].
+ : Use the [Maildir] format for mail storage..
+
+`mail_path`:
+
+ : The user's mail storage resides in a directory `Mail` under their
+ home directory. This is enough if you're fine with the default IMAP
+ hierarchy delimiter (which depends on the mail format) is used, and
+ if you need a single [IMAP namespace][RFC 2342]. For more complex
+ setups you'll need one or more [`namespace {…}` block][Dovecot Namespaces].
Mail storage access
===================
@@ -88,7 +94,7 @@ the latter to create a mailbox `foo`, add a sample message to it, and
finally mark it as `\Seen`.
$ env -i PATH="/usr/bin:/bin" USER="testuser" \
- doveadm -c "$BASEDIR/dovecot.conf" mailbox create "foo"
+ doveadm -c "$BASEDIR/dovecot.conf" mailbox create --no-userdb-lookup "foo"
<!-- -->
$ env -i PATH="/usr/bin:/bin" USER="testuser" HOME="$BASEDIR/testuser" \
doveadm -c "$BASEDIR/dovecot.conf" exec dovecot-lda -e -m "foo" <<-EOF
@@ -102,7 +108,7 @@ finally mark it as `\Seen`.
EOF
<!-- -->
$ env -i PATH="/usr/bin:/bin" USER="testuser" \
- doveadm -c "$BASEDIR/dovecot.conf" flags add "\\Seen" mailbox "foo" "*"
+ doveadm -c "$BASEDIR/dovecot.conf" flags add --no-userdb-lookup "\\Seen" mailbox "foo" "*"
Normally [`dovecot-lda`(1)][Dovecot LDA] tries to do a userdb lookup in
order to determine the user's home directory. Since we didn't configure
@@ -136,7 +142,7 @@ and `remote` accounts.
Run [`interimap`(1)] without `--watch` in order to create the database.
- $ env -i PATH="$PATH" perl -I./lib -T ./interimap --config="$BASEDIR/interimap.conf"
+ $ env -i PATH="$PATH" perl -T ./interimap --config="$BASEDIR/interimap.conf"
Creating new schema in database file …/interimap.db
database: Created mailbox INBOX
[…]
@@ -144,7 +150,7 @@ Run [`interimap`(1)] without `--watch` in order to create the database.
You can now run [`interimap`(1)] with `--watch` set, here to one second
to observe synchronization steps early.
- $ env -i PATH="$PATH" perl -I./lib -T ./interimap --config="$BASEDIR/interimap.conf" \
+ $ env -i PATH="$PATH" perl -T ./interimap --config="$BASEDIR/interimap.conf" \
--watch=1 --debug
Use instructions from the [previous section][Mail storage access]
@@ -168,12 +174,12 @@ Create a [`pullimap`(1)] configuration file with as section `[foo]`.
Run [`pullimap`(1)] without `--idle` in order to create the state file.
- $ env -i PATH="$PATH" perl -I./lib -T ./pullimap --config="$BASEDIR/pullimap.conf" \
+ $ env -i PATH="$PATH" perl -T ./pullimap --config="$BASEDIR/pullimap.conf" \
--no-delivery foo
You can now run [`pullimap`(1)] with `--idle` set.
- $ env -i PATH="$PATH" perl -I./lib -T ./pullimap --config="$BASEDIR/pullimap.conf" \
+ $ env -i PATH="$PATH" perl -T ./pullimap --config="$BASEDIR/pullimap.conf" \
--no-delivery --idle --debug foo
Use instructions from the [previous section][Mail storage access]
@@ -193,14 +199,13 @@ recursively remove the directory `$BASEDIR`.
[IMAP4rev1]: https://tools.ietf.org/html/rfc3501
[Dovecot]: https://dovecot.org
-[Dovecot Logging]: https://doc.dovecot.org/admin_manual/logging/
-[Dovecot LDA]: https://wiki.dovecot.org/LDA
-[`getlogin`(3)]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/getlogin.html
-[User Databases]: https://doc.dovecot.org/configuration_manual/authentication/user_databases_userdb/
-[Maildir]: https://wiki.dovecot.org/MailLocation/Maildir
+[Dovecot Logging]: https://doc.dovecot.org/latest/core/admin/logging.html#dovecot-logging
+[Dovecot LDA]: https://doc.dovecot.org/latest/core/config/delivery/lda.html
+[User Databases]: https://doc.dovecot.org/latest/core/config/auth/userdb.html
+[Maildir]: https://doc.dovecot.org/latest/core/config/mailbox_formats/maildir.html
[RFC 2342]: https://tools.ietf.org/html/rfc2342
-[Dovecot Namespaces]: https://doc.dovecot.org/configuration_manual/namespace/
+[Dovecot Namespaces]: https://doc.dovecot.org/latest/core/config/namespaces.html
[`interimap`(1)]: interimap.1.html
[`pullimap`(1)]: pullimap.1.html
-[`doveadm`(1)]: https://wiki.dovecot.org/Tools/Doveadm
-[`%`-variable]: https://doc.dovecot.org/configuration_manual/config_file/config_variables/
+[`doveadm`(1)]: https://doc.dovecot.org/latest/core/man/doveadm.1.html
+[settings variables]: https://doc.dovecot.org/latest/core/settings/variables.html
diff --git a/doc/getting-started.md b/doc/getting-started.md
index 1d059b4..e89eb19 100644
--- a/doc/getting-started.md
+++ b/doc/getting-started.md
@@ -20,7 +20,7 @@ format][mbox]). Local mail clients usually access it directly. They
also often maintain their own cache in order to speed up message header
listing and searches.
-While most bidirectional synchronisation software (such as [OfflineIMAP])
+While most bidirectional synchronization software (such as [OfflineIMAP])
are able to handle a mail storage in Maildir format, *InterIMAP is
not*. Instead, InterIMAP needs an [IMAP4rev1] server on *both* peers
to synchronize. This may sound like a severe limitation at first, but by
@@ -84,7 +84,7 @@ mailbox delivery) then don't disable them, and modify Dovecot's system
wide configuration instead. Same thing if your mail client isn't able
to spawn a command for IMAP communication, and instead insists on
connecting to a network socket (in that case you'll even need to
-configure [user authentication](https://wiki.dovecot.org/Authentication)
+configure [user authentication](https://doc.dovecot.org/latest/core/config/auth/overview.html)
for the IMAP service, which is out of scope for the present document).
Run the following command to terminate and disable the system-wide
@@ -94,14 +94,19 @@ Dovecot processes.
Create a new directory `$XDG_CONFIG_HOME/dovecot` holding the local
-Dovecot configuration:
+Dovecot 2.4 configuration:
$ install -m0700 -vd ${XDG_CONFIG_HOME:-~/.config}/dovecot
<!-- -->
$ cat >${XDG_CONFIG_HOME:-~/.config}/dovecot/dovecot.conf <<-EOF
+ dovecot_config_version = 2.4.0
+ dovecot_storage_version = 2.4.0
+
ssl = no
- mail_location = maildir:~/Mail
- namespace {
+ mail_home = /home/%{user | username | lower}
+ mail_driver = maildir
+ mail_path = ~/Mail
+ namespace inbox {
inbox = yes
separator = /
}
@@ -113,9 +118,10 @@ Some remarks on the above:
running `` `doveconf -nc ${XDG_CONFIG_HOME:-~/.config}/dovecot/dovecot.conf` ``.
* Messages will be stored in Maildir format under `~/Mail`. Ensure
the directory is either *empty* or *doesn't exist* before
- continuing! You may want to choose a different [format](https://wiki.dovecot.org/MailboxFormat)
- here, or simply append `:LAYOUT=fs` to the `mail_location` value in
- order to use a nicer (File System like) Maildir layout.
+ continuing! You may want to choose a different
+ [format](https://doc.dovecot.org/latest/core/config/mailbox_formats/overview.html)
+ here, or simply set `mailbox_list_layout = fs` in order to use a
+ nicer (File System like) Maildir layout.
* The `separator` setting defines the IMAP hierarchy delimiter. This
is orthogonal to the Maildir layout delimiter, and you can safely
change it later (even on an existing mail store). Popular hierarchy
@@ -198,7 +204,7 @@ for the sake of clarity we start from an empty file here.
shell process doesn't linger around during the IMAP session.)
3. And finally append a `[remote]` section with your account
- information at `imap.example.org` (adapt the values accordingly):
+ information at `imap.example.net` (adapt the values accordingly):
$ cat >>${XDG_CONFIG_HOME:-~/.config}/interimap/config <<-EOF
@@ -281,7 +287,7 @@ Manual
[IMAP4rev1]: https://tools.ietf.org/html/rfc3501
[INI file]: https://en.wikipedia.org/wiki/INI_file
[`interimap`(1)]: interimap.1.html
-[LMTP server]: https://doc.dovecot.org/configuration_manual/protocols/lmtp_server/
+[LMTP server]: https://doc.dovecot.org/latest/core/config/delivery/lmtp.html
[Maildir]: https://en.wikipedia.org/wiki/Maildir
[mbox]: https://en.wikipedia.org/wiki/Mbox
[MTA]: https://en.wikipedia.org/wiki/Message_transfer_agent
diff --git a/doc/interimap.1.md b/doc/interimap.1.md
index 5370d79..18b3581 100644
--- a/doc/interimap.1.md
+++ b/doc/interimap.1.md
@@ -249,7 +249,7 @@ Valid options are:
Two wildcards are available, and passed verbatim to the IMAP server:
a ‘\*’ character matches zero or more characters, while a ‘%’
character matches zero or more characters up to the hierarchy
- delimiter. Hardcoding the hierarchy delimiter in this setting is
+ delimiter. Hard-coding the hierarchy delimiter in this setting is
not advised because the server might silently change it at some
point. A null character should be used instead. For instance, if
*list-mailbox* is set `"foo\x00bar"` then, assuming the hierarchy
@@ -309,7 +309,7 @@ Valid options are:
`type=imap` and `type=imaps` are respectively used for IMAP and IMAP
over SSL/TLS connections over an INET socket.
`type=tunnel` causes `interimap` to create an unnamed pair of
- connected sockets for interprocess communication with a *command*
+ connected sockets for inter-process communication with a *command*
instead of opening a network socket.
Note that specifying `type=tunnel` in the `[remote]` section makes
the default *database* to be `localhost.db`.
@@ -317,7 +317,9 @@ Valid options are:
*host*
-: Server hostname, for `type=imap` and `type=imaps`.
+: Server hostname or IP address, for `type=imap` and `type=imaps`.
+ The value can optionally be enclosed in square brackets to force its
+ interpretation as an IP literal (hence skip name resolution).
(Default: `localhost`.)
*port*
@@ -327,8 +329,8 @@ Valid options are:
*proxy*
-: An optional SOCKS proxy to use for TCP connections to the IMAP
- server (`type=imap` and `type=imaps` only), formatted as
+: Optional SOCKS proxy to use for TCP connections to the IMAP server
+ (`type=imap` and `type=imaps` only), formatted as
`PROTOCOL://[USER:PASSWORD@]PROXYHOST[:PROXYPORT]`.
If `PROXYPORT` is omitted, it is assumed at port 1080.
Only [SOCKSv5][RFC 1928] is supported (with optional
@@ -376,29 +378,44 @@ Valid options are:
*null-stderr*
: Whether to redirect *command*'s standard error to `/dev/null` for
- `type=tunnel`. (Default: `NO`.)
+ `type=tunnel`. This option is ignored when the `--debug` flag is
+ set. (Default: `NO`.)
*SSL_protocols*
-: A space-separated list of SSL protocols to enable or disable (if
- prefixed with an exclamation mark `!`. Known protocols are `SSLv2`,
- `SSLv3`, `TLSv1`, `TLSv1.1`, `TLSv1.2`, and `TLSv1.3`. Enabling a
- protocol is a short-hand for disabling all other protocols.
- (Default: `!SSLv2 !SSLv3 !TLSv1 !TLSv1.1`, i.e., only enable TLSv1.2
- and above.)
+: Space-separated list of SSL/TLS protocol versions to explicitly
+ enable (or disable if prefixed with an exclamation mark `!`).
+ Potentially known protocols are `SSLv2`, `SSLv3`, `TLSv1`,
+ `TLSv1.1`, `TLSv1.2`, and `TLSv1.3`, depending on the OpenSSL
+ version used.
+ Enabling a protocol is a short-hand for disabling all other
+ protocols.
-*SSL_cipher_list*
+ *DEPRECATED*: Use *SSL_protocol_min* and/or *SSL_protocol_max*
+ instead.
-: The cipher list to send to the server. Although the server
- determines which cipher suite is used, it should take the first
- supported cipher in the list sent by the client. See
- [`ciphers`(1ssl)] for more information.
+*SSL_protocol_min*, *SSL_protocol_max*
+
+: Set minimum resp. maximum SSL/TLS protocol version to use for the
+ connection. Potentially recognized values are `SSLv3`, `TLSv1`,
+ `TLSv1.1`, `TLSv1.2`, and `TLSv1.3`, depending on the OpenSSL
+ version used.
+
+*SSL_cipherlist*, *SSL_ciphersuites*
+
+: Sets the TLSv1.2 and below cipher list resp. TLSv1.3 cipher suites.
+ The combination of these lists is sent to the server, which then
+ determines which cipher to use (normally the first supported one
+ from the list sent by the client). The default suites depend on the
+ OpenSSL version and its configuration, see [`ciphers`(1ssl)] for
+ more information.
*SSL_fingerprint*
-: Fingerprint of the server certificate's Subject Public Key Info, in
- the form `[ALGO$]DIGEST_HEX` where `ALGO` is the used algorithm (by
- default `sha256`).
+: Space-separated list of acceptable fingerprints for the server
+ certificate's Subject Public Key Info, in the form
+ `[ALGO$]DIGEST_HEX` where `ALGO` is the digest algorithm (by default
+ `sha256`).
Attempting to connect to a server with a non-matching certificate
SPKI fingerprint causes `interimap` to abort the connection during
the SSL/TLS handshake.
@@ -409,25 +426,47 @@ Valid options are:
| openssl pkey -pubin -outform DER \
| openssl dgst -sha256
+ Specifying multiple digest values can be useful in key rollover
+ scenarios and/or when the server supports certificates of different
+ types (for instance a dual-cert RSA/ECDSA setup). In that case the
+ connection is aborted when none of the specified digests matches.
+
*SSL_verify*
-: Whether to verify the server certificate chain.
- Note that using *SSL_fingerprint* to specify the fingerprint of the
- server certificate is an orthogonal authentication measure as it
- ignores the CA chain.
+: Whether to 1/ verify the server certificate chain; and 2/ match its
+ Subject Alternative Name (SAN) or Subject CommonName (CN) against
+ the value of the *host* option.
(Default: `YES`.)
+ Note that using *SSL_fingerprint* to specify the fingerprint of the
+ server certificate provides an independent server authentication
+ measure as it pins directly its key material and ignore its chain of
+ trust.
+
+*SSL_CAfile*
+
+: File containing trusted certificates to use during server
+ certificate verification when `SSL_verify=YES`.
+
+ Trusted CA certificates are loaded from the default system locations
+ unless one (or both) of *SSL_CAfile* or *SSL_CApath* is set.
+
*SSL_CApath*
-: Directory to use for server certificate verification if
+: Directory to use for server certificate verification when
`SSL_verify=YES`.
This directory must be in “hash format”, see [`verify`(1ssl)] for
more information.
-*SSL_CAfile*
+ Trusted CA certificates are loaded from the default system locations
+ unless one (or both) of *SSL_CAfile* or *SSL_CApath* is set.
-: File containing trusted certificates to use during server
- certificate authentication if `SSL_verify=YES`.
+*SSL_hostname*
+
+: Name to use for the TLS SNI (Server Name Indication) extension. The
+ default value is taken from the *host* option when it is a hostname,
+ and to the empty string when it is an IP literal.
+ Setting *SSL_hostname* to the empty string explicitly disables SNI.
Supported extensions {#supported-extensions}
====================
@@ -561,6 +600,6 @@ A _getting started_ guide is available [there](getting-started.html).
[INI file]: https://en.wikipedia.org/wiki/INI_file
[PCRE]: https://en.wikipedia.org/wiki/Perl_Compatible_Regular_Expressions
-[`ciphers`(1ssl)]: https://www.openssl.org/docs/manmaster/apps/ciphers.html
-[`verify`(1ssl)]: https://www.openssl.org/docs/manmaster/apps/verify.html
-[`doveadm-deduplicate`(1)]: https://wiki.dovecot.org/Tools/Doveadm/Deduplicate
+[`ciphers`(1ssl)]: https://www.openssl.org/docs/manmaster/man1/openssl-ciphers.html
+[`verify`(1ssl)]: https://www.openssl.org/docs/manmaster/man1/openssl-verify.html
+[`doveadm-deduplicate`(1)]: https://doc.dovecot.org/latest/core/man/doveadm-deduplicate.1.html
diff --git a/doc/multi-account.md b/doc/multi-account.md
index cc0a1b8..58fdd2d 100644
--- a/doc/multi-account.md
+++ b/doc/multi-account.md
@@ -82,20 +82,23 @@ over: they will only live on the local instance.
$ cat >${XDG_CONFIG_HOME:-~/.config}/dovecot/dovecot.conf <<-EOF
ssl = no
- namespace {
- location = maildir:~/Mail
- inbox = yes
- separator = /
+ namespace indbox {
+ mail_driver = maildir
+ mail_path = ~/Mail
+ inbox = yes
+ separator = /
}
namespace perso {
- prefix = perso/
- location = maildir:~/Mail/perso
- separator = /
+ prefix = perso/
+ mail_driver = maildir
+ mail_path = ~/Mail/perso
+ separator = /
}
namespace work {
- prefix = work/
- location = maildir:~/Mail/work
- separator = /
+ prefix = work/
+ mail_driver = maildir
+ mail_path = ~/Mail/work
+ separator = /
}
EOF
@@ -205,5 +208,5 @@ Template user unit for systemd are provided in order to run these
[Getting Started]: getting-started.html
[RFC 2342]: https://tools.ietf.org/html/rfc2342
[RFC 3501]: https://tools.ietf.org/html/rfc3501
-[Dovecot namespaces]: https://doc.dovecot.org/configuration_manual/namespace/
+[Dovecot namespaces]: https://doc.dovecot.org/latest/core/config/namespaces.html
[`interimap`(1)]: interimap.1.html
diff --git a/doc/pullimap.1.md b/doc/pullimap.1.md
index 0055675..89969b2 100644
--- a/doc/pullimap.1.md
+++ b/doc/pullimap.1.md
@@ -106,14 +106,14 @@ Valid options are:
*deliver-ehlo*
-: Hostname to use in `EHLO` or `LHLO` commands.
+: Name to use in `EHLO` or `LHLO` commands.
(Default: `localhost.localdomain`.)
*deliver-rcpt*
: Message recipient. Note that the local part needs to quoted if it
contains special characters; see [RFC 5321] for details.
- (Default: the username associated with the effective uid of the
+ (Default: the username associated with the effective user ID of the
`pullimap` process.)
*purge-after*
@@ -123,7 +123,7 @@ Valid options are:
`SEARCH` criterion ignoring time and timezone.)
If *purge-after* is set to `0` then messages are deleted immediately
after delivery. Otherwise `pullimap` issues an IMAP `SEARCH` (or
- extended `SEARCH` on servers advertizing the [`ESEARCH`][RFC 4731]
+ extended `SEARCH` on servers advertising the [`ESEARCH`][RFC 4731]
capability) command to list old messages; if `--idle` is set then
the `SEARCH` command is issued again every 12 hours.
@@ -133,13 +133,15 @@ Valid options are:
`type=imap` and `type=imaps` are respectively used for IMAP and IMAP
over SSL/TLS connections over an INET socket.
`type=tunnel` causes `pullimap` to create an unnamed pair of
- connected sockets for interprocess communication with a *command*
+ connected sockets for inter-process communication with a *command*
instead of opening a network socket.
(Default: `imaps`.)
*host*
-: Server hostname, for `type=imap` and `type=imaps`.
+: Server hostname or IP address, for `type=imap` and `type=imaps`.
+ The value can optionally be enclosed in square brackets to force its
+ interpretation as an IP literal (hence skip name resolution).
(Default: `localhost`.)
*port*
@@ -149,8 +151,8 @@ Valid options are:
*proxy*
-: An optional SOCKS proxy to use for TCP connections to the IMAP
- server (`type=imap` and `type=imaps` only), formatted as
+: Optional SOCKS proxy to use for TCP connections to the IMAP server
+ (`type=imap` and `type=imaps` only), formatted as
`PROTOCOL://[USER:PASSWORD@]PROXYHOST[:PROXYPORT]`.
If `PROXYPORT` is omitted, it is assumed at port 1080.
Only [SOCKSv5][RFC 1928] is supported (with optional
@@ -200,25 +202,39 @@ Valid options are:
*SSL_protocols*
-: A space-separated list of SSL protocols to enable or disable (if
- prefixed with an exclamation mark `!`. Known protocols are `SSLv2`,
- `SSLv3`, `TLSv1`, `TLSv1.1`, `TLSv1.2`, and `TLSv1.3`. Enabling a
- protocol is a short-hand for disabling all other protocols.
- (Default: `!SSLv2 !SSLv3 !TLSv1 !TLSv1.1`, i.e., only enable TLSv1.2
- and above.)
+: Space-separated list of SSL/TLS protocol versions to explicitly
+ enable (or disable if prefixed with an exclamation mark `!`).
+ Potentially known protocols are `SSLv2`, `SSLv3`, `TLSv1`,
+ `TLSv1.1`, `TLSv1.2`, and `TLSv1.3`, depending on the OpenSSL
+ version used.
+ Enabling a protocol is a short-hand for disabling all other
+ protocols.
-*SSL_cipher_list*
+ *DEPRECATED*: Use *SSL_protocol_min* and/or *SSL_protocol_max*
+ instead.
-: The cipher list to send to the server. Although the server
- determines which cipher suite is used, it should take the first
- supported cipher in the list sent by the client. See
- [`ciphers`(1ssl)] for more information.
+*SSL_protocol_min*, *SSL_protocol_max*
+
+: Set minimum resp. maximum SSL/TLS protocol version to use for the
+ connection. Potentially recognized values are `SSLv3`, `TLSv1`,
+ `TLSv1.1`, `TLSv1.2`, and `TLSv1.3`, depending on the OpenSSL
+ version used.
+
+*SSL_cipherlist*, *SSL_ciphersuites*
+
+: Sets the TLSv1.2 and below cipher list resp. TLSv1.3 cipher suites.
+ The combination of these lists is sent to the server, which then
+ determines which cipher to use (normally the first supported one
+ from the list sent by the client). The default suites depend on the
+ OpenSSL version and its configuration, see [`ciphers`(1ssl)] for
+ more information.
*SSL_fingerprint*
-: Fingerprint of the server certificate's Subject Public Key Info, in
- the form `[ALGO$]DIGEST_HEX` where `ALGO` is the used algorithm (by
- default `sha256`).
+: Space-separated list of acceptable fingerprints for the server
+ certificate's Subject Public Key Info, in the form
+ `[ALGO$]DIGEST_HEX` where `ALGO` is the digest algorithm (by default
+ `sha256`).
Attempting to connect to a server with a non-matching certificate
SPKI fingerprint causes `pullimap` to abort the connection during
the SSL/TLS handshake.
@@ -229,25 +245,47 @@ Valid options are:
| openssl pkey -pubin -outform DER \
| openssl dgst -sha256
+ Specifying multiple digest values can be useful in key rollover
+ scenarios and/or when the server supports certificates of different
+ types (for instance a dual-cert RSA/ECDSA setup). In that case the
+ connection is aborted when none of the specified digests matches.
+
*SSL_verify*
-: Whether to verify the server certificate chain.
- Note that using *SSL_fingerprint* to specify the fingerprint of the
- server certificate is an orthogonal authentication measure as it
- ignores the CA chain.
+: Whether to 1/ verify the server certificate chain; and 2/ match its
+ Subject Alternative Name (SAN) or Subject CommonName (CN) against
+ the value of the *host* option.
(Default: `YES`.)
+ Note that using *SSL_fingerprint* to specify the fingerprint of the
+ server certificate provides an independent server authentication
+ measure as it pins directly its key material and ignore its chain of
+ trust.
+
+*SSL_CAfile*
+
+: File containing trusted certificates to use during server
+ certificate verification when `SSL_verify=YES`.
+
+ Trusted CA certificates are loaded from the default system locations
+ unless one (or both) of *SSL_CAfile* or *SSL_CApath* is set.
+
*SSL_CApath*
-: Directory to use for server certificate verification if
+: Directory to use for server certificate verification when
`SSL_verify=YES`.
This directory must be in “hash format”, see [`verify`(1ssl)] for
more information.
-*SSL_CAfile*
+ Trusted CA certificates are loaded from the default system locations
+ unless one (or both) of *SSL_CAfile* or *SSL_CApath* is set.
-: File containing trusted certificates to use during server
- certificate authentication if `SSL_verify=YES`.
+*SSL_hostname*
+
+: Name to use for the TLS SNI (Server Name Indication) extension. The
+ default value is taken from the *host* option when it is a hostname,
+ and to the empty string when it is an IP literal.
+ Setting *SSL_hostname* to the empty string explicitly disables SNI.
Control flow {#control-flow}
============
@@ -303,7 +341,7 @@ In more details, `pullimap` works as follows:
to terminate it gracefully.
3. Issue a `UID STORE` command to mark all retrieved messages (and
- stalled UIDs found in the *statefile* after the eigth byte) as
+ stalled UIDs found in the *statefile* after the eighth byte) as
`\Seen`.
4. Update the *statefile* with the new UIDNEXT value (bytes 5-8).
@@ -372,5 +410,5 @@ Standards
[`fetchmail`(1)]: https://www.fetchmail.info/
[`getmail`(1)]: http://pyropus.ca/software/getmail/
[`write`(2)]: https://man7.org/linux/man-pages/man2/write.2.html
-[`ciphers`(1ssl)]: https://www.openssl.org/docs/manmaster/apps/ciphers.html
-[`verify`(1ssl)]: https://www.openssl.org/docs/manmaster/apps/verify.html
+[`ciphers`(1ssl)]: https://www.openssl.org/docs/manmaster/man1/openssl-ciphers.html
+[`verify`(1ssl)]: https://www.openssl.org/docs/manmaster/man1/openssl-verify.html
diff --git a/doc/template.html b/doc/template.html
index d825cde..c3809ad 100644
--- a/doc/template.html
+++ b/doc/template.html
@@ -14,7 +14,7 @@ $if(keywords)$
<meta name="keywords" content="$for(keywords)$$keywords$$sep$, $endfor$" />
$endif$
<title>$if(title-prefix)$$title-prefix$ – $endif$$pagetitle$</title>
- <style type="text/css">
+ <style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
diff --git a/interimap b/interimap
index 7781571..b4302f9 100755
--- a/interimap
+++ b/interimap
@@ -2,7 +2,7 @@
#----------------------------------------------------------------------
# Fast bidirectional synchronization for QRESYNC-capable IMAP servers
-# Copyright © 2015-2019 Guilhem Moulin <guilhem@fripost.org>
+# Copyright © 2015-2022 Guilhem Moulin <guilhem@fripost.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -22,17 +22,18 @@ use v5.14.2;
use strict;
use warnings;
-our $VERSION = '0.5.2';
+our $VERSION = '0.5.8';
my $NAME = 'interimap';
my $DATABASE_VERSION = 1;
use Getopt::Long qw/:config posix_default no_ignore_case gnu_compat
bundling auto_version/;
use DBI ':sql_types';
use DBD::SQLite::Constants ':file_open';
-use Fcntl qw/F_GETFD F_SETFD FD_CLOEXEC/;
+use Fcntl qw/O_WRONLY O_CREAT O_EXCL F_GETFD F_SETFD FD_CLOEXEC/;
use List::Util 'first';
-use Net::IMAP::InterIMAP 0.0.5 qw/xdg_basedir read_config compact_set/;
+use lib "./lib";
+use Net::IMAP::InterIMAP 0.5.8 qw/xdg_basedir read_config compact_set/;
# Clean up PATH
$ENV{PATH} = join ':', qw{/usr/bin /bin};
@@ -159,6 +160,12 @@ $SIG{TERM} = sub { cleanup(); exit 0; };
# Open (and maybe create) the database
{
+ # don't auto-create in long-lived mode
+ unless ($CONFIG{watch} or -e $DBFILE) {
+ sysopen(my $fh, $DBFILE, O_WRONLY | O_CREAT | O_EXCL, 0600) or die "Can't create $DBFILE: $!";
+ close $fh or warn "close: $!";
+ }
+
my $dbi_data_source = "dbi:SQLite:dbname=".$DBFILE;
my %dbi_attrs = (
AutoCommit => 0,
@@ -166,8 +173,6 @@ $SIG{TERM} = sub { cleanup(); exit 0; };
sqlite_use_immediate_transaction => 1,
sqlite_open_flags => SQLITE_OPEN_READWRITE
);
- # don't auto-create in long-lived mode
- $dbi_attrs{sqlite_open_flags} |= SQLITE_OPEN_CREATE unless defined $CONFIG{watch};
$DBH = DBI::->connect($dbi_data_source, undef, undef, \%dbi_attrs);
$DBH->sqlite_busy_timeout(250);
@@ -286,7 +291,7 @@ sub list_mailboxes($) {
my ($m) = sort keys %$delims;
$imap->{delimiter} = delete $delims->{$m};
} else {
- # didn't get a non-INBOX LIST reply so we need to explicitely query
+ # didn't get a non-INBOX LIST reply so we need to explicitly query
# the hierarchy delimiter
get_delimiter($name, $imap, $ref);
}
diff --git a/interimap.sample b/interimap.sample
index 2a7b8de..36eeb2a 100644
--- a/interimap.sample
+++ b/interimap.sample
@@ -1,4 +1,4 @@
-#database = imap.example.org.db
+#database = imap.example.net.db
# only consider subscribed mailboxes
list-select-opts = SUBSCRIBED
@@ -15,7 +15,7 @@ null-stderr = YES
[remote]
#type = imaps
-host = imap.example.org
+host = imap.example.net
#port = 993
#proxy = socks5h://localhost:9050
username = guilhem
@@ -23,10 +23,8 @@ password = xxxxxxxxxxxxxxxx
#compress = YES
# SSL options
-SSL_CApath = /etc/ssl/certs
#SSL_verify = YES
-#SSL_protocols = !SSLv2 !SSLv3 !TLSv1 !TLSv1.1
-#SSL_cipherlist = EECDH+AESGCM:!MEDIUM:!LOW:!EXP:!aNULL:!eNULL
+#SSL_protocol_min = TLSv1.2
#SSL_fingerprint = sha256$29111aea5d5be7e448bdc5c6e8a9d03bc9221c53c09b1cfbe6f953221e24dda0
# vim:ft=dosini
diff --git a/interimap.service b/interimap.service
index 6d7fa45..6abc874 100644
--- a/interimap.service
+++ b/interimap.service
@@ -1,11 +1,12 @@
[Unit]
Description=Fast bidirectional synchronization for QRESYNC-capable IMAP servers
Documentation=man:interimap(1)
+Documentation=https://guilhem.org/interimap/interimap.1.html
Wants=network-online.target
After=network-online.target
[Service]
-ExecStart=/usr/bin/interimap --watch=60
+ExecStart=@bindir@/interimap --watch=60
RestartSec=10min
Restart=on-failure
diff --git a/interimap@.service b/interimap@.service
index 6957b79..f870065 100644
--- a/interimap@.service
+++ b/interimap@.service
@@ -1,12 +1,13 @@
[Unit]
Description=Fast bidirectional synchronization for QRESYNC-capable IMAP servers (instance %i)
Documentation=man:interimap(1)
+Documentation=https://guilhem.org/interimap/interimap.1.html
PartOf=interimap.service
Wants=network-online.target
After=network-online.target
[Service]
-ExecStart=/usr/bin/interimap --config=%i --watch=60
+ExecStart=@bindir@/interimap --config=%i --watch=60
RestartSec=10min
Restart=on-failure
diff --git a/lib/Net/IMAP/InterIMAP.pm b/lib/Net/IMAP/InterIMAP.pm
index 8b69e12..10162c7 100644
--- a/lib/Net/IMAP/InterIMAP.pm
+++ b/lib/Net/IMAP/InterIMAP.pm
@@ -1,6 +1,6 @@
#----------------------------------------------------------------------
# A minimal IMAP4 client for QRESYNC-capable servers
-# Copyright © 2015-2019 Guilhem Moulin <guilhem@fripost.org>
+# Copyright © 2015-2022 Guilhem Moulin <guilhem@fripost.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -16,7 +16,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#----------------------------------------------------------------------
-package Net::IMAP::InterIMAP v0.0.5;
+package Net::IMAP::InterIMAP v0.5.8;
use v5.20.0;
use warnings;
use strict;
@@ -24,7 +24,7 @@ use strict;
use Compress::Raw::Zlib qw/Z_OK Z_STREAM_END Z_FULL_FLUSH Z_SYNC_FLUSH MAX_WBITS/;
use Config::Tiny ();
use Errno qw/EEXIST EINTR/;
-use Net::SSLeay 1.73 ();
+use Net::SSLeay 1.86_06 ();
use List::Util qw/all first/;
use POSIX ':signal_h';
use Socket qw/SOCK_STREAM SOCK_RAW SOCK_CLOEXEC IPPROTO_TCP SHUT_RDWR
@@ -62,9 +62,13 @@ my %OPTIONS = (
command => qr/\A(\P{Control}+)\z/,
'null-stderr' => qr/\A(YES|NO)\z/i,
compress => qr/\A(YES|NO)\z/i,
- SSL_protocols => qr/\A(!?$RE_SSL_PROTO(?: !?$RE_SSL_PROTO)*)\z/,
- SSL_fingerprint => qr/\A((?:[A-Za-z0-9]+\$)?\p{AHex}+)\z/,
+ SSL_protocols => qr/\A(!?$RE_SSL_PROTO(?: !?$RE_SSL_PROTO)*)\z/, # TODO deprecated, remove in 0.6
+ SSL_protocol_min => qr/\A(\P{Control}+)\z/,
+ SSL_protocol_max => qr/\A(\P{Control}+)\z/,
+ SSL_fingerprint => qr/\A((?:[A-Za-z0-9]+\$)?\p{AHex}+(?: (?:[A-Za-z0-9]+\$)?\p{AHex}+)*)\z/,
SSL_cipherlist => qr/\A(\P{Control}+)\z/,
+ SSL_ciphersuites => qr/\A(\P{Control}*)\z/, # "an empty list is permissible"
+ SSL_hostname => qr/\A(\P{Control}*)\z/,
SSL_verify => qr/\A(YES|NO)\z/i,
SSL_CApath => qr/\A(\P{Control}+)\z/,
SSL_CAfile => qr/\A(\P{Control}+)\z/,
@@ -324,19 +328,19 @@ sub new($%) {
my $pid = fork // $self->panic("fork: $!");
unless ($pid) {
# children
- close($self->{S}) or $self->panic("Can't close: $!");
- open STDIN, '<&', $s or $self->panic("Can't dup: $!");
- open STDOUT, '>&', $s or $self->panic("Can't dup: $!");
+ close($self->{S}) or $self->panic("close: $!");
+ open STDIN, '<&', $s or $self->panic("dup: $!");
+ open STDOUT, '>&', $s or $self->panic("dup: $!");
my $stderr2;
- if ($self->{'null-stderr'} // 0) {
+ if (($self->{'null-stderr'} // 0) and !($self->{debug} // 0)) {
open $stderr2, '>&', *STDERR;
- open STDERR, '>', '/dev/null' or $self->panic("Can't open /dev/null: $!");
+ open STDERR, '>', '/dev/null' or $self->panic("open(/dev/null): $!");
}
my $sigset = POSIX::SigSet::->new(SIGINT);
my $oldsigset = POSIX::SigSet::->new();
- sigprocmask(SIG_BLOCK, $sigset, $oldsigset) // $self->panic("Can't block SIGINT: $!");
+ sigprocmask(SIG_BLOCK, $sigset, $oldsigset) // $self->panic("sigprocmask: $!");
unless (exec $command) {
my $err = $!;
@@ -344,12 +348,12 @@ sub new($%) {
close STDERR;
open STDERR, '>&', $stderr2;
}
- $self->panic("Can't exec: $err");
+ $self->panic("exec: $err");
}
}
# parent
- close($s) or $self->panic("Can't close: $!");
+ close($s) or $self->panic("close: $!");
}
else {
foreach (qw/host port/) {
@@ -359,9 +363,9 @@ sub new($%) {
: $self->_tcp_connect(@$self{qw/host port/});
if (defined $self->{keepalive}) {
setsockopt($self->{S}, Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, 1)
- or $self->fail("Can't setsockopt SO_KEEPALIVE: $!");
+ or $self->fail("setsockopt SO_KEEPALIVE: $!");
setsockopt($self->{S}, Socket::IPPROTO_TCP, Socket::TCP_KEEPIDLE, 60)
- or $self->fail("Can't setsockopt TCP_KEEPIDLE: $!");
+ or $self->fail("setsockopt TCP_KEEPIDLE: $!");
}
}
@@ -1308,7 +1312,8 @@ sub pull_new_messages($$&@) {
# 2^32-1: don't use '*' since the highest UID can be known already
$range .= "$since:4294967295";
- $UIDNEXT = $cache->{UIDNEXT} // $self->panic(); # sanity check
+ $UIDNEXT = $cache->{UIDNEXT} //
+ $self->panic("Unknown UIDNEXT value - non-compliant server?");
$self->fetch($range, "($attrs)", sub($) {
my $mail = shift;
$UIDNEXT = $mail->{UID} + 1 if $UIDNEXT <= $mail->{UID};
@@ -1367,7 +1372,7 @@ sub push_flag_updates($$@) {
$modified->{$uid} //= [ 0, undef ];
} elsif (defined (my $m = $modified->{$uid})) {
# received an untagged FETCH response, remove from the list of pending changes
- # if the flag list was up to date (either implicitely or explicitely)
+ # if the flag list was up to date (either implicitely or explicitly)
if (!defined $m->[1] or $m->[1] eq $flags) {
delete $modified->{$uid};
push @ok, $uid;
@@ -1446,23 +1451,39 @@ my $RE_IPv6 = do {
| (?: (?: $h16 : ){0,6} $h16 )? ::
/x };
+# Parse an IPv4 or IPv6. In list context, return a pair (IP, family),
+# otherwise only the IP. If the argument is not an IP (for instance if
+# it's a hostname), then return (undef, undef) resp. undef. The input
+# can optionaly be enclosed in square brackets which forces its
+# interpretation as an IP literal: an error is raised if it is not the
+# case.
+my $RE_IPv4_anchored = qr/\A($RE_IPv4)\z/;
+my $RE_IPv6_anchored = qr/\A($RE_IPv6)\z/;
+sub _parse_hostip($) {
+ my $v = shift // return;
+ my $literal = $v =~ s/\A\[(.*)\]\z/$1/ ? 1 : 0;
+ my ($ip, $af) = $v =~ $RE_IPv4_anchored ? ($1, AF_INET)
+ : $v =~ $RE_IPv6_anchored ? ($1, AF_INET6)
+ : (undef, undef);
+ die "Invalid IP literal: $v\n" if $literal and !defined($ip);
+ return wantarray ? ($ip, $af) : $ip;
+}
# Opens a TCP socket to the given $host and $port.
sub _tcp_connect($$$) {
my ($self, $host, $port) = @_;
my %hints = (socktype => SOCK_STREAM, protocol => IPPROTO_TCP);
- if ($host =~ qr/\A$RE_IPv4\z/) {
- $hints{family} = AF_INET;
- $hints{flags} |= AI_NUMERICHOST;
- } elsif ($host =~ qr/\A\[($RE_IPv6)\]\z/) {
- $host = $1;
- $hints{family} = AF_INET6;
+ my ($host2, $family) = _parse_hostip($host);
+ if (defined $family) {
+ $hints{family} = $family;
$hints{flags} |= AI_NUMERICHOST;
+ } else {
+ $host2 = $host;
}
- my ($err, @res) = getaddrinfo($host, $port, \%hints);
- $self->fail("Can't getaddrinfo: $err") if $err ne '';
+ my ($err, @res) = getaddrinfo($host2, $port, \%hints);
+ $self->fail("getaddrinfo($host2): $err") if $err ne '';
SOCKETS:
foreach my $ai (@res) {
@@ -1473,9 +1494,9 @@ sub _tcp_connect($$$) {
# https://stackoverflow.com/questions/8284243/how-do-i-set-so-rcvtimeo-on-a-socket-in-perl
my $timeout = pack('l!l!', 30, 0);
setsockopt($s, Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, $timeout)
- or $self->fail("Can't setsockopt SO_RCVTIMEO: $!");
+ or $self->fail("setsockopt SO_RCVTIMEO: $!");
setsockopt($s, Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, $timeout)
- or $self->fail("Can't setsockopt SO_RCVTIMEO: $!");
+ or $self->fail("setsockopt SO_RCVTIMEO: $!");
until (connect($s, $ai->{addr})) {
next if $! == EINTR; # try again if connect(2) was interrupted by a signal
@@ -1492,7 +1513,7 @@ sub _xwrite($$$) {
while ($length > 0) {
my $n = syswrite($_[0], $_[1], $length, $offset);
- $self->fail("Can't write: $!") unless defined $n and $n > 0;
+ $self->fail("write: $!") unless defined $n and $n > 0;
$offset += $n;
$length -= $n;
}
@@ -1504,7 +1525,7 @@ sub _xread($$$) {
my $offset = 0;
my $buf;
while ($length > 0) {
- my $n = sysread($fh, $buf, $length, $offset) // $self->fail("Can't read: $!");
+ my $n = sysread($fh, $buf, $length, $offset) // $self->fail("read: $!");
$self->fail("0 bytes read (got EOF)") unless $n > 0; # EOF
$offset += $n;
$length -= $n;
@@ -1520,7 +1541,7 @@ sub _proxify($$$$) {
$port = getservbyname($port, 'tcp') // $self->fail("Can't getservbyname $port")
unless $port =~ /\A[0-9]+\z/;
- $proxy =~ /\A([A-Za-z0-9]+):\/\/(\P{Control}*\@)?($RE_IPv4|\[$RE_IPv6\]|[^:]+)(:[A-Za-z0-9]+)?\z/
+ $proxy =~ /\A([A-Za-z0-9]+):\/\/(\P{Control}*\@)?([^:]+|\[[^\]]+\])(:[A-Za-z0-9]+)?\z/
or $self->fail("Invalid proxy URI $proxy");
my ($proto, $userpass, $proxyhost, $proxyport) = ($1, $2, $3, $4);
$userpass =~ s/\@\z// if defined $userpass;
@@ -1553,23 +1574,30 @@ sub _proxify($$$$) {
$self->fail('SOCKSv5', 'No acceptable authentication methods');
}
- if ($host !~ /\A(?:$RE_IPv4|\[$RE_IPv6\])\z/ and !$resolv) {
+ my ($hostip, $fam) = _parse_hostip($host);
+ unless (defined($fam) or $resolv) {
# resove the hostname $host locally
my ($err, @res) = getaddrinfo($host, undef, {socktype => SOCK_RAW});
- $self->fail("Can't getaddrinfo: $err") if $err ne '';
- ($host) = first { defined $_ } map {
+ $self->fail("getaddrinfo($host): $err") if $err ne '';
+ my ($addr) = first { defined($_) } map {
my ($err, $ipaddr) = getnameinfo($_->{addr}, NI_NUMERICHOST, NIx_NOSERV);
- $err eq '' ? $ipaddr : undef
+ $err eq '' ? [$ipaddr,$_->{family}] : undef
} @res;
- $self->fail("Can't getnameinfo") unless defined $host;
+ $self->fail("getnameinfo") unless defined $addr;
+ ($hostip, $fam) = @$addr;
}
# send a CONNECT command (CMD 0x01)
- my ($typ, $addr) =
- $host =~ /\A$RE_IPv4\z/ ? (0x01, Socket::inet_pton(AF_INET, $host))
- : ($host =~ /\A\[($RE_IPv6)\]\z/ or $host =~ /\A($RE_IPv6)\z/) ? (0x04, Socket::inet_pton(AF_INET6, $1))
- : (0x03, pack('C',length($host)).$host);
- $self->_xwrite($socket, pack('C4', $v, 0x01, 0x00, $typ).$addr.pack('n', $port));
+ my ($typ, $addr);
+ if (defined $fam) {
+ $typ = $fam == AF_INET ? 0x01 : $fam == AF_INET6 ? 0x04 : $self->panic();
+ $addr = Socket::inet_pton($fam, $hostip);
+ } else {
+ # let the SOCKS server do the resolution
+ $typ = 0x03;
+ $addr = pack('C',length($host)) . $host;
+ }
+ $self->_xwrite($socket, pack('C4', $v, 0x01, 0x00, $typ) . $addr . pack('n', $port));
($v2, my $r, my $rsv, $typ) = unpack('C4', $self->_xread($socket, 4));
$self->fail('SOCKSv5', 'Invalid protocol') unless $v == $v2 and $rsv == 0x00;
@@ -1593,14 +1621,14 @@ sub _proxify($$$$) {
return $socket;
}
else {
- $self->error("Unsupported proxy protocol $proto");
+ $self->fail("Unsupported proxy protocol $proto");
}
}
# $self->_ssl_verify($self, $preverify_ok, $x509_ctx)
# SSL verify callback function, see
-# https://www.openssl.org/docs/manmaster/ssl/SSL_CTX_set_verify.html
+# https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_verify.html
sub _ssl_verify($$$) {
my ($self, $ok, $x509_ctx) = @_;
return 0 unless $x509_ctx; # reject
@@ -1614,7 +1642,7 @@ sub _ssl_verify($$$) {
$self->log(' Subject Name: ', Net::SSLeay::X509_NAME_oneline(Net::SSLeay::X509_get_subject_name($cert)));
}
- $ok = 1 unless $self->{SSL_verify} // 1;
+ $ok = 1 unless $self->{SSL_verify} // die; # safety check, always set
if ($depth == 0 and !exists $self->{_SSL_PEER_VERIFIED}) {
if ($self->{debug}) {
my $algo = 'sha256';
@@ -1624,15 +1652,23 @@ sub _ssl_verify($$$) {
.$algo.'$'.unpack('H*', Net::SSLeay::X509_digest($cert, $type)));
}
- if (defined (my $fpr = $self->{SSL_fingerprint})) {
- (my $algo, $fpr) = $fpr =~ /^([^\$]+)\$(.*)/ ? ($1, $2) : ('sha256', $fpr);
- my $digest = pack 'H*', ($fpr =~ tr/://rd);
+ if (defined (my $fprs = $self->{SSL_fingerprint})) {
+ my $rv = 0;
+ foreach my $fpr (split /\s+/, $fprs) {
+ (my $algo, $fpr) = $fpr =~ /^([^\$]+)\$(.*)/ ? ($1, $2) : ('sha256', $fpr);
+ my $digest = pack 'H*', ($fpr =~ tr/://rd);
- my $type = Net::SSLeay::EVP_get_digestbyname($algo)
- or $self->_ssl_error("Can't find MD value for name '$algo'");
+ my $type = Net::SSLeay::EVP_get_digestbyname($algo)
+ or $self->_ssl_error("Can't find MD value for name '$algo'");
- my $pkey = Net::SSLeay::X509_get_X509_PUBKEY($cert);
- unless (defined $pkey and Net::SSLeay::EVP_Digest($pkey, $type) eq $digest) {
+ my $pkey = Net::SSLeay::X509_get_X509_PUBKEY($cert);
+ if (defined $pkey and Net::SSLeay::EVP_Digest($pkey, $type) eq $digest) {
+ $self->log('Peer certificate matches pinned SPKI digest ', $algo .'$'. $fpr) if $self->{debug};
+ $rv = 1;
+ last;
+ }
+ }
+ unless ($rv) {
$self->warn("Fingerprint doesn't match! MiTM in action?");
$ok = 0;
}
@@ -1643,7 +1679,7 @@ sub _ssl_verify($$$) {
}
my %SSL_proto;
-BEGIN {
+BEGIN { # TODO deprecated, remove in 0.6
sub _append_ssl_proto($$) {
my ($k, $v) = @_;
$SSL_proto{$k} = $v if defined $v;
@@ -1656,82 +1692,141 @@ BEGIN {
_append_ssl_proto( "TLSv1.3", eval { Net::SSLeay::OP_NO_TLSv1_3() } );
}
+# see ssl/ssl_conf.c:protocol_from_string() in the OpenSSL source tree
+my %SSL_protocol_versions = (
+ "SSLv3" => eval { Net::SSLeay::SSL3_VERSION() }
+ , "TLSv1" => eval { Net::SSLeay::TLS1_VERSION() }
+ , "TLSv1.1" => eval { Net::SSLeay::TLS1_1_VERSION() }
+ , "TLSv1.2" => eval { Net::SSLeay::TLS1_2_VERSION() }
+ , "TLSv1.3" => eval { Net::SSLeay::TLS1_3_VERSION() }
+);
+
# $self->_start_ssl($socket)
# Upgrade the $socket to SSL/TLS.
sub _start_ssl($$) {
my ($self, $socket) = @_;
- my $ctx = Net::SSLeay::CTX_new() or $self->panic("Failed to create SSL_CTX $!");
- my $ssl_options = Net::SSLeay::OP_SINGLE_DH_USE() | Net::SSLeay::OP_SINGLE_ECDH_USE();
+ # need OpenSSL 1.1.0 or later for SSL_CTX_set_min_proto_version(3ssl), see
+ # https://www.openssl.org/docs/man1.1.0/man3/SSL_CTX_set_min_proto_version.html
+ $self->panic("SSL/TLS functions require OpenSSL 1.1.0 or later")
+ if Net::SSLeay::OPENSSL_VERSION_NUMBER() < 0x1010000f;
+
+ my $ctx = Net::SSLeay::CTX_new() or $self->panic("SSL_CTX_new(): $!");
+ $self->{SSL_verify} //= 1; # default is to perform certificate verification
if (defined $self->{_OUTBUF} and $self->{_OUTBUF} ne '') {
$self->warn("Truncating non-empty output buffer (unauthenticated response injection?)");
undef $self->{_OUTBUF};
}
- $self->{SSL_protocols} //= q{!SSLv2 !SSLv3 !TLSv1 !TLSv1.1};
- my ($proto_include, $proto_exclude) = (0, 0);
- foreach (split /\s+/, $self->{SSL_protocols}) {
- my $neg = s/^!// ? 1 : 0;
- s/\.0$//;
- ($neg ? $proto_exclude : $proto_include) |= $SSL_proto{$_} // $self->panic("Unknown SSL protocol: $_");
- }
- if ($proto_include != 0) {
- # exclude all protocols except those explictly included
- my $x = 0;
- $x |= $_ foreach values %SSL_proto;
- $x &= ~ $proto_include;
- $proto_exclude |= $x;
- }
- my @proto_exclude = grep { ($proto_exclude & $SSL_proto{$_}) != 0 } keys %SSL_proto;
- $self->log("Disabling SSL protocols: ".join(', ', sort @proto_exclude)) if $self->{debug};
- $ssl_options |= $SSL_proto{$_} foreach @proto_exclude;
+ my $ssl_options = Net::SSLeay::OP_SINGLE_DH_USE() | Net::SSLeay::OP_SINGLE_ECDH_USE();
$ssl_options |= Net::SSLeay::OP_NO_COMPRESSION();
- # https://www.openssl.org/docs/manmaster/ssl/SSL_CTX_set_options.html
+ if (defined $self->{SSL_protocol_min} or defined $self->{SSL_protocol_max}) {
+ my ($min, $max) = @$self{qw/SSL_protocol_min SSL_protocol_max/};
+ if (defined $min) {
+ my $v = $SSL_protocol_versions{$min} // $self->panic("Unknown protocol version: $min");
+ $self->_ssl_error("CTX_set_min_proto_version()") unless Net::SSLeay::CTX_set_min_proto_version($ctx, $v) == 1;
+ $self->log("Minimum SSL/TLS protocol version: ", $min) if $self->{debug};
+ }
+ if (defined $max) {
+ my $v = $SSL_protocol_versions{$max} // $self->panic("Unknown protocol version: $max");
+ $self->_ssl_error("CTX_set_max_proto_version()") unless Net::SSLeay::CTX_set_max_proto_version($ctx, $v) == 1;
+ $self->log("Maximum SSL/TLS protocol version: ", $max) if $self->{debug};
+ }
+ } elsif (defined (my $protos = $self->{SSL_protocols})) { # TODO deprecated, remove in 0.6
+ $self->warn("SSL_protocols is deprecated and will be removed in a future release! " .
+ "Use SSL_protocol_{min,max} instead.");
+ my ($proto_include, $proto_exclude) = (0, 0);
+ foreach (split /\s+/, $protos) {
+ my $neg = s/^!// ? 1 : 0;
+ s/\.0$//;
+ ($neg ? $proto_exclude : $proto_include) |= $SSL_proto{$_} // $self->panic("Unknown SSL protocol: $_");
+ }
+ if ($proto_include != 0) {
+ # exclude all protocols except those explictly included
+ my $x = 0;
+ $x |= $_ foreach values %SSL_proto;
+ $x &= ~ $proto_include;
+ $proto_exclude |= $x;
+ }
+ my @proto_exclude = grep { ($proto_exclude & $SSL_proto{$_}) != 0 } keys %SSL_proto;
+ $self->log("Disabling SSL protocols: ".join(', ', sort @proto_exclude)) if $self->{debug};
+ $ssl_options |= $SSL_proto{$_} foreach @proto_exclude;
+ }
+
+ # https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_options.html
+ # TODO 0.6: move SSL_CTX_set_options() and SSL_CTX_set_mode() before SSL_CTX_set_{min,max}_proto_version()
Net::SSLeay::CTX_set_options($ctx, $ssl_options);
- # https://www.openssl.org/docs/manmaster/ssl/SSL_CTX_set_mode.html
+ # https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_mode.html
Net::SSLeay::CTX_set_mode($ctx,
Net::SSLeay::MODE_ENABLE_PARTIAL_WRITE() |
Net::SSLeay::MODE_ACCEPT_MOVING_WRITE_BUFFER() |
Net::SSLeay::MODE_AUTO_RETRY() | # don't fail SSL_read on renegotiation
Net::SSLeay::MODE_RELEASE_BUFFERS() );
- if (defined (my $ciphers = $self->{SSL_cipherlist})) {
- Net::SSLeay::CTX_set_cipher_list($ctx, $ciphers)
- or $self->_ssl_error("Can't set cipher list");
+ if (defined (my $str = $self->{SSL_cipherlist})) {
+ $self->_ssl_error("SSL_CTX_set_cipher_list()") unless Net::SSLeay::CTX_set_cipher_list($ctx, $str) == 1;
}
+ if (defined (my $str = $self->{SSL_ciphersuites})) {
+ $self->_ssl_error("SSL_CTX_set_ciphersuites()") unless Net::SSLeay::CTX_set_ciphersuites($ctx, $str) == 1;
+ }
+
+ my $vpm = Net::SSLeay::X509_VERIFY_PARAM_new() or $self->_ssl_error("X509_VERIFY_PARAM_new()");
+ my $purpose = Net::SSLeay::X509_PURPOSE_SSL_SERVER();
+ $self->_ssl_error("X509_VERIFY_PARAM_set_purpose()") unless Net::SSLeay::X509_VERIFY_PARAM_set_purpose($vpm, $purpose) == 1;
+ $self->_ssl_error("CTX_set_purpose()") unless Net::SSLeay::CTX_set_purpose($ctx, $purpose) == 1;
- if ($self->{SSL_verify} // 1) {
- # verify the certificate chain
- my ($file, $path) = ($self->{SSL_CAfile} // '', $self->{SSL_CApath} // '');
- if ($file ne '' or $path ne '') {
- Net::SSLeay::CTX_load_verify_locations($ctx, $file, $path)
- or $self->_ssl_error("Can't load verify locations");
+ my $host = $self->{host} // $self->panic();
+ my ($hostip, $hostipfam) = _parse_hostip($host);
+ if ($self->{SSL_verify}) {
+ # verify certificate chain
+ if (defined $self->{SSL_CAfile} or defined $self->{SSL_CApath}) {
+ $self->_ssl_error("SSL_CTX_load_verify_locations()")
+ unless Net::SSLeay::CTX_load_verify_locations($ctx,
+ $self->{SSL_CAfile} // '', $self->{SSL_CApath} // '') == 1;
+ } else {
+ $self->log("Using default locations for trusted CA certificates") if $self->{debug};
+ $self->_ssl_error("SSL_CTX_set_default_verify_paths()")
+ unless Net::SSLeay::CTX_set_default_verify_paths($ctx) == 1;
+ }
+
+ # verify DNS hostname or IP literal
+ if (defined $hostipfam) {
+ my $addr = Socket::inet_pton($hostipfam, $hostip) // $self->panic();
+ $self->_ssl_error("X509_VERIFY_PARAM_set1_ip()")
+ unless Net::SSLeay::X509_VERIFY_PARAM_set1_ip($vpm, $addr) == 1;
+ } else {
+ $self->_ssl_error("X509_VERIFY_PARAM_set1_host()")
+ unless Net::SSLeay::X509_VERIFY_PARAM_set1_host($vpm, $host) == 1;
}
}
else {
Net::SSLeay::CTX_set_verify_depth($ctx, 0);
}
- Net::SSLeay::CTX_set_purpose($ctx, Net::SSLeay::X509_PURPOSE_SSL_SERVER())
- or $self->_ssl_error("Can't set purpose");
Net::SSLeay::CTX_set_verify($ctx, Net::SSLeay::VERIFY_PEER(), sub($$) {$self->_ssl_verify(@_)});
+ $self->_ssl_error("CTX_SSL_set1_param()") unless Net::SSLeay::CTX_set1_param($ctx, $vpm) == 1;
+
+ my $ssl = Net::SSLeay::new($ctx) or $self->fail("SSL_new()");
+ $self->fail("SSL_set_fd()") unless Net::SSLeay::set_fd($ssl, fileno($socket)) == 1;
+
+ # always use 'SSL_hostname' when set, otherwise use 'host' (unless it's an IP)
+ my $servername = $self->{SSL_hostname} // (defined $hostipfam ? "" : $host);
+ if ($servername ne "") {
+ $self->_ssl_error("SSL_set_tlsext_host_name($servername)")
+ unless Net::SSLeay::set_tlsext_host_name($ssl, $servername) == 1;
+ $self->log("Using SNI with name $servername") if $self->{debug};
+ }
- my $ssl = Net::SSLeay::new($ctx) or $self->fail("Can't create new SSL structure");
- Net::SSLeay::set_fd($ssl, fileno $socket) or $self->fail("SSL filehandle association failed");
$self->_ssl_error("Can't initiate TLS/SSL handshake") unless Net::SSLeay::connect($ssl) == 1;
- $self->panic("Couldn't verify") unless $self->{_SSL_PEER_VERIFIED}; # sanity check
+ $self->panic() unless $self->{_SSL_PEER_VERIFIED}; # sanity check
+ $self->panic() if $self->{SSL_verify} and Net::SSLeay::get_verify_result($ssl) != Net::SSLeay::X509_V_OK();
+ Net::SSLeay::X509_VERIFY_PARAM_free($vpm);
if ($self->{debug}) {
- my $v = Net::SSLeay::version($ssl);
- $self->log(sprintf('SSL protocol: %s (0x%x)', ($v == 0x0002 ? 'SSLv2' :
- $v == 0x0300 ? 'SSLv3' :
- $v == 0x0301 ? 'TLSv1' :
- $v == 0x0302 ? 'TLSv1.1' :
- $v == 0x0303 ? 'TLSv1.2' :
- $v == 0x0304 ? 'TLSv1.3' :
- '??'),
- $v));
+ $self->log(sprintf('SSL protocol: %s (0x%x)',
+ , Net::SSLeay::get_version($ssl)
+ , Net::SSLeay::version($ssl)));
$self->log(sprintf('SSL cipher: %s (%d bits)'
, Net::SSLeay::get_cipher($ssl)
, Net::SSLeay::get_cipher_bits($ssl)));
@@ -1766,7 +1861,7 @@ sub _getline($;$) {
$n = sysread($stdout, $buf, $BUFSIZE, 0);
}
- $self->_ssl_error("Can't read: $!") unless defined $n;
+ $self->_ssl_error("read: $!") unless defined $n;
$self->_ssl_error("0 bytes read (got EOF)") unless $n > 0; # EOF
$self->{_OUTRAWCOUNT} += $n;
@@ -1979,7 +2074,7 @@ sub _cmd_flush($;$$) {
my $written = defined $ssl ?
Net::SSLeay::write_partial($ssl, $offset, $length, $self->{_INBUF}) :
syswrite($stdin, $self->{_INBUF}, $length, $offset);
- $self->_ssl_error("Can't write: $!") unless defined $written and $written > 0;
+ $self->_ssl_error("write: $!") unless defined $written and $written > 0;
$offset += $written;
$length -= $written;
diff --git a/pullimap b/pullimap
index 7c9dd9f..d4aebf9 100755
--- a/pullimap
+++ b/pullimap
@@ -2,7 +2,7 @@
#----------------------------------------------------------------------
# Pull mails from an IMAP mailbox and deliver them to an SMTP session
-# Copyright © 2016-2019 Guilhem Moulin <guilhem@fripost.org>
+# Copyright © 2016-2022 Guilhem Moulin <guilhem@fripost.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -22,7 +22,7 @@ use v5.20.2;
use strict;
use warnings;
-our $VERSION = '0.5.2';
+our $VERSION = '0.5.8';
my $NAME = 'pullimap';
use Errno 'EINTR';
@@ -31,7 +31,8 @@ use Getopt::Long qw/:config posix_default no_ignore_case gnu_getopt auto_version
use List::Util 'first';
use Socket qw/PF_INET PF_INET6 SOCK_STREAM IPPROTO_TCP/;
-use Net::IMAP::InterIMAP 0.0.5 qw/xdg_basedir read_config compact_set/;
+use lib "./lib";
+use Net::IMAP::InterIMAP 0.5.8 qw/xdg_basedir read_config compact_set/;
# Clean up PATH
$ENV{PATH} = join ':', qw{/usr/bin /bin};
@@ -104,7 +105,7 @@ do {
# Read a UID (32-bits integer) from the statefile, or undef if we're at
# the end of the statefile
sub readUID() {
- my $n = sysread($STATE, my $buf, 4) // die "Can't sysread: $!";
+ my $n = sysread($STATE, my $buf, 4) // die "read: $!";
return if $n == 0; # EOF
# file length is a multiple of 4 bytes, and we always read 4 bytes at a time
die "Corrupted state file!" if $n != 4;
@@ -117,7 +118,7 @@ sub writeUID($) {
my $offset = 0;
for ( my $offset = 0
; $offset < 4
- ; $offset += syswrite($STATE, $uid, 4-$offset, $offset) // die "Can't syswrite: $!"
+ ; $offset += syswrite($STATE, $uid, 4-$offset, $offset) // die "write: $!"
) {}
# no need to sync (or flush) since $STATE is opened with O_DSYNC
}
@@ -333,11 +334,11 @@ sub pull(;$) {
undef $SMTP;
# update the statefile
- my $p = sysseek($STATE, 4, SEEK_SET) // die "Can't seek: $!";
+ my $p = sysseek($STATE, 4, SEEK_SET) // die "seek: $!";
die "Couldn't seek to 4" unless $p == 4; # safety check
my ($uidnext) = $IMAP->get_cache('UIDNEXT');
writeUID($uidnext);
- truncate($STATE, 8) // die "Can't truncate";
+ truncate($STATE, 8) // die "truncate: $!";
}
do {
diff --git a/pullimap.sample b/pullimap.sample
index 136d3d4..f1a66f9 100644
--- a/pullimap.sample
+++ b/pullimap.sample
@@ -4,10 +4,8 @@ deliver-method = smtp:[127.0.0.1]:25
purge-after = 90
# SSL options
-SSL_CApath = /etc/ssl/certs
#SSL_verify = YES
-#SSL_protocols = !SSLv2 !SSLv3 !TLSv1 !TLSv1.1
-#SSL_cipherlist = EECDH+AESGCM:!MEDIUM:!LOW:!EXP:!aNULL:!eNULL
+#SSL_protocol_min = TLSv1.2
[private]
#type = imaps
diff --git a/pullimap@.service b/pullimap@.service
index a9ce09a..4f9518b 100644
--- a/pullimap@.service
+++ b/pullimap@.service
@@ -1,11 +1,12 @@
[Unit]
Description=Pull mails from an IMAP mailbox and deliver them to an SMTP session (instance %i)
Documentation=man:pullimap(1)
+Documentation=https://guilhem.org/interimap/pullimap.1.html
Wants=network-online.target
After=network-online.target
[Service]
-ExecStart=/usr/bin/pullimap --idle %i
+ExecStart=@bindir@/pullimap --idle %i
RestartSec=2min
Restart=always
diff --git a/tests/auth-login/t b/tests/auth-login/t
index 7fd83d5..38e2028 100644
--- a/tests/auth-login/t
+++ b/tests/auth-login/t
@@ -9,4 +9,4 @@ grep -Fx "remote: C: xxx LOGIN [REDACTED]" <"$STDERR" || error
check_mailbox_status "INBOX"
-# vim: set filetype=sh :
+# vim: set filetype=bash :
diff --git a/tests/auth-logindisabled/remote.conf b/tests/auth-logindisabled/remote.conf
index 1f02afe..990dcbd 100644
--- a/tests/auth-logindisabled/remote.conf
+++ b/tests/auth-logindisabled/remote.conf
@@ -1,4 +1,6 @@
!include conf.d/imapd.conf
# trick dovecot into treating local connections as insecure
-imap_capability = +LOGINDISABLED
+imap_capability {
+ LOGINDISABLED = yes
+}
diff --git a/tests/auth-logindisabled/t b/tests/auth-logindisabled/t
index 0bcd0d6..402355f 100644
--- a/tests/auth-logindisabled/t
+++ b/tests/auth-logindisabled/t
@@ -13,4 +13,4 @@ grep -Fx "LOGINDISABLED" <"$TMPDIR/capabilities" || error
grep -Fx "remote: ERROR: Logins are disabled." <"$STDERR" || error
! grep -E "^remote: C: .* (AUTHENTICATE|LOGIN) " <"$STDERR" || error
-# vim: set filetype=sh :
+# vim: set filetype=bash :
diff --git a/tests/auth-noplaintext/t b/tests/auth-noplaintext/t
index 11d7d4d..862bc8d 100644
--- a/tests/auth-noplaintext/t
+++ b/tests/auth-noplaintext/t
@@ -12,4 +12,4 @@ tr " " "\\n" <"$TMPDIR/capability" >"$TMPDIR/capabilities"
grep -Fx "remote: ERROR: Server did not advertise STARTTLS capability." <"$STDERR" || error
! grep -E "^remote: C: .* (AUTHENTICATE|LOGIN) " <"$STDERR" || error
-# vim: set filetype=sh :
+# vim: set filetype=bash :
diff --git a/tests/auth-sasl-plain-no-ir/t b/tests/auth-sasl-plain-no-ir/t
index 17aa9e6..f236ac7 100644
--- a/tests/auth-sasl-plain-no-ir/t
+++ b/tests/auth-sasl-plain-no-ir/t
@@ -23,4 +23,4 @@ xcgrep "$n" -E "^remote(\(INBOX\))?: C: [0-9]+ APPEND INBOX .* \{[0-9]+\}$" <"$S
check_mailbox_status "INBOX"
-# vim: set filetype=sh :
+# vim: set filetype=bash :
diff --git a/tests/auth-sasl-plain/t b/tests/auth-sasl-plain/t
index 68f71a9..c5cb024 100644
--- a/tests/auth-sasl-plain/t
+++ b/tests/auth-sasl-plain/t
@@ -9,4 +9,4 @@ grep -Fx "remote: C: xxx AUTHENTICATE PLAIN [REDACTED]" <"$STDERR" || error
check_mailbox_status "INBOX"
-# vim: set filetype=sh :
+# vim: set filetype=bash :
diff --git a/tests/certs/.gitignore b/tests/certs/.gitignore
new file mode 100644
index 0000000..8b2d0ad
--- /dev/null
+++ b/tests/certs/.gitignore
@@ -0,0 +1,4 @@
+!/generate
+/*.key
+/*.crt
+/*.pem
diff --git a/tests/certs/generate b/tests/certs/generate
new file mode 100755
index 0000000..f449764
--- /dev/null
+++ b/tests/certs/generate
@@ -0,0 +1,57 @@
+#!/bin/sh
+
+set -ue
+PATH="/usr/bin:/bin"
+export PATH
+
+BASEDIR="$(dirname -- "$0")"
+OU="InterIMAP test suite"
+cd "$BASEDIR"
+
+OPENSSL_CONF="./openssl.cnf"
+export OPENSSL_CONF
+
+cadir="$(mktemp --tmpdir --directory)"
+trap 'rm -rf -- "$cadir"' EXIT INT TERM
+genpkey() {
+ local key="$1"
+ shift
+ openssl genpkey -out "$key" "$@" 2>&1
+}
+
+# generate CA (we intentionally throw away the private key and serial
+# file to avoid reuse)
+genpkey "$cadir/ca.key" -algorithm RSA
+openssl req -new -x509 -rand /dev/urandom \
+ -subj "/OU=$OU/CN=Fake Root CA" \
+ -addext subjectKeyIdentifier="hash" \
+ -addext authorityKeyIdentifier="keyid:always,issuer" \
+ -addext basicConstraints="critical,CA:TRUE" \
+ -key "$cadir/ca.key" -out ./ca.crt
+
+SERIAL=1
+new() {
+ local key="$1" cn="$2"
+ openssl req -new -rand /dev/urandom -key "$key" \
+ -subj "/OU=$OU/CN=$cn" ${3+-addext subjectAltName="$3"} \
+ -out "$cadir/new.csr"
+ cat >"$cadir/new-ext.cnf" <<-EOF
+ basicConstraints = critical, CA:FALSE
+ keyUsage = critical, digitalSignature, keyEncipherment
+ extendedKeyUsage = critical, serverAuth
+ EOF
+ if [ -n "${3+x}" ]; then
+ printf "subjectAltName = %s\\n" "$3" >>"$cadir/new-ext.cnf"
+ fi
+ openssl x509 -req -in "$cadir/new.csr" -CA ./ca.crt -CAkey "$cadir/ca.key" \
+ -CAserial "$cadir/ca.srl" -CAcreateserial -extfile "$cadir/new-ext.cnf" 2>&1
+}
+
+genpkey ./dovecot.rsa.key -algorithm RSA
+new ./dovecot.rsa.key "localhost" "DNS:localhost,DNS:ip6-localhost,IP:127.0.0.1,IP:::1" >./dovecot.rsa.crt
+
+genpkey ./dovecot.ecdsa.key -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -pkeyopt ec_param_enc:named_curve
+new ./dovecot.ecdsa.key "localhost" >./dovecot.ecdsa.crt
+
+genpkey ./dovecot.rsa2.key -algorithm RSA
+new ./dovecot.rsa2.key "imap.example.net" "DNS:imap.example.net,DNS:localhost" >./dovecot.rsa2.crt
diff --git a/tests/certs/openssl.cnf b/tests/certs/openssl.cnf
new file mode 100644
index 0000000..b1af7b8
--- /dev/null
+++ b/tests/certs/openssl.cnf
@@ -0,0 +1,4 @@
+[ req ]
+distinguished_name = req_distinguished_name
+
+[ req_distinguished_name ]
diff --git a/tests/compress/t b/tests/compress/t
index 5625761..0a04a73 100644
--- a/tests/compress/t
+++ b/tests/compress/t
@@ -16,4 +16,4 @@ echo "compress = no" >>"$XDG_CONFIG_HOME/interimap/config"
interimap --debug || error
! grep -E "^remote: C: [^[:blank:]]+ COMPRESS DEFLATE$" <"$STDERR" || error
-# vim: set filetype=sh :
+# vim: set filetype=bash :
diff --git a/tests/condstore/t b/tests/condstore/t
index 1963b2b..303287e 100644
--- a/tests/condstore/t
+++ b/tests/condstore/t
@@ -28,7 +28,7 @@ while [ $(date +%s) -le $timer ]; do
u="$(shuf -n1 -e "local" "remote")"
f="$(shuf -n1 -e "${FLAGS[@]}")"
seqs="$(shuf -n$((N/8)) -i1-$N)" # trigger changes on 1/8 of all messages
- doveadm -u "$u" flags "$a" "$f" mailbox "INBOX" "${seqs//$'\n'/,}"
+ doveadm -u "$u" flags "$a" --no-userdb-lookup "$f" mailbox "INBOX" "${seqs//$'\n'/,}"
sleep "0.0$(shuf -n1 -i10-99)" # 10 to 99ms
done
sleep 5
@@ -39,7 +39,7 @@ trap - EXIT INT TERM
# make sure the list of uids for a given tag match
flagged_uids() {
local u="$1" f="$2"
- doveadm -u "$u" search mailbox "INBOX" keyword "$f" | cut -d" " -f2 | sort -n
+ doveadm -u "$u" search --no-userdb-lookup mailbox "INBOX" keyword "$f" | cut -d" " -f2 | sort -n
}
for f in "${FLAGS[@]}"; do
diff --label="local/$f" --label="remote/$f" -u -- \
@@ -47,4 +47,4 @@ for f in "${FLAGS[@]}"; do
error "UID list differs for keyword '$f'"
done
-# vim: set filetype=sh :
+# vim: set filetype=bash :
diff --git a/tests/snippets/dovecot/dhparams.pem b/tests/config/dovecot/dhparams.pem
index 7734d2a..7734d2a 100644
--- a/tests/snippets/dovecot/dhparams.pem
+++ b/tests/config/dovecot/dhparams.pem
diff --git a/tests/snippets/dovecot/imapd.conf b/tests/config/dovecot/imapd.conf
index 2b26451..6f456f6 100644
--- a/tests/snippets/dovecot/imapd.conf
+++ b/tests/config/dovecot/imapd.conf
@@ -1,8 +1,5 @@
-protocols = $protocols imap
-
-mail_plugins = $mail_plugins zlib
-protocol imap {
- mail_plugins = $mail_plugins imap_zlib
+protocols {
+ imap = yes
}
service imap-login {
diff --git a/tests/snippets/dovecot/interimap-required-capabilities.conf b/tests/config/dovecot/interimap-required-capabilities.conf
index 10dd8e1..10dd8e1 100644
--- a/tests/snippets/dovecot/interimap-required-capabilities.conf
+++ b/tests/config/dovecot/interimap-required-capabilities.conf
diff --git a/tests/snippets/dovecot/lmtpd.conf b/tests/config/dovecot/lmtpd.conf
index 6aa8365..23cdc1e 100644
--- a/tests/snippets/dovecot/lmtpd.conf
+++ b/tests/config/dovecot/lmtpd.conf
@@ -1,4 +1,6 @@
-protocols = $protocols lmtp
+protocols {
+ lmtp = yes
+}
service lmtp {
inet_listener lmtp {
diff --git a/tests/config/dovecot/ssl.conf b/tests/config/dovecot/ssl.conf
new file mode 100644
index 0000000..fd119cd
--- /dev/null
+++ b/tests/config/dovecot/ssl.conf
@@ -0,0 +1,6 @@
+ssl = required
+ssl_server_cert_file = dovecot.rsa.crt
+ssl_server_key_file = dovecot.rsa.key
+ssl_server_dh_file = dhparams.pem
+ssl_min_protocol = TLSv1.2
+ssl_cipher_list = DEFAULT@SECLEVEL=2
diff --git a/tests/db-exclusive-lock/t b/tests/db-exclusive-lock/t
index c2df4b5..0d0badb 100644
--- a/tests/db-exclusive-lock/t
+++ b/tests/db-exclusive-lock/t
@@ -10,7 +10,7 @@ sleep .5
# subsequent runs fail as we can't acquire the exclusive lock
! interimap || error
-grep -Fx "DBD::SQLite::db do failed: database is locked at ./interimap line 176." <"$STDERR" \
- || error "Is \$DBH->do(\"PRAGMA locking_mode = EXCLUSIVE\"); at line 176?"
+grep -Ex "DBD::SQLite::db do failed: database is locked at (\S+/)?interimap line 181\." <"$STDERR" \
+ || error "Is \$DBH->do(\"PRAGMA locking_mode = EXCLUSIVE\"); at line 181?"
-# vim: set filetype=sh :
+# vim: set filetype=bash :
diff --git a/tests/db-migration-0-1-foreign-key-violation/t b/tests/db-migration-0-1-foreign-key-violation/t
index 35e5be5..fa4afb4 100644
--- a/tests/db-migration-0-1-foreign-key-violation/t
+++ b/tests/db-migration-0-1-foreign-key-violation/t
@@ -18,4 +18,4 @@ EOF
grep -Fx "Upgrading database version from 0" <"$STDERR" || error "DB upgrade not attempted"
grep -Fx "database: ERROR: Broken referential integrity! Refusing to commit changes." <"$STDERR" || error "DB upgrade successful despite broken refint"
-# vim: set filetype=sh :
+# vim: set filetype=bash :
diff --git a/tests/db-no-create--watch/t b/tests/db-no-create--watch/t
index a8ea07e..3097558 100644
--- a/tests/db-no-create--watch/t
+++ b/tests/db-no-create--watch/t
@@ -1,6 +1,6 @@
! interimap --watch=60 || error
-grep -Ex "DBI connect\(.*\) failed: unable to open database file at \./interimap line 172\." <"$STDERR" || error
+grep -Ex "DBI connect\(.*\) failed: unable to open database file at (\S+/)?interimap line 177\." <"$STDERR" || error
test \! -e "$XDG_DATA_HOME/interimap/remote.db" || error
-# vim: set filetype=sh :
+# vim: set filetype=bash :
diff --git a/tests/db-upgrade-0-1-delim-mismatch/t b/tests/db-upgrade-0-1-delim-mismatch/t
index d133437..c15927c 100644
--- a/tests/db-upgrade-0-1-delim-mismatch/t
+++ b/tests/db-upgrade-0-1-delim-mismatch/t
@@ -4,4 +4,4 @@ sqlite3 "$XDG_DATA_HOME/interimap/remote.db" <"$TESTDIR/before.sql" || error "Co
grep -Fx 'ERROR: Local and remote hierachy delimiters differ (local "\"", remote "^"), refusing to update table `mailboxes`.' <"$STDERR" || error
-# vim: set filetype=sh :
+# vim: set filetype=bash :
diff --git a/tests/db-upgrade-0-1/t b/tests/db-upgrade-0-1/t
index 088008e..2d0546c 100644
--- a/tests/db-upgrade-0-1/t
+++ b/tests/db-upgrade-0-1/t
@@ -1,6 +1,6 @@
# create the mailboxes from the database
-doveadm -u "local" mailbox create "a.b1.c1" "a.b1.c2" "a.b2.c" "a2"
-doveadm -u "remote" mailbox create "a.b1.c1" "a.b1.c2" "a.b2.c" "a2"
+doveadm -u "local" mailbox create --no-userdb-lookup "a.b1.c1" "a.b1.c2" "a.b2.c" "a2"
+doveadm -u "remote" mailbox create --no-userdb-lookup "a.b1.c1" "a.b1.c2" "a.b2.c" "a2"
# import an existing non-migrated database
sqlite3 "$XDG_DATA_HOME/interimap/remote.db" <"$TESTDIR/before.sql" || error "Couldn't import DB"
@@ -31,4 +31,4 @@ diff -u --label="a/dump.sql" --label="b/dump.sql" \
"$TMPDIR/dump-expected.sql" "$TMPDIR/dump.sql" \
|| error "DB dumps differ"
-# vim: set filetype=sh :
+# vim: set filetype=bash :
diff --git a/tests/delete/t b/tests/delete/t
index c38d4d3..3baca2c 100644
--- a/tests/delete/t
+++ b/tests/delete/t
@@ -1,4 +1,4 @@
-doveadm -u "local" mailbox create "foo.bar" "foo.bar.baz"
+doveadm -u "local" mailbox create --no-userdb-lookup "foo.bar" "foo.bar.baz"
for m in "foo.bar" "foo.bar.baz" "INBOX"; do
sample_message | deliver -u "local" -- -m "$m"
@@ -48,14 +48,14 @@ EOF
diff -u --label="a/dump.sql" --label="b/dump.sql" \
"$TMPDIR/dump.sql" "$TMPDIR/dump2.sql" || error "SQL dumps differ"
-! doveadm -u "local" mailbox status uidvalidity "foo.bar" # gone
- doveadm -u "remote" mailbox status uidvalidity "foo^bar"
+! doveadm -u "local" mailbox status --no-userdb-lookup uidvalidity "foo.bar" # gone
+ doveadm -u "remote" mailbox status --no-userdb-lookup uidvalidity "foo^bar"
# now delete from the remote server and the database
interimap --delete "foo.bar"
-! doveadm -u "local" mailbox status uidvalidity "foo.bar"
-! doveadm -u "remote" mailbox status uidvalidity "foo^bar"
+! doveadm -u "local" mailbox status --no-userdb-lookup uidvalidity "foo.bar"
+! doveadm -u "remote" mailbox status --no-userdb-lookup uidvalidity "foo^bar"
sqlite3 "$XDG_DATA_HOME/interimap/remote.db" >"$TMPDIR/mailboxes2.sql" <<-EOF
SELECT idx, mailbox FROM mailboxes ORDER BY idx
@@ -80,8 +80,8 @@ step_done
step_start "\\Noinferiors mailbox"
interimap --delete "foo.bar.baz"
-! doveadm -u "local" mailbox status uidvalidity "foo.bar.baz"
-! doveadm -u "remote" mailbox status uidvalidity "foo^bar^baz"
+! doveadm -u "local" mailbox status --no-userdb-lookup uidvalidity "foo.bar.baz"
+! doveadm -u "remote" mailbox status --no-userdb-lookup uidvalidity "foo^bar^baz"
sqlite3 "$XDG_DATA_HOME/interimap/remote.db" >"$TMPDIR/count" <<-EOF
SELECT COUNT(*) FROM mailboxes
@@ -92,4 +92,4 @@ check_mailbox_list
check_mailboxes_status "INBOX"
step_done
-# vim: set filetype=sh :
+# vim: set filetype=bash :
diff --git a/tests/delimiter-change/t b/tests/delimiter-change/t
index 3f96953..72229de 100644
--- a/tests/delimiter-change/t
+++ b/tests/delimiter-change/t
@@ -1,5 +1,5 @@
# create and populate some mailboxes
-doveadm -u "local" mailbox create -- "foo" "foo.bar" "baz"
+doveadm -u "local" mailbox create --no-userdb-lookup -- "foo" "foo.bar" "baz"
run() {
local i m u s1="$1" s2="$2" m2
@@ -31,7 +31,7 @@ sed -ri "s,^(\\s*separator\\s*)=.*,separator = .," "$HOME_remote/.dovecot/config
run "." "."
# ensure there were no duplicates
-n="$(doveadm -u "local" search all | wc -l)"
+n="$(doveadm -u "local" search --no-userdb-lookup all | wc -l)"
[ "$n" -eq 64 ] || error "$n != 64"
-# vim: set filetype=sh :
+# vim: set filetype=bash :
diff --git a/tests/ignore-mailbox/t b/tests/ignore-mailbox/t
index f90227c..1b09a1d 100644
--- a/tests/ignore-mailbox/t
+++ b/tests/ignore-mailbox/t
@@ -1,5 +1,5 @@
-doveadm -u "local" mailbox create "foo" -- "-virtual"
-doveadm -u "remote" mailbox create "bar" -- "virtual-"
+doveadm -u "local" mailbox create --no-userdb-lookup "foo" -- "-virtual"
+doveadm -u "remote" mailbox create --no-userdb-lookup "bar" -- "virtual-"
interimap_init
check_mailbox_list
@@ -15,8 +15,8 @@ for ((i = 0; i < 16; i++)); do
done
# create new mailboxes matching 'ignore-mailbox'
-doveadm -u "local" mailbox create "virtual" "virtual.foo"
-doveadm -u "remote" mailbox create "virtual^bar"
+doveadm -u "local" mailbox create --no-userdb-lookup "virtual" "virtual.foo"
+doveadm -u "remote" mailbox create --no-userdb-lookup "virtual^bar"
for n in $(seq 1 "$(shuf -n1 -i1-8)"); do
sample_message | deliver -u "local" -- -m "virtual"
sample_message | deliver -u "local" -- -m "virtual.foo"
@@ -59,4 +59,4 @@ EOF
check_mailboxes_status "virtual" "virtual.bar" || error
-# vim: set filetype=sh :
+# vim: set filetype=bash :
diff --git a/tests/list b/tests/interimap.list
index db77f50..559daed 100644
--- a/tests/list
+++ b/tests/interimap.list
@@ -51,13 +51,13 @@ split-set Split large sets to avoid extra-long command lines
tls SSL/TLS handshake
... tls-verify-peer
tls-pin-fingerprint pubkey fingerprint pinning
+ tls-rsa+ecdsa pubkey fingerprint pinning for dual-cert RSA+ECDSA
+ tls-sni TLS servername extension (SNI)
tls-protocols force TLS protocol versions
+ tls-ciphers force TLS cipher list/suites
. Live synchronization (60s)
sync-live local/remote simulation
sync-live-crippled local/remote simulation (crippled remote)
sync-live-tls local/remote simulation (TLS remote)
sync-live-multi local/remote1+remote2+remote3 simulation (3 local namespaces)
-
-. pullimap
- ... pullimap
diff --git a/tests/largeint/t b/tests/largeint/t
index b0877d5..84d21ae 100644
--- a/tests/largeint/t
+++ b/tests/largeint/t
@@ -1,15 +1,15 @@
-doveadm -u "local" mailbox create "foo" "bar" "baz"
-doveadm -u "remote" mailbox create "foo" "bar" "baz"
+doveadm -u "local" mailbox create --no-userdb-lookup "foo" "bar" "baz"
+doveadm -u "remote" mailbox create --no-userdb-lookup "foo" "bar" "baz"
-doveadm -u "local" mailbox update --uid-validity 1 "INBOX"
-doveadm -u "local" mailbox update --uid-validity 2147483647 "foo" # 2^31-1
-doveadm -u "local" mailbox update --uid-validity 2147483648 "bar" # 2^31
-doveadm -u "local" mailbox update --uid-validity 4294967295 "baz" # 2^32-1
+doveadm -u "local" mailbox update --no-userdb-lookup --uid-validity 1 "INBOX"
+doveadm -u "local" mailbox update --no-userdb-lookup --uid-validity 2147483647 "foo" # 2^31-1
+doveadm -u "local" mailbox update --no-userdb-lookup --uid-validity 2147483648 "bar" # 2^31
+doveadm -u "local" mailbox update --no-userdb-lookup --uid-validity 4294967295 "baz" # 2^32-1
-doveadm -u "remote" mailbox update --uid-validity 4294967295 "INBOX" # 2^32-1
-doveadm -u "remote" mailbox update --uid-validity 2147483648 "foo" # 2^31
-doveadm -u "remote" mailbox update --uid-validity 2147483647 "bar" # 2^31-1
-doveadm -u "remote" mailbox update --uid-validity 1 "baz" #
+doveadm -u "remote" mailbox update --no-userdb-lookup --uid-validity 4294967295 "INBOX" # 2^32-1
+doveadm -u "remote" mailbox update --no-userdb-lookup --uid-validity 2147483648 "foo" # 2^31
+doveadm -u "remote" mailbox update --no-userdb-lookup --uid-validity 2147483647 "bar" # 2^31-1
+doveadm -u "remote" mailbox update --no-userdb-lookup --uid-validity 1 "baz" #
run() {
local u m i
@@ -25,15 +25,17 @@ run() {
run
# raise UIDNEXT AND HIGHESTMODSEQ close to the max values (resp. 2^32-1 och 2^63-1)
-doveadm -u "local" mailbox update --min-next-uid 2147483647 --min-highest-modseq 9223372036854775807 "INBOX" # 2^31-1, 2^63-1
-doveadm -u "local" mailbox update --min-next-uid 2147483647 --min-highest-modseq 9223372036854775807 "foo" # 2^31-1, 2^63-1
-doveadm -u "local" mailbox update --min-next-uid 2147483648 --min-highest-modseq 9223372036854775808 "bar" # 2^31, 2^63
-doveadm -u "local" mailbox update --min-next-uid 2147483648 --min-highest-modseq 9223372036854775808 "baz" # 2^31, 2^63
+# XXX as of dovecot 2.4 --min-highest-modseq rejects values higher than INT64_MAX instead of UINT64_MAX,
+# cf. str_parse_int64()
+doveadm -u "local" mailbox update --no-userdb-lookup --min-next-uid 2147483647 --min-highest-modseq 9223372036854775807 "INBOX" # 2^31-1, 2^63-1
+doveadm -u "local" mailbox update --no-userdb-lookup --min-next-uid 2147483647 --min-highest-modseq 9223372036854775807 "foo" # 2^31-1, 2^63-1
+doveadm -u "local" mailbox update --no-userdb-lookup --min-next-uid 2147483648 --min-highest-modseq 9223372036854775807 "bar" # 2^31, 2^63-1
+doveadm -u "local" mailbox update --no-userdb-lookup --min-next-uid 2147483648 --min-highest-modseq 9223372036854775807 "baz" # 2^31, 2^63-1
-doveadm -u "remote" mailbox update --min-next-uid 4294967168 --min-highest-modseq 18446744073709551488 "INBOX" # 2^32-128, 2^64-128
-doveadm -u "remote" mailbox update --min-next-uid 2147483776 --min-highest-modseq 9223372036854775936 "foo" # 2^31+128, 2^63+128
-doveadm -u "remote" mailbox update --min-next-uid 2147483648 --min-highest-modseq 9223372036854775808 "bar" # 2^31, 2^63
+doveadm -u "remote" mailbox update --no-userdb-lookup --min-next-uid 4294967168 --min-highest-modseq 9223372036854775807 "INBOX" # 2^32-128, 2^63-1
+doveadm -u "remote" mailbox update --no-userdb-lookup --min-next-uid 2147483776 --min-highest-modseq 9223372036854775807 "foo" # 2^31+128, 2^63-1
+doveadm -u "remote" mailbox update --no-userdb-lookup --min-next-uid 2147483648 --min-highest-modseq 9223372036854775807 "bar" # 2^31, 2^63-1
run
-# vim: set filetype=sh :
+# vim: set filetype=bash :
diff --git a/tests/list-mailbox/t b/tests/list-mailbox/t
index e905537..6c19705 100644
--- a/tests/list-mailbox/t
+++ b/tests/list-mailbox/t
@@ -1,6 +1,6 @@
# create and populate some mailboxes locally
declare -a MAILBOXES=( "foo" "foo bar" "f\\\"o!o.bar" "f.o.o" )
-doveadm -u "local" mailbox create -- "${MAILBOXES[@]}" "foobad" "baz" "INBOX"
+doveadm -u "local" mailbox create --no-userdb-lookup -- "${MAILBOXES[@]}" "foobad" "baz" "INBOX"
for ((i = 0; i < 32; i++)); do
m="$(shuf -n1 -e -- "${MAILBOXES[@]}" "foobad" "baz" "INBOX")"
sample_message | deliver -u "local" -- -m "$m"
@@ -54,4 +54,4 @@ for v in '""' '"f o o""bar"' '"f o o" "bar" "baz\" x'; do
grep -xF "Invalid value for list-mailbox: $v" <"$STDERR"
done
-# vim: set filetype=sh :
+# vim: set filetype=bash :
diff --git a/tests/list-reference/t b/tests/list-reference/t
index a2cc9c7..e8d38e6 100644
--- a/tests/list-reference/t
+++ b/tests/list-reference/t
@@ -1,6 +1,6 @@
# create and populate some mailboxes in and out the respective list references
-doveadm -u "local" mailbox create "foo" "foobar" "foo/bar/baz" "foo/baz" "bar" "bar/baz"
-doveadm -u "remote" mailbox create "foo" "foobaz" "foo/baz" "foo\\baz" "bar\\baz" "bar\\!"
+doveadm -u "local" mailbox create --no-userdb-lookup "foo" "foobar" "foo/bar/baz" "foo/baz" "bar" "bar/baz"
+doveadm -u "remote" mailbox create --no-userdb-lookup "foo" "foobaz" "foo\\bar" "foo\\baz" "bar\\baz" "bar\\!"
populate() {
local i
@@ -8,7 +8,7 @@ populate() {
m="$(shuf -n1 -e -- "foo" "foobar" "foo/bar/baz" "foo/baz" "bar" "bar/baz")"
sample_message | deliver -u "local" -- -m "$m"
- m="$(shuf -n1 -e -- "foo" "foobar" "foo/baz" "foo\\baz" "bar\\baz" "bar\\!")"
+ m="$(shuf -n1 -e -- "foo" "foobaz" "foo\\bar" "foo\\baz" "bar\\baz" "bar\\!")"
sample_message | deliver -u "remote" -- -m "$m"
done
}
@@ -44,4 +44,4 @@ verify
! doveadm -u "local" mailbox status uidvalidity "foobaz" || error
! doveadm -u "remote" mailbox status uidvalidity "foobar" || error
-# vim: set filetype=sh :
+# vim: set filetype=bash :
diff --git a/tests/list-select-opts/t b/tests/list-select-opts/t
index 98acb43..570098c 100644
--- a/tests/list-select-opts/t
+++ b/tests/list-select-opts/t
@@ -1,5 +1,5 @@
-doveadm -u "local" mailbox create -s "INBOX" "foo.bar"
-doveadm -u "remote" mailbox create -s "INBOX" "bar"
+doveadm -u "local" mailbox create --no-userdb-lookup -s "INBOX" "foo.bar"
+doveadm -u "remote" mailbox create --no-userdb-lookup -s "INBOX" "bar"
interimap_init
check_mailbox_list
@@ -15,8 +15,8 @@ for ((i = 0; i < 16; i++)); do
done
# create new unsubscribed mailboxes
-doveadm -u "local" mailbox create "foo"
-doveadm -u "remote" mailbox create "baz"
+doveadm -u "local" mailbox create --no-userdb-lookup "foo"
+doveadm -u "remote" mailbox create --no-userdb-lookup "baz"
for ((i = 0; i < 8; i++)); do
u="$(shuf -n1 -e "local" "remote")" # choose target at random
@@ -37,12 +37,12 @@ diff -u --label="a/mailboxes.sql" --label="b/mailboxes.sql" \
check_mailboxes_status "INBOX" "foo.bar" "bar"
# double check the unsubscribed mailboxes weren't copied
-! doveadm -u "remote" mailbox status uidvalidity "foo" || error
-! doveadm -u "local" mailbox status uidvalidity "baz" || error
+! doveadm -u "remote" mailbox status --no-userdb-lookup uidvalidity "foo" || error
+! doveadm -u "local" mailbox status --no-userdb-lookup uidvalidity "baz" || error
# reconcile when subcribed
-doveadm -u "local" mailbox subscribe "foo"
-doveadm -u "remote" mailbox subscribe "baz"
+doveadm -u "local" mailbox subscribe --no-userdb-lookup "foo"
+doveadm -u "remote" mailbox subscribe --no-userdb-lookup "baz"
interimap || error
grep -Fx "database: Created mailbox foo" <"$STDERR" || error
@@ -53,4 +53,4 @@ grep -Fx "remote: Created mailbox foo" <"$STDERR" || error
check_mailbox_list
check_mailboxes_status "INBOX" "foo" "foo.bar" "bar" "baz"
-# vim: set filetype=sh :
+# vim: set filetype=bash :
diff --git a/tests/preauth-plaintext/imapd b/tests/preauth-plaintext/imapd
index 8f3ac30..bf2ed72 100755
--- a/tests/preauth-plaintext/imapd
+++ b/tests/preauth-plaintext/imapd
@@ -18,7 +18,7 @@ while (1) {
die "accept: $!";
};
- # minimum CAPABILITY list, see tests/snippets/dovecot/interimap-required-capabilities.conf
+ # minimum CAPABILITY list, see tests/config/dovecot/interimap-required-capabilities.conf
$conn->printflush("* PREAUTH [CAPABILITY IMAP4rev1 ENABLE UIDPLUS LIST-EXTENDED QRESYNC LIST-STATUS] IMAP4rev1 Server\r\n");
my $x;
@@ -39,6 +39,6 @@ while (1) {
END {
if (defined $S) {
shutdown($S, SHUT_RDWR) or warn "shutdown: $!";
- close($S) or print STDERR "Can't close: $!\n";
+ close($S) or print STDERR "close: $!\n";
}
}
diff --git a/tests/preauth-plaintext/t b/tests/preauth-plaintext/t
index 427d57b..2f3071f 100644
--- a/tests/preauth-plaintext/t
+++ b/tests/preauth-plaintext/t
@@ -10,10 +10,10 @@ grep -Fx 'remote: ERROR: PREAUTH greeting on plaintext connection? MiTM in actio
! grep '^remote: C: ' <"$STDERR" || error "wrote command in MiTM'ed PREAUTH connection!"
-# Ignore the warning when STARTTLS is explicitely disabled
+# Ignore the warning when STARTTLS is explicitly disabled
echo "STARTTLS = NO" >>"$XDG_CONFIG_HOME/interimap/config"
interimap --debug || true
grep -Fx "remote: S: * STATUS INBOX (UIDNEXT 1 UIDVALIDITY 1 HIGHESTMODSEQ 1)" <"$STDERR" || error
-# vim: set filetype=sh :
+# vim: set filetype=bash :
diff --git a/tests/pullimap.list b/tests/pullimap.list
new file mode 100644
index 0000000..f4304b9
--- /dev/null
+++ b/tests/pullimap.list
@@ -0,0 +1,2 @@
+. pullimap
+ ... pullimap
diff --git a/tests/pullimap/t b/tests/pullimap/t
index 0dfe634..1c57f8d 100644
--- a/tests/pullimap/t
+++ b/tests/pullimap/t
@@ -6,14 +6,32 @@ step_start "\`pullimap --idle\` refuses to create the state file"
! pullimap --idle "remote" || error
step_done
+step_start "\`pullimap\` creates statefile with mode 0600"
+pullimap "remote" || error
+if ! st="$(stat -c"%#a" -- "$XDG_DATA_HOME/pullimap/remote")" || [ "$st" != "0600" ]; then
+ error "$XDG_DATA_HOME/pullimap/remote has mode $st != 0600"
+fi
+step_done
+
+step_start "\`pullimap\` locks its statefile"
+pullimap --idle "remote" & PID=$!
+trap "ptree_abort $PID" EXIT INT TERM
+# wait a short while so we have time to lock the database (ugly and racy...)
+sleep .5
+! pullimap "remote" || error
+grep -F "Can't lock $XDG_DATA_HOME/pullimap/remote: Resource temporarily unavailable at " <"$STDERR" || error
+ptree_abort $PID
+trap - EXIT INT TERM
+step_done
+
# compare mailboxes (can't compare the RFC 3501 TEXT as the LMTPd inconditionally
# adds a Return-Path: header -- and also Delivered-To: and Received: to by default)
list_mails_sha256() {
local u="$1" guid uid
local fields="body date.sent imap.bodystructure imap.envelope"
while read guid uid; do
- doveadm -u "$u" -f "flow" fetch "$fields" mailbox-guid "$guid" uid "$uid" | sha256sum
- done < <(doveadm -u "$u" search mailbox "$MAILBOX") | sort -f
+ doveadm -u "$u" -f "flow" fetch --no-userdb-lookup "$fields" mailbox-guid "$guid" uid "$uid" | sha256sum
+ done < <(doveadm -u "$u" search mailbox --no-userdb-lookup "$MAILBOX") | sort -f
}
check() {
diff -u --label="local/mails" --label="remote/mails" \
@@ -73,7 +91,7 @@ step_done
# make sure remote UIDs are 11-bytes long
-doveadm -u "remote" mailbox update --min-next-uid 1000000000 "$MAILBOX"
+doveadm -u "remote" mailbox update --no-userdb-lookup --min-next-uid 1000000000 "$MAILBOX"
# Add some messages and sync
step_start "Fetching messages"
@@ -91,7 +109,7 @@ for ((i = 0; i < N; i+=2)); do
done
for ((i = 0; i < N; i+=2)); do
# expunge every other message
- doveadm -u "remote" expunge mailbox "$MAILBOX" $((N-i+32+7))
+ doveadm -u "remote" expunge --no-userdb-lookup mailbox "$MAILBOX" $((N-i+32+7))
sample_message | deliver -u "remote" -- -m "$MAILBOX"
done
@@ -99,34 +117,36 @@ pullimap "remote" || error
check
# count unseen remote messages
-doveadm -u "remote" search mailbox "$MAILBOX" unseen >"$TMPDIR/unseen"
+doveadm -u "remote" search --no-userdb-lookup mailbox "$MAILBOX" unseen >"$TMPDIR/unseen"
[ ! -s "$TMPDIR/unseen" ] || error "\\Unseen messages left"
step_done
-step_start "--idle (${TIMEOUT}s)"
+if [ $TIMEOUT -gt 0 ]; then
+ step_start "--idle (${TIMEOUT}s)"
-pullimap --idle "remote" & PID=$!
-trap "ptree_abort $PID" EXIT INT TERM
+ pullimap --idle "remote" & PID=$!
+ trap "ptree_abort $PID" EXIT INT TERM
+
+ timer=$(( $(date +%s) + TIMEOUT ))
+ while [ $(date +%s) -le $timer ]; do
+ n="$(shuf -n1 -i1-5)"
+ for (( i=0; i < n; i++)); do
+ sample_message | deliver -u "remote" -- -m "$MAILBOX"
+ done
-timer=$(( $(date +%s) + TIMEOUT ))
-while [ $(date +%s) -le $timer ]; do
- n="$(shuf -n1 -i1-5)"
- for (( i=0; i < n; i++)); do
- sample_message | deliver -u "remote" -- -m "$MAILBOX"
+ s=$(shuf -n1 -i1-1500)
+ [ $s -ge 1000 ] && s="$(printf "1.%03d" $((s-1000)))" || s="$(printf "0.%03d" $s)"
+ sleep "$s"
done
- s=$(shuf -n1 -i1-1500)
- [ $s -ge 1000 ] && s="$(printf "1.%03d" $((s-1000)))" || s="$(printf "0.%03d" $s)"
- sleep "$s"
-done
+ sleep 5
+ ptree_abort $PID
+ trap - EXIT INT TERM
-sleep 5
-ptree_abort $PID
-trap - EXIT INT TERM
-
-check
-step_done
+ check
+ step_done
+fi
step_start "Purging"
@@ -136,8 +156,8 @@ for ((i = 0; i < 32; i++)); do
done
pullimap "remote"
-doveadm -u "remote" search mailbox "$MAILBOX" all >"$TMPDIR/messages"
+doveadm -u "remote" search --no-userdb-lookup mailbox "$MAILBOX" all >"$TMPDIR/messages"
[ ! -s "$TMPDIR/messages" ] || error "messages left"
step_done
-# vim: set filetype=sh :
+# vim: set filetype=bash :
diff --git a/tests/rename-exists-db/t b/tests/rename-exists-db/t
index cb6cfcd..2ee8d77 100644
--- a/tests/rename-exists-db/t
+++ b/tests/rename-exists-db/t
@@ -1,14 +1,14 @@
-doveadm -u "local" mailbox create "root.from" "root.from.child" "t.o"
-doveadm -u "remote" mailbox create "root\\from" "root\\from\\child" "t\\o"
+doveadm -u "local" mailbox create --no-userdb-lookup "root.from" "root.from.child" "t.o"
+doveadm -u "remote" mailbox create --no-userdb-lookup "root\\from" "root\\from\\child" "t\\o"
interimap_init
check_mailbox_list
# delete a mailbox on both servers but leave it in the database, then try to use it as target for --rename
-doveadm -u "local" mailbox delete "t.o"
-doveadm -u "remote" mailbox delete "t\\o"
+doveadm -u "local" mailbox delete --no-userdb-lookup "t.o"
+doveadm -u "remote" mailbox delete --no-userdb-lookup "t\\o"
! interimap --rename "root.from" "t.o" || error
grep -Fx 'database: ERROR: Mailbox t.o exists. Run `interimap --target=database --delete t.o` to delete.' <"$STDERR" || error
-# vim: set filetype=sh :
+# vim: set filetype=bash :
diff --git a/tests/rename-exists-local/t b/tests/rename-exists-local/t
index 190f49a..907b14a 100644
--- a/tests/rename-exists-local/t
+++ b/tests/rename-exists-local/t
@@ -1,13 +1,13 @@
-doveadm -u "local" mailbox create "root.from" "root.from.child" "t.o"
-doveadm -u "remote" mailbox create "root\\from" "root\\from\\child"
+doveadm -u "local" mailbox create --no-userdb-lookup "root.from" "root.from.child" "t.o"
+doveadm -u "remote" mailbox create --no-userdb-lookup "root\\from" "root\\from\\child"
interimap_init
check_mailbox_list
# delete a mailbox on the remote server, then try to use it as target for --rename
-doveadm -u "remote" mailbox delete "t\\o"
+doveadm -u "remote" mailbox delete --no-userdb-lookup "t\\o"
! interimap --rename "root.from" "t.o" || error
grep -Fx 'local: ERROR: Mailbox t.o exists. Run `interimap --target=local --delete t.o` to delete.' <"$STDERR" || error
-# vim: set filetype=sh :
+# vim: set filetype=bash :
diff --git a/tests/rename-exists-remote/t b/tests/rename-exists-remote/t
index be16a12..5cb4336 100644
--- a/tests/rename-exists-remote/t
+++ b/tests/rename-exists-remote/t
@@ -1,13 +1,13 @@
-doveadm -u "local" mailbox create "root.from" "root.from.child" "t.o"
-doveadm -u "remote" mailbox create "root\\from" "root\\from\\child" "t\\o"
+doveadm -u "local" mailbox create --no-userdb-lookup "root.from" "root.from.child" "t.o"
+doveadm -u "remote" mailbox create --no-userdb-lookup "root\\from" "root\\from\\child" "t\\o"
interimap_init
check_mailbox_list
# delete a mailbox on the local server, then try to use it as target for --rename
-doveadm -u "local" mailbox delete "t.o"
+doveadm -u "local" mailbox delete --no-userdb-lookup "t.o"
! interimap --rename "root.from" "t.o" || error
grep -Fx 'remote: ERROR: Mailbox t\o exists. Run `interimap --target=remote --delete t.o` to delete.' <"$STDERR" || remote
-# vim: set filetype=sh :
+# vim: set filetype=bash :
diff --git a/tests/rename-inferiors/t b/tests/rename-inferiors/t
index 9267e6f..3715680 100644
--- a/tests/rename-inferiors/t
+++ b/tests/rename-inferiors/t
@@ -1,5 +1,5 @@
-doveadm -u "local" mailbox create "root.from" "root.from.child" "root.from.child2" "root.from.child.grandchild"
-doveadm -u "remote" mailbox create "root^sibbling" "root^sibbling^grandchild" "root2"
+doveadm -u "local" mailbox create --no-userdb-lookup "root.from" "root.from.child" "root.from.child2" "root.from.child.grandchild"
+doveadm -u "remote" mailbox create --no-userdb-lookup "root^sibbling" "root^sibbling^grandchild" "root2"
for m in "root.from" "root.from.child" "root.from.child2" "root.from.child.grandchild" "INBOX"; do
sample_message | deliver -u "local" -- -m "$m"
@@ -97,4 +97,4 @@ check_mailbox_list
check_mailboxes_status "from.root" "from.root.child" "from.root.child2" "from.root.child.grandchild" \
"newroot.sibbling" "newroot.sibbling.grandchild" "root2" "INBOX"
-# vim: set filetype=sh :
+# vim: set filetype=bash :
diff --git a/tests/rename-simple/t b/tests/rename-simple/t
index 6ebee9a..82682fb 100644
--- a/tests/rename-simple/t
+++ b/tests/rename-simple/t
@@ -1,4 +1,4 @@
-doveadm -u "local" mailbox create "foo"
+doveadm -u "local" mailbox create --no-userdb-lookup "foo"
sample_message | deliver -u "local" -- -m "INBOX"
sample_message | deliver -u "remote" -- -m "INBOX"
@@ -58,4 +58,4 @@ grep -Fx "database: Created mailbox INBOX" <"$STDERR"
check_mailbox_list
check_mailboxes_status "INBOX" "bar" "baz"
-# vim: set filetype=sh :
+# vim: set filetype=bash :
diff --git a/tests/repair/t b/tests/repair/t
index 6b205ea..bdad2e3 100644
--- a/tests/repair/t
+++ b/tests/repair/t
@@ -1,6 +1,6 @@
# create some mailboxes and populate them
-doveadm -u "local" mailbox create "foo.bar"
-doveadm -u "remote" mailbox create "foo~bar" "baz"
+doveadm -u "local" mailbox create --no-userdb-lookup "foo.bar"
+doveadm -u "remote" mailbox create --no-userdb-lookup "foo~bar" "baz"
for ((i = 0; i < 8; i++)); do
sample_message | deliver -u "local" -- -m "foo.bar"
sample_message | deliver -u "remote" -- -m "foo~bar"
@@ -15,12 +15,12 @@ check_mailboxes_status "foo.bar" "baz" "INBOX"
# make more changes (flag updates, new massages, deletions)
sample_message | deliver -u "remote" -- -m "INBOX"
-doveadm -u "local" expunge mailbox "baz" 1:10
-doveadm -u "remote" expunge mailbox "baz" "$(seq -s"," 1 2 32),$(seq -s"," 40 2 64)"
-doveadm -u "local" expunge mailbox "foo.bar" 2,3,5:7,10
-doveadm -u "remote" expunge mailbox "foo~bar" 4,5,7,10
-doveadm -u "local" flags add "\\Answered" mailbox "foo.bar" 2,3,5:7,10
-doveadm -u "remote" flags add "\\Seen" mailbox "foo~bar" 4,5,7
+doveadm -u "local" expunge --no-userdb-lookup mailbox "baz" 1:10
+doveadm -u "remote" expunge --no-userdb-lookup mailbox "baz" "$(seq -s"," 1 2 32),$(seq -s"," 40 2 64)"
+doveadm -u "local" expunge --no-userdb-lookup mailbox "foo.bar" 2,3,5:7,10
+doveadm -u "remote" expunge --no-userdb-lookup mailbox "foo~bar" 4,5,7,10
+doveadm -u "local" flags add --no-userdb-lookup "\\Answered" mailbox "foo.bar" 2,3,5:7,10
+doveadm -u "remote" flags add --no-userdb-lookup "\\Seen" mailbox "foo~bar" 4,5,7
# spoof HIGHESTMODSEQ value in the database to make it look that we recorded the new changes already
spoof() {
@@ -28,7 +28,7 @@ spoof() {
shift
while [ $# -gt 0 ]; do
[ "$1" = "local" ] && m="foo.bar" || m="$(printf "%s" "foo.bar" | tr "." "~")"
- v="$(doveadm -u "$1" -f flow mailbox status "${k,,[A-Z]}" "$m" | sed 's/.*=//')"
+ v="$(doveadm -u "$1" -f flow mailbox status --no-userdb-lookup "${k,,[A-Z]}" "$m" | sed 's/.*=//')"
sqlite3 "$XDG_DATA_HOME/interimap/remote.db" <<-EOF
UPDATE \`$1\` SET $k = $v
WHERE idx = (SELECT idx FROM mailboxes WHERE mailbox = x'$hex');
@@ -41,8 +41,8 @@ spoof HIGHESTMODSEQ "local" "remote"
sqlite3 "$XDG_DATA_HOME/interimap/remote.db" >"$TMPDIR/dump.sql" <<-EOF
.dump
EOF
-doveadm -u "local" mailbox status "all" "foo.bar" >"$TMPDIR/foo-bar.status.local"
-doveadm -u "remote" mailbox status "all" "foo~bar" >"$TMPDIR/foo-bar.status.remote"
+doveadm -u "local" mailbox status --no-userdb-lookup "all" "foo.bar" >"$TMPDIR/foo-bar.status.local"
+doveadm -u "remote" mailbox status --no-userdb-lookup "all" "foo~bar" >"$TMPDIR/foo-bar.status.remote"
# verify that without --repair interimap does nothing due to the spoofed HIGHESTMODSEQ values
@@ -51,8 +51,8 @@ interimap "foo.bar" || error
sqlite3 "$XDG_DATA_HOME/interimap/remote.db" >"$TMPDIR/dump2.sql" <<-EOF
.dump
EOF
-doveadm -u "local" mailbox status all "foo.bar" >"$TMPDIR/foo-bar.status2.local"
-doveadm -u "remote" mailbox status all "foo~bar" >"$TMPDIR/foo-bar.status2.remote"
+doveadm -u "local" mailbox status --no-userdb-lookup all "foo.bar" >"$TMPDIR/foo-bar.status2.local"
+doveadm -u "remote" mailbox status --no-userdb-lookup all "foo~bar" >"$TMPDIR/foo-bar.status2.remote"
diff -u --label="a/dump.sql" --label="b/dump.sql" "$TMPDIR/dump.sql" "$TMPDIR/dump2.sql"
diff -u --label="a/foo_bar.local" --label="a/foo_bar.local" "$TMPDIR/foo-bar.status.local" "$TMPDIR/foo-bar.status2.local"
diff -u --label="a/foo_bar.remote" --label="a/foo_bar.remote" "$TMPDIR/foo-bar.status.remote" "$TMPDIR/foo-bar.status2.remote"
@@ -104,4 +104,4 @@ check_mailboxes_status "baz" "foo.bar"
interimap || error
check_mailboxes_status "baz" "foo.bar" "INBOX"
-# vim: set filetype=sh :
+# vim: set filetype=bash :
diff --git a/tests/resume/t b/tests/resume/t
index a281ef3..da80557 100644
--- a/tests/resume/t
+++ b/tests/resume/t
@@ -1,5 +1,5 @@
# create and populate a bunch of mailboxes
-doveadm -u "local" mailbox create "foo" "foo.bar" "baz"
+doveadm -u "local" mailbox create --no-userdb-lookup "foo" "foo.bar" "baz"
for ((i = 0; i < 8; i++)); do
sample_message | deliver -u "local" -- -m "foo"
sample_message | deliver -u "local" -- -m "foo.bar"
@@ -24,17 +24,17 @@ set_uidnext() {
}
# spoof "foo"'s UIDVALIDITY and UIDNEXT values
-uidvalidity="$(doveadm -u "local" -f flow mailbox status uidvalidity "foo" | sed 's/.*=//')"
+uidvalidity="$(doveadm -u "local" -f flow mailbox status --no-userdb-lookup uidvalidity "foo" | sed 's/.*=//')"
[ $uidvalidity -eq 4294967295 ] && uidvalidity2=1 || uidvalidity2=$((uidvalidity+1))
-doveadm -u "local" mailbox update --uid-validity "$uidvalidity2" "foo"
+doveadm -u "local" mailbox update --no-userdb-lookup --uid-validity "$uidvalidity2" "foo"
set_uidnext "local" "$(printf "%s" "foo" | xxd -ps)" 1
# verify that interimap chokes on the UIDVALIDITY change without doing any changes
sqlite3 "$XDG_DATA_HOME/interimap/remote.db" >"$TMPDIR/dump.sql" <<-EOF
.dump
EOF
-doveadm -u "local" mailbox status "all" "foo" >"$TMPDIR/foo.local"
-doveadm -u "remote" mailbox status "all" "foo" >"$TMPDIR/foo.remote"
+doveadm -u "local" mailbox status --no-userdb-lookup "all" "foo" >"$TMPDIR/foo.local"
+doveadm -u "remote" mailbox status --no-userdb-lookup "all" "foo" >"$TMPDIR/foo.remote"
! interimap || error
grep -Fx "Resuming interrupted sync for foo" <"$STDERR"
@@ -43,8 +43,8 @@ grep -Fx "local(foo): ERROR: UIDVALIDITY changed! ($uidvalidity2 != $uidvalidity
sqlite3 "$XDG_DATA_HOME/interimap/remote.db" >"$TMPDIR/dump2.sql" <<-EOF
.dump
EOF
-doveadm -u "local" mailbox status "all" "foo" >"$TMPDIR/foo.local2"
-doveadm -u "remote" mailbox status "all" "foo" >"$TMPDIR/foo.remote2"
+doveadm -u "local" mailbox status --no-userdb-lookup "all" "foo" >"$TMPDIR/foo.local2"
+doveadm -u "remote" mailbox status --no-userdb-lookup "all" "foo" >"$TMPDIR/foo.remote2"
diff -u --label="a/dump.sql" --label="b/dump.sql" "$TMPDIR/dump2.sql" "$TMPDIR/dump.sql"
diff -u --label="a/foo.local" --label="b/foo.remote" "$TMPDIR/foo.local" "$TMPDIR/foo.local2"
@@ -57,12 +57,12 @@ set_uidnext "remote" "$(printf "%s" "INBOX" | xxd -ps)" 2
set_uidnext "remote" "$(printf "%s\\0%s" "foo" "bar" | xxd -ps)" 0
# set some flags and remove some messages for UIDs >2
-doveadm -u "local" flags add "\\Seen" mailbox "INBOX" 6,7
-doveadm -u "remote" flags add "\\Deleted" mailbox "INBOX" 6,8
+doveadm -u "local" flags add --no-userdb-lookup "\\Seen" mailbox "INBOX" 6,7
+doveadm -u "remote" flags add --no-userdb-lookup "\\Deleted" mailbox "INBOX" 6,8
-doveadm -u "local" expunge mailbox "INBOX" 4,5
-doveadm -u "remote" expunge mailbox "INBOX" 3,4
-doveadm -u "remote" expunge mailbox "foo~bar" 5
+doveadm -u "local" expunge --no-userdb-lookup mailbox "INBOX" 4,5
+doveadm -u "remote" expunge --no-userdb-lookup mailbox "INBOX" 3,4
+doveadm -u "remote" expunge --no-userdb-lookup mailbox "foo~bar" 5
# add new messages
sample_message | deliver -u "local" -- -m "foo.bar"
@@ -95,4 +95,4 @@ diff -u --label="a/count" --label="b/count" "$TMPDIR/count" - <<-EOF
9
EOF
-# vim: set filetype=sh :
+# vim: set filetype=bash :
diff --git a/tests/run b/tests/run
index 8164524..c5614ee 100755
--- a/tests/run
+++ b/tests/run
@@ -35,6 +35,15 @@ if [ ! -d "$TESTDIR" ]; then
exit 1
fi
+# cleanup environment
+unset OPENSSL_CONF SSL_CERT_FILE SSL_CERT_DIR
+
+if [ -z "${INTERIMAP_PATH+x}" ]; then
+ INTERIMAP_PATH="./"
+elif [ -n "$INTERIMAP_PATH" ]; then
+ INTERIMAP_PATH="${INTERIMAP_PATH%/}/"
+fi
+
ROOTDIR="$(mktemp --tmpdir="${TMPDIR:-/dev/shm}" --directory "$1.XXXXXXXXXX")"
declare -a DOVECOT_SERVER=()
trap cleanup EXIT INT TERM
@@ -88,12 +97,15 @@ prepare() {
mkdir -pm0700 -- "$home/.dovecot"
cat >"$home/.dovecot/config" <<-EOF
+ dovecot_config_version = 2.4.0
+ dovecot_storage_version = 2.4.0
log_path = $HOME_local/mail.log
mail_home = $home
- mail_location = dbox:~/inbox:LAYOUT=index
+ mail_driver = sdbox
+ mail_path = ~/inbox
mailbox_list_index = yes
ssl = no
- listen = 127.0.0.1, ::1
+ listen = 127.0.0.1, 127.0.1.1, ::1
namespace inbox {
inbox = yes
}
@@ -101,12 +113,15 @@ prepare() {
if [ -f "$TESTDIR/$u.conf" ] || [ -L "$TESTDIR/$u.conf" ]; then
cat >>"$home/.dovecot/config" <"$TESTDIR/$u.conf"
fi
- cp -aT -- "$BASEDIR/snippets/dovecot" "$home/.dovecot/conf.d"
+ cp -aT -- "$BASEDIR/config/dovecot" "$home/.dovecot/conf.d"
+ cp -at "$home/.dovecot/conf.d" -- "$BASEDIR/certs/ca.crt" "$BASEDIR/certs"/dovecot.*
proto="$(env -i "${ENVIRON[@]}" doveconf -c "$home/.dovecot/config" -h protocols)"
if [ -n "$proto" ]; then
cat >>"$home/.dovecot/config" <<-EOF
- # https://wiki.dovecot.org/HowTo/Rootless
+ # https://doc.dovecot.org/latest/core/config/rootless.html#rootless-installation
+ dovecot_config_version = 2.4.0
+ dovecot_storage_version = 2.4.0
base_dir = $home/.dovecot/run
default_internal_user = $(id -un)
default_internal_group = $(id -gn)
@@ -122,13 +137,14 @@ prepare() {
chroot =
}
- passdb {
- args = scheme=PLAIN username_format=%u $home/.dovecot/users
+ passdb passwd-file {
driver = passwd-file
+ default_password_scheme = plain
+ passwd_file_path = $home/.dovecot/users
}
- userdb {
- args = username_format=%u $home/.dovecot/users
+ userdb passwd-file {
driver = passwd-file
+ passwd_file_path = $home/.dovecot/users
}
EOF
@@ -202,23 +218,32 @@ prepare
interimap() { _interimap_cmd "interimap" "$@"; }
pullimap() { _interimap_cmd "pullimap" "$@"; }
_interimap_cmd() {
- declare -a ENVIRON=()
+ declare -a ENVIRON=() args=()
local script="$1" rv=0
shift
environ_set "local"
- env -i "${ENVIRON[@]}" perl -I./lib -T "./$script" "$@" 2>"$STDERR" || rv=$?
+ [ -z "${OPENSSL_CONF+x}" ] || ENVIRON+=( OPENSSL_CONF="$OPENSSL_CONF" )
+ [ -z "${SSL_CERT_FILE+x}" ] || ENVIRON+=( SSL_CERT_FILE="$SSL_CERT_FILE" )
+ [ -z "${SSL_CERT_DIR+x}" ] || ENVIRON+=( SSL_CERT_DIR="$SSL_CERT_DIR" )
+ [ -z "${INTERIMAP_I:+x}" ] || args+=( perl -I"$INTERIMAP_I" -T )
+ args+=( "$INTERIMAP_PATH$script" "$@" )
+ #printf "I: Running \`%s\`\\n" "${args[*]}" >&3
+ env -i "${ENVIRON[@]}" "${args[@]}" 2>"$STDERR" || rv=$?
cat <"$STDERR" >&2
return $rv
}
interimap_init() {
local u="${1-remote}"
- local db="$XDG_DATA_HOME/interimap/$u.db"
+ local db="$XDG_DATA_HOME/interimap/$u.db" st
local cfg="config${u#remote}"
test \! -e "$db" || error "Database already exists" 1
interimap --config "$cfg" || error "Couldn't initialize interimap" 1
test -f "$db" || error "Database is still missing" 1
grep -Fx "Creating new schema in database file $db" <"$STDERR" || error "DB wasn't created" 1
+ if ! st="$(stat -c"%#a" -- "$db")" || [ "$st" != "0600" ]; then
+ error "$db has mode $st != 0600" 1
+ fi
}
doveadm() {
if [ $# -le 2 ] || [ "$1" != "-u" ]; then
@@ -333,9 +358,9 @@ check_mailbox_status2() {
check_mailbox_status_values "$u" "$rmailbox" $rUIDVALIDITY $rUIDNEXT $rHIGHESTMODSEQ $MESSAGES
local a b
- a="$(doveadm -u "local" -f "flow" mailbox status "messages unseen vsize" -- "$lmailbox" | \
+ a="$(doveadm -u "local" -f "flow" mailbox status --no-userdb-lookup "messages unseen vsize" -- "$lmailbox" | \
sed -nr '/.*\s+(\w+=[0-9]+\s+\w+=[0-9]+\s+\w+=[0-9]+)$/ {s//\1/p;q}')"
- b="$(doveadm -u "$u" -f "flow" mailbox status "messages unseen vsize" -- "$rmailbox" | \
+ b="$(doveadm -u "$u" -f "flow" mailbox status --no-userdb-lookup "messages unseen vsize" -- "$rmailbox" | \
sed -nr '/.*\s+(\w+=[0-9]+\s+\w+=[0-9]+\s+\w+=[0-9]+)$/ {s//\1/p;q}')"
if [ "$a" != "$b" ]; then
echo "Mailbox $lmailbox status differs: \"$a\" != \"$b\"" >&2
@@ -344,7 +369,7 @@ check_mailbox_status2() {
}
check_mailbox_status_values() {
local user="$1" mailbox="$2" UIDVALIDITY="$3" UIDNEXT="$4" HIGHESTMODSEQ="$5" MESSAGES="$6" x xs v k
- xs="$(doveadm -u "$user" -f "flow" mailbox status "uidvalidity uidnext highestmodseq messages" -- "$mailbox" | \
+ xs="$(doveadm -u "$user" -f "flow" mailbox status --no-userdb-lookup "uidvalidity uidnext highestmodseq messages" -- "$mailbox" | \
sed -nr '/.*\s+(\w+=[0-9]+\s+\w+=[0-9]+\s+\w+=[0-9]+\s+\w+=[0-9]+)$/ {s//\1/p;q}')"
[ -n "$xs" ] || exit 1
for x in $xs; do
@@ -392,12 +417,12 @@ check_mailbox_list() {
done
fi
- mapfile -t lmailboxes < <( doveadm -u "local" mailbox list $sub -- "${lmailboxes[@]}" )
+ mapfile -t lmailboxes < <( doveadm -u "local" mailbox list --no-userdb-lookup $sub -- "${lmailboxes[@]}" )
for ((i = 0; i < ${#lmailboxes[@]}; i++)); do
lmailboxes[i]="${lmailboxes[i]#"$lprefix"}"
done
- mapfile -t rmailboxes < <( doveadm -u "remote" mailbox list $sub -- "${rmailboxes[@]}" )
+ mapfile -t rmailboxes < <( doveadm -u "remote" mailbox list --no-userdb-lookup $sub -- "${rmailboxes[@]}" )
for ((i = 0; i < ${#rmailboxes[@]}; i++)); do
rmailboxes[i]="${rmailboxes[i]#"$rprefix"}"
rmailboxes[i]="${rmailboxes[i]//"$rsep"/"$lsep"}"
@@ -442,7 +467,7 @@ passed() {
# Run test in a sub-shell
declare -a ENVIRON=()
environ_set "local"
-export TMPDIR TESTDIR STDERR "${ENVIRON[@]}"
+export TMPDIR TESTDIR INTERIMAP_PATH INTERIMAP_I STDERR "${ENVIRON[@]}"
export -f environ_set doveadm interimap interimap_init pullimap _interimap_cmd
export -f sqlite3 sample_message deliver ptree_abort step_start step_done passed
export -f check_mailbox_status check_mailbox_status_values check_mailbox_status2
diff --git a/tests/run-all b/tests/run-all
index d13f689..79e62d1 100755
--- a/tests/run-all
+++ b/tests/run-all
@@ -24,6 +24,7 @@ export PATH
BASEDIR="$(dirname -- "$0")"
RUN="$BASEDIR/run"
+list="$1"
failed=0
@@ -54,7 +55,7 @@ while IFS="" read -r x; do
fi
INDENT="$indent" "$RUN" "$t" ${desc+"$desc"} || failed=$(( failed+1 ))
-done <"$BASEDIR/list"
+done <"$BASEDIR/$list"
if [ $failed -eq 0 ]; then
printf "All tests passed.\\n"
diff --git a/tests/snippets/dovecot/dovecot.key b/tests/snippets/dovecot/dovecot.key
deleted file mode 100644
index 95c9846..0000000
--- a/tests/snippets/dovecot/dovecot.key
+++ /dev/null
@@ -1,5 +0,0 @@
------BEGIN EC PRIVATE KEY-----
-MHcCAQEEIGkqkKq69zVeF17S3y2U2HkQWh8z9M/xeblCztkKIfzJoAoGCCqGSM49
-AwEHoUQDQgAE1LLppulKw8KjINrDhOjEd0NTax5iDCds+vpA2PwsvvtGoprNAjQM
-zX+40u30N3CE0r591txqohSBQ/X+nvG2ug==
------END EC PRIVATE KEY-----
diff --git a/tests/snippets/dovecot/dovecot.pem b/tests/snippets/dovecot/dovecot.pem
deleted file mode 100644
index 7e53d90..0000000
--- a/tests/snippets/dovecot/dovecot.pem
+++ /dev/null
@@ -1,11 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIBkzCCATmgAwIBAgIUQ+3hBMsPJcl59xDDujDDfexurOswCgYIKoZIzj0EAwIw
-HzEdMBsGA1UEAwwUSW50ZXJJTUFQIHRlc3Qgc3VpdGUwHhcNMTkxMTEwMTM1NDAw
-WhcNMjkxMTA3MTM1NDAwWjAfMR0wGwYDVQQDDBRJbnRlcklNQVAgdGVzdCBzdWl0
-ZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABNSy6abpSsPCoyDaw4ToxHdDU2se
-YgwnbPr6QNj8LL77RqKazQI0DM1/uNLt9DdwhNK+fdbcaqIUgUP1/p7xtrqjUzBR
-MB0GA1UdDgQWBBRlh8nSwyX+VlhwuhV7RKYwvKLyDzAfBgNVHSMEGDAWgBRlh8nS
-wyX+VlhwuhV7RKYwvKLyDzAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0gA
-MEUCIQDK8xPPHTbYW5JnZ1Siy8ChZ6GOu2sRwQu7OgtGYGZRSQIgFKn1oAhnq2Oi
-aIPqxjvBPMsK/sjrdI/rNsr2XgaulU4=
------END CERTIFICATE-----
diff --git a/tests/snippets/dovecot/ssl.conf b/tests/snippets/dovecot/ssl.conf
deleted file mode 100644
index 240f24b..0000000
--- a/tests/snippets/dovecot/ssl.conf
+++ /dev/null
@@ -1,4 +0,0 @@
-ssl = required
-ssl_cert = <dovecot.pem
-ssl_key = <dovecot.key
-ssl_dh = <dhparams.pem
diff --git a/tests/split-set/t b/tests/split-set/t
index 5e8ea52..7e124e3 100644
--- a/tests/split-set/t
+++ b/tests/split-set/t
@@ -7,8 +7,8 @@ N=2048
# set UIDNEXT to 10^9 so all uids are 10 chars long, otherwise we'd need
# to add many more messages to obtain large sets
-doveadm -u "local" mailbox update --min-next-uid 1000000000 "INBOX"
-doveadm -u "remote" mailbox update --min-next-uid 1000000000 "INBOX"
+doveadm -u "local" mailbox update --no-userdb-lookup --min-next-uid 1000000000 "INBOX"
+doveadm -u "remote" mailbox update --no-userdb-lookup --min-next-uid 1000000000 "INBOX"
for ((i = 0; i < N; i++)); do
u="$(shuf -n1 -e "local" "remote")"
@@ -20,7 +20,7 @@ check_mailbox_status "INBOX"
# mark every other message as \Seen on the local server
for ((i = 0; i < N; i+=2)); do
- doveadm -u "local" flags add "\\Seen" mailbox "INBOX" $((N-i))
+ doveadm -u "local" flags add --no-userdb-lookup "\\Seen" mailbox "INBOX" $((N-i))
done
# send the changes to the remote; this results into an UID STORE set
@@ -31,7 +31,7 @@ check_mailbox_status "INBOX"
# now expunge every other message on the remote server; this results
# into large UID STORE and UID EXPUNGE set representation
for ((i = 0; i < N; i+=2)); do
- doveadm -u "local" expunge mailbox "INBOX" $((N-i))
+ doveadm -u "local" expunge --no-userdb-lookup mailbox "INBOX" $((N-i))
# add some more messages
u="$(shuf -n1 -e "local" "remote")"
sample_message | deliver -u "$u"
@@ -40,4 +40,4 @@ done
interimap || error
check_mailbox_status "INBOX"
-# vim: set filetype=sh :
+# vim: set filetype=bash :
diff --git a/tests/starttls-injection/imapd b/tests/starttls-injection/imapd
index 9000c8d..52cbe9a 100755
--- a/tests/starttls-injection/imapd
+++ b/tests/starttls-injection/imapd
@@ -4,7 +4,7 @@ use warnings;
use strict;
use Errno qw/EINTR/;
-use Net::SSLeay qw/die_now die_if_ssl_error/;
+use Net::SSLeay qw/die_now/;
use Socket qw/INADDR_LOOPBACK AF_INET SOCK_STREAM pack_sockaddr_in
SOL_SOCKET SO_REUSEADDR SHUT_RDWR/;
@@ -20,16 +20,16 @@ bind($S, pack_sockaddr_in(10143, INADDR_LOOPBACK)) or die "bind: $!\n";
listen($S, 1) or die "listen: $!";
my $CONFDIR = $ENV{HOME} =~ /\A(\p{Print}+)\z/ ? "$1/.dovecot/conf.d" : die;
-my $CTX = Net::SSLeay::CTX_new() or die_now("SSL_CTX_new");
+my $CTX = Net::SSLeay::CTX_new() or die_now("SSL_CTX_new()");
Net::SSLeay::CTX_set_mode($CTX,
Net::SSLeay::MODE_ENABLE_PARTIAL_WRITE() |
Net::SSLeay::MODE_ACCEPT_MOVING_WRITE_BUFFER() |
Net::SSLeay::MODE_AUTO_RETRY() | # don't fail SSL_read on renegotiation
Net::SSLeay::MODE_RELEASE_BUFFERS() );
-Net::SSLeay::CTX_use_PrivateKey_file($CTX, "$CONFDIR/dovecot.key", &Net::SSLeay::FILETYPE_PEM)
- or die_if_ssl_error("Can't load private key: $!");
-Net::SSLeay::CTX_use_certificate_file($CTX, "$CONFDIR/dovecot.pem", &Net::SSLeay::FILETYPE_PEM)
- or die_if_ssl_error("Can't load certificate: $!");
+Net::SSLeay::CTX_use_PrivateKey_file($CTX, "$CONFDIR/dovecot.rsa.key", &Net::SSLeay::FILETYPE_PEM)
+ or die_now("Can't load private key: $!");
+Net::SSLeay::CTX_use_certificate_file($CTX, "$CONFDIR/dovecot.rsa.crt", &Net::SSLeay::FILETYPE_PEM)
+ or die_now("Can't load certificate: $!");
while (1) {
my $sockaddr = accept(my $conn, $S) or do {
@@ -52,14 +52,14 @@ while (1) {
$conn->printf("%06d OK CAPABILITY injected\r\n", $1+1);
$conn->flush();
- my $ssl = Net::SSLeay::new($CTX) or die_if_ssl_error("SSL_new");
- Net::SSLeay::set_fd($ssl, $conn) or die_if_ssl_error("SSL_set_fd");
- Net::SSLeay::accept($ssl) and die_if_ssl_error("SSL_accept");
+ my $ssl = Net::SSLeay::new($CTX) or die_now("SSL_new()");
+ die_now("SSL_set_fd()") unless Net::SSLeay::set_fd($ssl, $conn) == 1;
+ die_now("SSL_accept()") unless Net::SSLeay::accept($ssl);
- Net::SSLeay::ssl_read_CRLF($ssl) =~ /\A(\S+) CAPABILITY\r\n\z/ or die_now("SSL_read");
+ Net::SSLeay::ssl_read_CRLF($ssl) =~ /\A(\S+) CAPABILITY\r\n\z/ or die_now("SSL_read()");
Net::SSLeay::ssl_write_CRLF($ssl, "* CAPABILITY IMAP4rev1 AUTH=LOGIN\r\n$1 OK CAPABILITY completed");
- Net::SSLeay::ssl_read_CRLF($ssl) =~ /\A(\S+) LOGIN .*\r\n\z/ or die_now("SSL_read");
+ Net::SSLeay::ssl_read_CRLF($ssl) =~ /\A(\S+) LOGIN .*\r\n\z/ or die_now("SSL_read()");
Net::SSLeay::ssl_write_CRLF($ssl, "$1 OK [CAPABILITY IMAP4rev1] LOGIN completed");
Net::SSLeay::free($ssl);
@@ -72,6 +72,6 @@ END {
Net::SSLeay::CTX_free($CTX) if defined $CTX;
if (defined $S) {
shutdown($S, SHUT_RDWR) or warn "shutdown: $!";
- close($S) or print STDERR "Can't close: $!\n";
+ close($S) or print STDERR "close: $!\n";
}
}
diff --git a/tests/starttls-injection/remote.conf b/tests/starttls-injection/remote.conf
index f23f3de..340a484 100644
--- a/tests/starttls-injection/remote.conf
+++ b/tests/starttls-injection/remote.conf
@@ -1,4 +1,6 @@
-protocols = $protocols imap
+protocols {
+ imap = yes
+}
service imap-login {
inet_listener imap {
port = 0
diff --git a/tests/starttls-injection/t b/tests/starttls-injection/t
index d57aa7a..023baff 100644
--- a/tests/starttls-injection/t
+++ b/tests/starttls-injection/t
@@ -13,4 +13,4 @@ grep -Fx 'remote: WARNING: Truncating non-empty output buffer (unauthenticated r
! grep -Fx 'remote: ERROR: Logins are disabled.' <"$STDERR" || error "injected capability wasn't ignored"
grep -Fx 'remote: ERROR: Server did not advertise ENABLE (RFC 5161) capability.' <"$STDERR" || error "injected capability wasn't ignored"
-# vim: set filetype=sh :
+# vim: set filetype=bash :
diff --git a/tests/starttls-logindisabled/remote.conf b/tests/starttls-logindisabled/remote.conf
index be2d51e..86e033a 100644
--- a/tests/starttls-logindisabled/remote.conf
+++ b/tests/starttls-logindisabled/remote.conf
@@ -2,4 +2,6 @@
!include conf.d/ssl.conf
# trick dovecot into treating local connections as insecure
-imap_capability = +LOGINDISABLED
+imap_capability {
+ LOGINDISABLED = yes
+}
diff --git a/tests/starttls-logindisabled/t b/tests/starttls-logindisabled/t
index 0ac7465..b2bf87b 100644
--- a/tests/starttls-logindisabled/t
+++ b/tests/starttls-logindisabled/t
@@ -16,4 +16,4 @@ grep -Fx "remote: C: 000001 CAPABILITY" <"$STDERR" || error
# can't go further as the capability string still has the manually
# enforced 'LOGINDISABLED'
-# vim: set filetype=sh :
+# vim: set filetype=bash :
diff --git a/tests/starttls/t b/tests/starttls/t
index 99a39c2..633997c 100644
--- a/tests/starttls/t
+++ b/tests/starttls/t
@@ -1,3 +1,7 @@
+ssl_server_cert_file="$(doveconf -c "$HOME_remote/.dovecot/config" -hx ssl_server/cert_file)"
+X509_SHA256="$(openssl x509 -in "$ssl_server_cert_file" -noout -fingerprint -sha256 \
+ | sed -rn "/^.*=\\s*/ {s///p;q}" | tr -d : | tr "[A-Z]" "[a-z]")"
+
for ((i = 0; i < 32; i++)); do
u="$(shuf -n1 -e "local" "remote")"
sample_message | deliver -u "$u"
@@ -17,11 +21,10 @@ grep -Fx "STARTTLS" <"$TMPDIR/capabilities" || error
grep -Fx "remote: C: 000000 STARTTLS" <"$STDERR" || error
grep -Fx "remote: C: 000001 CAPABILITY" <"$STDERR" || error
-grep -Fx "remote: Disabling SSL protocols: SSLv3, TLSv1, TLSv1.1" <"$STDERR" || error
-grep -Fx "remote: Peer certificate fingerprint: sha256\$35944e3bd3300d3ac310bb497a32cc1eef6931482a587ddbc95343740cdf1323" <"$STDERR" || error
-grep "^remote: SSL protocol: TLSv1\.[23] " <"$STDERR" || error
+grep -Fx "remote: Peer certificate fingerprint: sha256\$$X509_SHA256" <"$STDERR" || error
+grep "^remote: SSL protocol: TLSv" <"$STDERR" || error
grep "^remote: SSL cipher: " <"$STDERR" || error
check_mailbox_status "INBOX"
-# vim: set filetype=sh :
+# vim: set filetype=bash :
diff --git a/tests/sync-live-multi/local.conf b/tests/sync-live-multi/local.conf
index baae39d..6d64a84 100644
--- a/tests/sync-live-multi/local.conf
+++ b/tests/sync-live-multi/local.conf
@@ -1,30 +1,34 @@
namespace inbox {
- separator = /
- location = dbox:~/inbox:LAYOUT=index
- inbox = yes
- list = yes
+ separator = /
+ mail_driver = sdbox
+ mail_path = ~/inbox
+ inbox = yes
+ list = yes
}
namespace foo {
- separator = /
- prefix = foo/
- location = dbox:~/foo:LAYOUT=index
- inbox = no
- list = yes
+ separator = /
+ prefix = foo/
+ mail_driver = sdbox
+ mail_path = ~/foo
+ inbox = no
+ list = yes
}
namespace bar {
- separator = /
- prefix = bar/
- location = dbox:~/bar:LAYOUT=index
- inbox = no
- list = yes
+ separator = /
+ prefix = bar/
+ mail_driver = sdbox
+ mail_path = ~/bar
+ inbox = no
+ list = yes
}
namespace baz {
separator = /
- prefix = baz/
- location = dbox:~/baz:LAYOUT=index
- inbox = no
- list = yes
+ prefix = baz/
+ mail_driver = sdbox
+ mail_path = ~/baz
+ inbox = no
+ list = yes
}
diff --git a/tests/sync-live-multi/t b/tests/sync-live-multi/t
index ba7f326..5a651f5 100644
--- a/tests/sync-live-multi/t
+++ b/tests/sync-live-multi/t
@@ -49,15 +49,15 @@ while [ $(date +%s) -le $timer ]; do
remote3) m="${m//\//\?}";;
*) error "Uh?";;
esac
- doveadm -u "$u" mailbox create -- "$m"
+ doveadm -u "$u" mailbox create --no-userdb-lookup -- "$m"
fi
# EXPUNGE some messages
u="$(shuf -n1 -e -- "${TARGETS[@]}")" # choose target at random
n="$(shuf -n1 -i0-3)"
while read guid uid; do
- doveadm -u "$u" expunge mailbox-guid "$guid" uid "$uid"
- done < <(doveadm -u "$u" search all | shuf -n "$n")
+ doveadm -u "$u" expunge --no-userdb-lookup mailbox-guid "$guid" uid "$uid"
+ done < <(doveadm -u "$u" search --no-userdb-lookup all | shuf -n "$n")
# mark some existing messages as read (toggle \Seen flag as unlike other
# flags it's easier to query and check_mailboxes_status checks it)
@@ -65,8 +65,8 @@ while [ $(date +%s) -le $timer ]; do
n="$(shuf -n1 -i0-9)"
while read guid uid; do
a="$(shuf -n1 -e add remove replace)"
- doveadm -u "$u" flags "$a" "\\Seen" mailbox-guid "$guid" uid "$uid"
- done < <(doveadm -u "$u" search all | shuf -n "$n")
+ doveadm -u "$u" flags "$a" --no-userdb-lookup "\\Seen" mailbox-guid "$guid" uid "$uid"
+ done < <(doveadm -u "$u" search --no-userdb-lookup all | shuf -n "$n")
# select at random a mailbox where to deliver some messages
u="$(shuf -n1 -e "local" "remote")" # choose target at random
@@ -103,14 +103,14 @@ trap - EXIT INT TERM
# check that the mailbox lists match
diff -u --label="local/mailboxes" --label="remote1/mailboxes" \
- <( doveadm -u "local" mailbox list | sed -n "s,^foo/,,p" | sort ) \
- <( doveadm -u "remote1" mailbox list | tr '^' '/' | sort )
+ <( doveadm -u "local" mailbox list --no-userdb-lookup | sed -n "s,^foo/,,p" | sort ) \
+ <( doveadm -u "remote1" mailbox list --no-userdb-lookup | tr '^' '/' | sort )
diff -u --label="local/mailboxes" --label="remote2/mailboxes" \
- <( doveadm -u "local" mailbox list | sed -n "s,^bar/,,p" | sort ) \
- <( doveadm -u "remote2" mailbox list | tr '\\' '/' | sort )
+ <( doveadm -u "local" mailbox list --no-userdb-lookup | sed -n "s,^bar/,,p" | sort ) \
+ <( doveadm -u "remote2" mailbox list --no-userdb-lookup | tr '\\' '/' | sort )
diff -u --label="local/mailboxes" --label="remote3/mailboxes" \
- <( doveadm -u "local" mailbox list | sed -n "s,^baz/,,p" | sort ) \
- <( doveadm -u "remote3" mailbox list | tr '?' '/' | sort )
+ <( doveadm -u "local" mailbox list --no-userdb-lookup | sed -n "s,^baz/,,p" | sort ) \
+ <( doveadm -u "remote3" mailbox list --no-userdb-lookup | tr '?' '/' | sort )
for m in "${MAILBOXES[@]}"; do
case "$m" in
@@ -124,4 +124,4 @@ for m in "${MAILBOXES[@]}"; do
check_mailbox_status2 "$blob" "$m" "$u" "$mr"
done
-# vim: set filetype=sh :
+# vim: set filetype=bash :
diff --git a/tests/sync-live/t b/tests/sync-live/t
index 5f5b291..9b2074b 100644
--- a/tests/sync-live/t
+++ b/tests/sync-live/t
@@ -26,15 +26,15 @@ while [ $(date +%s) -le $timer ]; do
MAILBOXES+=( "$m" )
u="$(shuf -n1 -e "local" "remote")" # choose target at random
[ "$u" = "local" ] || m="${m//./^}"
- doveadm -u "$u" mailbox create -- "$m"
+ doveadm -u "$u" mailbox create --no-userdb-lookup -- "$m"
fi
# EXPUNGE some messages
u="$(shuf -n1 -e "local" "remote")" # choose target at random
n="$(shuf -n1 -i0-3)"
while read guid uid; do
- doveadm -u "$u" expunge mailbox-guid "$guid" uid "$uid"
- done < <(doveadm -u "$u" search all | shuf -n "$n")
+ doveadm -u "$u" expunge --no-userdb-lookup mailbox-guid "$guid" uid "$uid"
+ done < <(doveadm -u "$u" search --no-userdb-lookup all | shuf -n "$n")
# mark some existing messages as read (toggle \Seen flag as unlike other
# flags it's easier to query and check_mailboxes_status checks it)
@@ -42,8 +42,8 @@ while [ $(date +%s) -le $timer ]; do
n="$(shuf -n1 -i0-9)"
while read guid uid; do
a="$(shuf -n1 -e add remove replace)"
- doveadm -u "$u" flags "$a" "\\Seen" mailbox-guid "$guid" uid "$uid"
- done < <(doveadm -u "$u" search all | shuf -n "$n")
+ doveadm -u "$u" flags "$a" --no-userdb-lookup "\\Seen" mailbox-guid "$guid" uid "$uid"
+ done < <(doveadm -u "$u" search --no-userdb-lookup all | shuf -n "$n")
# select at random a mailbox where to deliver some messages
u="$(shuf -n1 -e "local" "remote")" # choose target at random
@@ -73,4 +73,4 @@ trap - EXIT INT TERM
check_mailbox_list
check_mailboxes_status "${MAILBOXES[@]}"
-# vim: set filetype=sh :
+# vim: set filetype=bash :
diff --git a/tests/sync-mailbox-list/t b/tests/sync-mailbox-list/t
index ea80fbf..ba533f0 100644
--- a/tests/sync-mailbox-list/t
+++ b/tests/sync-mailbox-list/t
@@ -4,10 +4,10 @@
# baz: present on both, subscribed to remote only
# foo.bar: present on local only
# foo.baz: present on remote only
-doveadm -u "local" mailbox create "foo" "bar" "baz" "foo.bar" "fo!o [b*a%r]"
-doveadm -u "local" mailbox subscribe "foo" "bar"
-doveadm -u "remote" mailbox create "foo" "bar" "baz" "foo~baz" "foo]bar"
-doveadm -u "remote" mailbox subscribe "foo" "baz"
+doveadm -u "local" mailbox create --no-userdb-lookup "foo" "bar" "baz" "foo.bar" "fo!o [b*a%r]"
+doveadm -u "local" mailbox subscribe --no-userdb-lookup "foo" "bar"
+doveadm -u "remote" mailbox create --no-userdb-lookup "foo" "bar" "baz" "foo~baz" "foo]bar"
+doveadm -u "remote" mailbox subscribe --no-userdb-lookup "foo" "baz"
populate() {
local i
@@ -45,7 +45,7 @@ step_start "aborts if present in database"
for u in "local" "remote"; do
[ "$u" = "local" ] && { m="foo.bar"; m2="$m"; } || { m="foo.baz"; m2="foo~baz"; }
- doveadm -u "$u" mailbox delete "$m2"
+ doveadm -u "$u" mailbox delete --no-userdb-lookup "$m2"
! interimap || error
grep -Fx "database: ERROR: Mailbox $m exists. Run \`interimap --target=database --delete $m\` to delete." <"$STDERR"
@@ -64,10 +64,10 @@ step_done
# (un)subscribe from some mailboxes, including a non-existent one
step_start "new (un)subscribtions"
-doveadm -u "local" mailbox unsubscribe "foo"
-doveadm -u "remote" mailbox unsubscribe "bar"
-doveadm -u "local" mailbox subscribe "foo.bar" "foo.nonexistent" "foo.baz"
-doveadm -u "remote" mailbox subscribe "foo~bar" "bar~nonexistent"
+doveadm -u "local" mailbox unsubscribe --no-userdb-lookup "foo"
+doveadm -u "remote" mailbox unsubscribe --no-userdb-lookup "bar"
+doveadm -u "local" mailbox subscribe --no-userdb-lookup "foo.bar" "foo.nonexistent" "foo.baz"
+doveadm -u "remote" mailbox subscribe --no-userdb-lookup "foo~bar" "bar~nonexistent"
populate
interimap
@@ -75,7 +75,7 @@ grep -Fx "remote: Unsubscribe to foo" <"$STDERR"
grep -Fx "local: Unsubscribe to bar" <"$STDERR"
grep -Fx "remote: Subscribe to foo~baz" <"$STDERR"
verify
-check_mailbox_list -s $(doveadm -u "local" mailbox list) # exclude "foo.nonexistent" and "bar~nonexistent"
+check_mailbox_list -s $(doveadm -u "local" mailbox list --no-userdb-lookup) # exclude "foo.nonexistent" and "bar~nonexistent"
# check that "baz", "foo.bar" and "foo.baz" are the only subscribed mailboxes
sqlite3 "$XDG_DATA_HOME/interimap/remote.db" >"$TMPDIR/count" <<-EOF
@@ -90,4 +90,4 @@ EOF
[ $(< "$TMPDIR/count") -eq 0 ] || error
step_done
-# vim: set filetype=sh :
+# vim: set filetype=bash :
diff --git a/tests/tls-ciphers/interimap.remote b/tests/tls-ciphers/interimap.remote
new file mode 120000
index 0000000..daf3741
--- /dev/null
+++ b/tests/tls-ciphers/interimap.remote
@@ -0,0 +1 @@
+../tls/interimap.remote \ No newline at end of file
diff --git a/tests/tls-ciphers/remote.conf b/tests/tls-ciphers/remote.conf
new file mode 120000
index 0000000..6029749
--- /dev/null
+++ b/tests/tls-ciphers/remote.conf
@@ -0,0 +1 @@
+../tls/remote.conf \ No newline at end of file
diff --git a/tests/tls-ciphers/t b/tests/tls-ciphers/t
new file mode 100644
index 0000000..ca0e610
--- /dev/null
+++ b/tests/tls-ciphers/t
@@ -0,0 +1,31 @@
+# backup config
+install -m0600 "$XDG_CONFIG_HOME/interimap/config" "$XDG_CONFIG_HOME/interimap/config~"
+with_remote_config() {
+ install -m0600 "$XDG_CONFIG_HOME/interimap/config~" "$XDG_CONFIG_HOME/interimap/config"
+ cat >>"$XDG_CONFIG_HOME/interimap/config"
+}
+
+with_remote_config <<-EOF
+ SSL_protocol_max = TLSv1.2
+ SSL_cipherlist = DHE-RSA-AES128-SHA256:ALL:!COMPLEMENTOFDEFAULT:!eNULL
+EOF
+interimap --debug || error
+grep -Fx "remote: SSL cipher: DHE-RSA-AES128-SHA256 (128 bits)" <"$STDERR" || error
+
+with_remote_config <<-EOF
+ SSL_protocol_max = TLSv1.2
+ SSL_cipherlist = NONEXISTENT:ECDHE-RSA-AES256-SHA384:ALL:!COMPLEMENTOFDEFAULT:!eNULL
+ SSL_ciphersuites = TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256
+EOF
+interimap --debug || error
+grep -Fx "remote: SSL cipher: ECDHE-RSA-AES256-SHA384 (256 bits)" <"$STDERR" || error
+
+with_remote_config <<-EOF
+ SSL_protocol_min = TLSv1.3
+ SSL_cipherlist = DHE-RSA-AES128-SHA256
+ SSL_ciphersuites = TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256
+EOF
+interimap --debug || error
+grep -Fx "remote: SSL cipher: TLS_CHACHA20_POLY1305_SHA256 (256 bits)" <"$STDERR" || error
+
+# vim: set filetype=bash :
diff --git a/tests/tls-pin-fingerprint/t b/tests/tls-pin-fingerprint/t
index 1b84390..679eaa4 100644
--- a/tests/tls-pin-fingerprint/t
+++ b/tests/tls-pin-fingerprint/t
@@ -1,3 +1,10 @@
+ssl_server_cert_file="$(doveconf -c "$HOME_remote/.dovecot/config" -hx ssl_server/cert_file)"
+PKEY_SHA256="$(openssl x509 -in "$ssl_server_cert_file" -pubkey \
+ | openssl pkey -in /dev/stdin -pubin -outform DER \
+ | openssl dgst -sha256 | sed -rn "/^.*=\\s*/ {s///p;q}")"
+INVALID_FPR="sha256\$deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
+INVALID_FPR2="sha256\$deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbee2"
+
# backup config
install -m0600 "$XDG_CONFIG_HOME/interimap/config" "$XDG_CONFIG_HOME/interimap/config~"
with_remote_config() {
@@ -7,7 +14,7 @@ with_remote_config() {
# pinned valid fingerprint
with_remote_config <<-EOF
- SSL_fingerprint = sha256\$e8fc8d03ffe75e03897136a2f1c5647bf8c36be7136a6883a732a8c4961c1614
+ SSL_fingerprint = sha256\$$PKEY_SHA256
EOF
for ((i = 0; i < 32; i++)); do
@@ -18,16 +25,54 @@ interimap_init
check_mailbox_status "INBOX"
+# with default algorithm (SHA256)
+with_remote_config <<-EOF
+ SSL_fingerprint = $INVALID_FPR $PKEY_SHA256
+EOF
+interimap --debug || error
+grep -Fx "remote: Peer certificate matches pinned SPKI digest sha256\$$PKEY_SHA256" <"$STDERR" || error
+
+
# and now an invalid one
with_remote_config <<-EOF
- SSL_fingerprint = sha256\$deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef
+ SSL_fingerprint = $INVALID_FPR
+EOF
+! interimap --debug || error
+
+grep -Fx "remote: ERROR: Can't initiate TLS/SSL handshake" <"$STDERR" || error
+grep -Fx "remote: WARNING: Fingerprint doesn't match! MiTM in action?" <"$STDERR" || error
+grep -Fx "remote: ERROR: Can't initiate TLS/SSL handshake" <"$STDERR" || error
+# make sure we didn't send any credentials or started speaking IMAP
+! grep -E "^remote: C: .* (AUTHENTICATE|LOGIN) " <"$STDERR" || error
+grep -Fx "remote: IMAP traffic (bytes): recv 0 sent 0" <"$STDERR" || error
+
+# two invalid ones
+with_remote_config <<-EOF
+ SSL_fingerprint = $INVALID_FPR $INVALID_FPR2
EOF
! interimap --debug || error
grep -Fx "remote: ERROR: Can't initiate TLS/SSL handshake" <"$STDERR" || error
grep -Fx "remote: WARNING: Fingerprint doesn't match! MiTM in action?" <"$STDERR" || error
grep -Fx "remote: ERROR: Can't initiate TLS/SSL handshake" <"$STDERR" || error
-# make sure we didn't send any credentials
+# make sure we didn't send any credentials or started speaking IMAP
! grep -E "^remote: C: .* (AUTHENTICATE|LOGIN) " <"$STDERR" || error
+grep -Fx "remote: IMAP traffic (bytes): recv 0 sent 0" <"$STDERR" || error
+
+
+# valid + invalid
+with_remote_config <<-EOF
+ SSL_fingerprint = sha256\$$PKEY_SHA256 $INVALID_FPR
+EOF
+interimap --debug || error
+grep -Fx "remote: Peer certificate matches pinned SPKI digest sha256\$$PKEY_SHA256" <"$STDERR" || error
+
+
+# invalid + valid
+with_remote_config <<-EOF
+ SSL_fingerprint = $INVALID_FPR sha256\$$PKEY_SHA256
+EOF
+interimap --debug || error
+grep -Fx "remote: Peer certificate matches pinned SPKI digest sha256\$$PKEY_SHA256" <"$STDERR" || error
-# vim: set filetype=sh :
+# vim: set filetype=bash :
diff --git a/tests/tls-protocols/openssl.cnf b/tests/tls-protocols/openssl.cnf
new file mode 100644
index 0000000..3d9769d
--- /dev/null
+++ b/tests/tls-protocols/openssl.cnf
@@ -0,0 +1,14 @@
+# as we want to test TLSv1 we need to set MinProtocol=None, see
+# see /usr/share/doc/libssl1.1/NEWS.Debian.gz
+
+openssl_conf = default_conf
+
+[default_conf]
+ssl_conf = ssl_sect
+
+[ssl_sect]
+system_default = system_default_sect
+
+[system_default_sect]
+MinProtocol = None
+CipherString = DEFAULT@SECLEVEL=0
diff --git a/tests/tls-protocols/remote.conf b/tests/tls-protocols/remote.conf
index 6029749..96b3713 120000..100644
--- a/tests/tls-protocols/remote.conf
+++ b/tests/tls-protocols/remote.conf
@@ -1 +1,4 @@
-../tls/remote.conf \ No newline at end of file
+!include conf.d/imapd.conf
+!include conf.d/ssl.conf
+ssl_min_protocol = TLSv1
+ssl_cipher_list = DEFAULT@SECLEVEL=0
diff --git a/tests/tls-protocols/t b/tests/tls-protocols/t
index f34a95b..b78dd69 100644
--- a/tests/tls-protocols/t
+++ b/tests/tls-protocols/t
@@ -1,3 +1,13 @@
+# system default
+interimap --debug || error
+! grep -E "^remote: Disabling SSL protocols: " <"$STDERR" || error # TODO deprecated
+! grep -E "^remote: Minimum SSL/TLS protocol version: " <"$STDERR" || error
+! grep -E "^remote: Maximum SSL/TLS protocol version: " <"$STDERR" || error
+grep -E "^remote: SSL protocol: TLSv" <"$STDERR" || error
+
+# load custom OpenSSL configuration to allow TLS protocol version <=1.1
+export OPENSSL_CONF="$TESTDIR/openssl.cnf"
+
# backup config
install -m0600 "$XDG_CONFIG_HOME/interimap/config" "$XDG_CONFIG_HOME/interimap/config~"
with_remote_tls_protocols() {
@@ -5,17 +15,15 @@ with_remote_tls_protocols() {
printf "SSL_protocols = %s\\n" "$*" >>"$XDG_CONFIG_HOME/interimap/config"
}
-# default
-interimap --debug || error
-grep -Fx "remote: Disabling SSL protocols: SSLv3, TLSv1, TLSv1.1" <"$STDERR" || error
-grep -E "^remote: SSL protocol: TLSv1\.[23] " <"$STDERR" || error
-
-# also disable TLSv1.2
+# disable TLSv1.2 and earlier
with_remote_tls_protocols "!SSLv2" "!SSLv3" "!TLSv1" "!TLSv1.1" "!TLSv1.2"
interimap --debug || error
grep -Fx "remote: Disabling SSL protocols: SSLv3, TLSv1, TLSv1.1, TLSv1.2" <"$STDERR" || error
grep -E "^remote: SSL protocol: TLSv1\.3 " <"$STDERR" || error
+interimap || error
+grep -E "^remote: WARNING: SSL_protocols is deprecated " <"$STDERR" || error "no deprecation warning"
+
# force TLSv1.2
with_remote_tls_protocols "TLSv1.2"
interimap --debug || error
@@ -28,12 +36,64 @@ interimap --debug || error
grep -Fx "remote: Disabling SSL protocols: SSLv3, TLSv1.3" <"$STDERR" || error
grep -E "^remote: SSL protocol: TLSv(1\.[12])? " <"$STDERR" || error
-# force SSLv2 and SSLv3, fails as it's disabled server side
+# force SSLv2 and SSLv3; this fails due to dovecot's ssl_min_protocol=TLSv1
with_remote_tls_protocols "SSLv2" "SSLv3"
! interimap --debug || error
grep -Fx "remote: Disabling SSL protocols: TLSv1, TLSv1.1, TLSv1.2, TLSv1.3" <"$STDERR" || error
grep -Fx "remote: ERROR: Can't initiate TLS/SSL handshake" <"$STDERR" || error
-# make sure we didn't send any credentials
+# make sure we didn't send any credentials or started speaking IMAP
+! grep -E "^remote: C: .* (AUTHENTICATE|LOGIN) " <"$STDERR" || error
+grep -Fx "remote: IMAP traffic (bytes): recv 0 sent 0" <"$STDERR" || error
+
+
+# new interface: SSL_protocol_{min,max}
+with_remote_tls_protocol_min_max() {
+ install -m0600 "$XDG_CONFIG_HOME/interimap/config~" "$XDG_CONFIG_HOME/interimap/config"
+ if [ -n "${1-}" ]; then
+ printf "SSL_protocol_min = %s\\n" "$1" >>"$XDG_CONFIG_HOME/interimap/config"
+ fi
+ if [ -n "${2-}" ]; then
+ printf "SSL_protocol_max = %s\\n" "$2" >>"$XDG_CONFIG_HOME/interimap/config"
+ fi
+}
+
+# disable TLSv1.2 and earlier
+# XXX this test assumes that TLSv1.3 is the highest version supported
+with_remote_tls_protocol_min_max "TLSv1.3"
+interimap --debug || error
+grep -Fx "remote: Minimum SSL/TLS protocol version: TLSv1.3" <"$STDERR" || error
+! grep -E "^remote: Maximum SSL/TLS protocol version: " <"$STDERR" || error
+grep -E "^remote: SSL protocol: TLSv1\.3 " <"$STDERR" || error
+
+# force TLSv1.2
+with_remote_tls_protocol_min_max "TLSv1.2" "TLSv1.2"
+interimap --debug || error
+grep -Fx "remote: Minimum SSL/TLS protocol version: TLSv1.2" <"$STDERR" || error
+grep -Fx "remote: Maximum SSL/TLS protocol version: TLSv1.2" <"$STDERR" || error
+grep -E "^remote: SSL protocol: TLSv1\.2 " <"$STDERR" || error
+
+# disable TLSv1.2 and later
+with_remote_tls_protocol_min_max "" "TLSv1.1"
+interimap --debug || error
+! grep -E "^remote: Minimum SSL/TLS protocol version: " <"$STDERR" || error
+grep -Fx "remote: Maximum SSL/TLS protocol version: TLSv1.1" <"$STDERR" || error
+grep -E "^remote: SSL protocol: TLSv1\.1 " <"$STDERR" || error
+
+# force SSLv3 to to TLSv1.1
+with_remote_tls_protocol_min_max "SSLv3" "TLSv1.1"
+interimap --debug || error
+grep -Fx "remote: Minimum SSL/TLS protocol version: SSLv3" <"$STDERR" || error
+grep -Fx "remote: Maximum SSL/TLS protocol version: TLSv1.1" <"$STDERR" || error
+grep -E "^remote: SSL protocol: TLSv1(\.1)? " <"$STDERR" || error
+
+# force SSLv3; this fails due to dovecot's ssl_min_protocol=TLSv1
+with_remote_tls_protocol_min_max "SSLv3" "SSLv3"
+! interimap --debug || error
+grep -Fx "remote: Minimum SSL/TLS protocol version: SSLv3" <"$STDERR" || error
+grep -Fx "remote: Maximum SSL/TLS protocol version: SSLv3" <"$STDERR" || error
+grep -Fx "remote: ERROR: Can't initiate TLS/SSL handshake" <"$STDERR" || error
+# make sure we didn't send any credentials or started speaking IMAP
! grep -E "^remote: C: .* (AUTHENTICATE|LOGIN) " <"$STDERR" || error
+grep -Fx "remote: IMAP traffic (bytes): recv 0 sent 0" <"$STDERR" || error
-# vim: set filetype=sh :
+# vim: set filetype=bash :
diff --git a/tests/tls-rsa+ecdsa/interimap.remote b/tests/tls-rsa+ecdsa/interimap.remote
new file mode 120000
index 0000000..daf3741
--- /dev/null
+++ b/tests/tls-rsa+ecdsa/interimap.remote
@@ -0,0 +1 @@
+../tls/interimap.remote \ No newline at end of file
diff --git a/tests/tls-rsa+ecdsa/remote.conf b/tests/tls-rsa+ecdsa/remote.conf
new file mode 100644
index 0000000..c0f2ff3
--- /dev/null
+++ b/tests/tls-rsa+ecdsa/remote.conf
@@ -0,0 +1,5 @@
+!include conf.d/imapd.conf
+!include conf.d/ssl.conf
+
+ssl_server_alt_cert_file = conf.d/dovecot.ecdsa.crt
+ssl_server_alt_key_file = conf.d/dovecot.ecdsa.key
diff --git a/tests/tls-rsa+ecdsa/t b/tests/tls-rsa+ecdsa/t
new file mode 100644
index 0000000..789d9e6
--- /dev/null
+++ b/tests/tls-rsa+ecdsa/t
@@ -0,0 +1,59 @@
+doveconf_remote() {
+ local p k="$1"
+ p="$(doveconf -c "$HOME_remote/.dovecot/config" -hx "$1")"
+ cat <"$p"
+}
+pkey_sha256() {
+ openssl x509 -in /dev/stdin -pubkey \
+ | openssl pkey -in /dev/stdin -pubin -outform DER \
+ | openssl dgst -sha256 | sed -rn "/^.*=\\s*/ {s///p;q}"
+}
+x509_sha256() {
+ openssl x509 -in /dev/stdin -noout -fingerprint -sha256 \
+ | sed -rn "/^.*=\\s*/ {s///p;q}" | tr -d : | tr "[A-Z]" "[a-z]"
+}
+
+PKEY_SHA256="$(doveconf_remote ssl_server/cert_file | pkey_sha256)"
+X509_SHA256="$(doveconf_remote ssl_server/cert_file | x509_sha256)"
+PKEY_ALT_SHA256="$(doveconf_remote ssl_server/alt_cert_file | pkey_sha256)"
+X509_ALT_SHA256="$(doveconf_remote ssl_server/alt_cert_file | x509_sha256)"
+
+# pinned valid fingerprints
+cat >>"$XDG_CONFIG_HOME/interimap/config" <<-EOF
+ SSL_fingerprint = sha256\$$PKEY_SHA256 sha256\$$PKEY_ALT_SHA256
+EOF
+
+for ((i = 0; i < 32; i++)); do
+ u="$(shuf -n1 -e "local" "remote")"
+ sample_message | deliver -u "$u"
+done
+interimap_init
+check_mailbox_status "INBOX"
+
+interimap --debug || error
+# which peer certificate is used is up to libssl
+grep -Fx -e "remote: Peer certificate fingerprint: sha256\$$X509_SHA256" \
+ -e "remote: Peer certificate fingerprint: sha256\$$X509_ALT_SHA256" \
+ <"$STDERR" || error
+grep -Fx -e "remote: Peer certificate matches pinned SPKI digest sha256\$$PKEY_SHA256" \
+ -e "remote: Peer certificate matches pinned SPKI digest sha256\$$PKEY_ALT_SHA256" \
+ <"$STDERR" || error
+
+# force RSA
+# XXX we also have to force TLS <=1.2 here as the TLSv1.3 ciphersuites
+# don't specify the certificate type (nor key exchange)
+cat >>"$XDG_CONFIG_HOME/interimap/config" <<-EOF
+ SSL_protocol_max = TLSv1.2
+ SSL_cipherlist = EECDH+AESGCM+aRSA
+EOF
+interimap --debug || error
+grep -Fx "remote: Peer certificate fingerprint: sha256\$$X509_SHA256" <"$STDERR" || error
+grep -Fx "remote: Peer certificate matches pinned SPKI digest sha256\$$PKEY_SHA256" <"$STDERR" || error
+
+# force ECDSA
+sed -i "s/^SSL_cipherlist\\s*=.*/SSL_cipherlist = EECDH+AESGCM+aECDSA/" -- "$XDG_CONFIG_HOME/interimap/config"
+interimap --debug || error
+grep -Fx "remote: Peer certificate fingerprint: sha256\$$X509_ALT_SHA256" <"$STDERR" || error
+grep -Fx "remote: Peer certificate matches pinned SPKI digest sha256\$$PKEY_ALT_SHA256" <"$STDERR" || error
+
+# vim: set filetype=bash :
diff --git a/tests/tls-sni/interimap.remote b/tests/tls-sni/interimap.remote
new file mode 100644
index 0000000..9f0d521
--- /dev/null
+++ b/tests/tls-sni/interimap.remote
@@ -0,0 +1,3 @@
+type = imaps
+port = 10993
+SSL_verify = no
diff --git a/tests/tls-sni/remote.conf b/tests/tls-sni/remote.conf
new file mode 100644
index 0000000..ef76cf9
--- /dev/null
+++ b/tests/tls-sni/remote.conf
@@ -0,0 +1,7 @@
+!include conf.d/imapd.conf
+!include conf.d/ssl.conf
+
+local_name imap.example.net {
+ ssl_server_cert_file = conf.d/dovecot.rsa2.crt
+ ssl_server_key_file = conf.d/dovecot.rsa2.key
+}
diff --git a/tests/tls-sni/t b/tests/tls-sni/t
new file mode 100644
index 0000000..7692f74
--- /dev/null
+++ b/tests/tls-sni/t
@@ -0,0 +1,66 @@
+SERVERNAME="imap.example.net" # cf local_name{} section in the dovecot config
+ssl_server_cert_file="$(doveconf -c "$HOME_remote/.dovecot/config" -hx ssl_server/cert_file)"
+X509_SHA256="$(openssl x509 -in "$ssl_server_cert_file" -noout -fingerprint -sha256 \
+ | sed -rn "/^.*=\\s*/ {s///p;q}" | tr -d : | tr "[A-Z]" "[a-z]")"
+ssl_server_cert_file2="$(doveconf -c "$HOME_remote/.dovecot/config" -f local_name="$SERVERNAME" -hx ssl_server/cert_file)"
+X509_2_SHA256="$(openssl x509 -in "$ssl_server_cert_file2" -noout -fingerprint -sha256 \
+ | sed -rn "/^.*=\\s*/ {s///p;q}" | tr -d : | tr "[A-Z]" "[a-z]")"
+
+# check that empty SSL_hostname disables SNI
+echo "SSL_hostname =" >>"$XDG_CONFIG_HOME/interimap/config"
+interimap --debug || error
+! grep "^remote: Using SNI with name " <"$STDERR" || error "Empty SSL_hostname didn't disable SNI"
+
+# default servername is the host value
+sed -i "/^SSL_hostname\\s*=/d" -- "$XDG_CONFIG_HOME/interimap/config"
+interimap --debug || error
+grep -Fx "remote: Using SNI with name localhost" <"$STDERR" || error "No default SNI"
+grep -Fx "remote: Peer certificate fingerprint: sha256\$$X509_SHA256" <"$STDERR" || error
+
+# verify that SNI is not used when host is an IP
+echo "host = __INVALID__" >>"$XDG_CONFIG_HOME/interimap/config"
+for ip in "127.0.0.1" "[::1]"; do
+ sed -i "s/^host\\s*=.*/host = $ip/" -- "$XDG_CONFIG_HOME/interimap/config"
+ interimap --debug || error
+ ! grep "^remote: Using SNI with name " <"$STDERR" || error "Using SNI with IP $ip"
+ grep -Fx "remote: Peer certificate fingerprint: sha256\$$X509_SHA256" <"$STDERR" || error
+done
+
+# verify that SNI actually works (ie we're served the right cert)
+sni_ok() {
+ grep -Fx "remote: Using SNI with name $SERVERNAME" <"$STDERR" || error
+ grep -Fx "remote: Peer certificate fingerprint: sha256\$$X509_2_SHA256" <"$STDERR" || error
+}
+echo "SSL_hostname = $SERVERNAME" >>"$XDG_CONFIG_HOME/interimap/config"
+interimap --debug || error
+sni_ok
+
+
+## make sure SSL_hostname doesn't affect certificate verification ##
+
+# bad CA, bad host
+sed -i "s/^host\\s*=.*/host = 127.0.0.1/" -- "$XDG_CONFIG_HOME/interimap/config"
+sed -i "s/^SSL_verify\\s*=.*/SSL_verify = YES/" -- "$XDG_CONFIG_HOME/interimap/config"
+! interimap --debug || error
+sni_ok
+grep -Fx "remote: ERROR: Can't initiate TLS/SSL handshake" <"$STDERR" || error
+
+# good CA, bad host
+echo "SSL_CAfile = $HOME/.dovecot/conf.d/ca.crt" >>"$XDG_CONFIG_HOME/interimap/config"
+! interimap --debug || error
+sni_ok
+grep -Fx "remote: ERROR: Can't initiate TLS/SSL handshake" <"$STDERR" || error
+
+# bad CA, good host
+sed -i "/^SSL_CAfile\\s*=/d" -- "$XDG_CONFIG_HOME/interimap/config"
+sed -i "s/^host\\s*=.*/host = localhost/" -- "$XDG_CONFIG_HOME/interimap/config"
+! interimap --debug || error
+sni_ok
+grep -Fx "remote: ERROR: Can't initiate TLS/SSL handshake" <"$STDERR" || error
+
+# good CA, good host
+echo "SSL_CAfile = $HOME/.dovecot/conf.d/ca.crt" >>"$XDG_CONFIG_HOME/interimap/config"
+interimap --debug || error
+sni_ok
+
+# vim: set filetype=bash :
diff --git a/tests/tls-verify-peer/interimap.remote b/tests/tls-verify-peer/interimap.remote
index b02fcd0..263655f 100644
--- a/tests/tls-verify-peer/interimap.remote
+++ b/tests/tls-verify-peer/interimap.remote
@@ -1,2 +1 @@
-host = ::1
port = 10993
diff --git a/tests/tls-verify-peer/t b/tests/tls-verify-peer/t
index d84328a..50c7445 100644
--- a/tests/tls-verify-peer/t
+++ b/tests/tls-verify-peer/t
@@ -1,80 +1,149 @@
-CERT=~/.dovecot/conf.d/dovecot.pem
+ssl_server_cert_file="$(doveconf -c "$HOME_remote/.dovecot/config" -hx ssl_server/cert_file)"
+X509_SHA256="$(openssl x509 -in "$ssl_server_cert_file" -noout -fingerprint -sha256 \
+ | sed -rn "/^.*=\\s*/ {s///p;q}" | tr -d : | tr "[A-Z]" "[a-z]")"
+PKEY_SHA256="$(openssl x509 -in "$ssl_server_cert_file" -pubkey \
+ | openssl pkey -in /dev/stdin -pubin -outform DER \
+ | openssl dgst -sha256 | sed -rn "/^.*=\\s*/ {s///p;q}")"
unverified_peer() {
! interimap --debug || error
+ # make sure we aborted the handshake immediately after connecting
+ grep -Fx "remote: Peer certificate fingerprint: sha256\$$X509_SHA256" <"$STDERR" || error
grep -Fx "remote: ERROR: Can't initiate TLS/SSL handshake" <"$STDERR" || error
sed -nr "s/remote: \[[0-9]+\] (preverify=[0-9]+)$/\1/p" <"$STDERR" >"$TMPDIR/preverify"
[ -s "$TMPDIR/preverify" ] || error
! grep -Fvx "preverify=0" <"$TMPDIR/preverify" || error
- # make sure we didn't send any credentials
+ # make sure we didn't send any credentials or started speaking IMAP
! grep -E "^remote: C: .* (AUTHENTICATE|LOGIN) " <"$STDERR" || error
+ grep -Fx "remote: IMAP traffic (bytes): recv 0 sent 0" <"$STDERR" || error
}
verified_peer() {
local i u
- for ((i = 0; i < 32; i++)); do
+ for ((i = 0; i < 4; i++)); do
u="$(shuf -n1 -e "local" "remote")"
sample_message | deliver -u "$u"
done
interimap --debug || error
+ grep -Fx "remote: Peer certificate fingerprint: sha256\$$X509_SHA256" <"$STDERR" || error
sed -nr "s/remote: \[[0-9]+\] (preverify=[0-9]+)$/\1/p" <"$STDERR" >"$TMPDIR/preverify"
[ -s "$TMPDIR/preverify" ] || error
! grep -Fvx "preverify=1" <"$TMPDIR/preverify" || error
- grep "^remote: SSL protocol: TLSv1\.[23] " <"$STDERR" || error
+ grep "^remote: SSL protocol: TLSv" <"$STDERR" || error
grep "^remote: SSL cipher: " <"$STDERR" || error
check_mailbox_status "INBOX"
}
# backup config
-install -m0600 "$XDG_CONFIG_HOME/interimap/config" "$XDG_CONFIG_HOME/interimap/config~"
+install -m0600 -- "$XDG_CONFIG_HOME/interimap/config" "$XDG_CONFIG_HOME/interimap/config~"
with_remote_config() {
- install -m0600 "$XDG_CONFIG_HOME/interimap/config~" "$XDG_CONFIG_HOME/interimap/config"
+ install -m0600 -- "$XDG_CONFIG_HOME/interimap/config~" "$XDG_CONFIG_HOME/interimap/config"
cat >>"$XDG_CONFIG_HOME/interimap/config"
}
step_start "peer verification enabled by default"
+# assume our fake root CA is not among OpenSSL's default trusted CAs
unverified_peer
+grep -Fx "remote: Using default locations for trusted CA certificates" <"$STDERR" || error
step_done
step_start "peer verification result honored when pinned pubkey matches"
-pkey_sha256="$(openssl x509 -pubkey <"$CERT" | openssl pkey -pubin -outform DER \
- | openssl dgst -sha256 | sed -rn "/^.*=\\s*/ {s///p;q}")"
with_remote_config <<-EOF
- SSL_fingerprint = sha256\$$pkey_sha256
+ SSL_fingerprint = sha256\$$PKEY_SHA256
EOF
unverified_peer
-! grep -Fx "remote: WARNING: Fingerprint doesn't match! MiTM in action?" <"$STDERR" || error
+grep -Fx "remote: Using default locations for trusted CA certificates" <"$STDERR" || error
+grep -Fx "remote: Peer certificate matches pinned SPKI digest sha256\$$PKEY_SHA256" <"$STDERR" || error
step_done
-step_start "SSL_CAfile"
+capath=$(mktemp --tmpdir="$TMPDIR" --directory capath.XXXXXX)
+cp -T -- ~/.dovecot/conf.d/ca.crt "$capath/ca-certificates.crt"
+
+step_start "SSL_CAfile/\$SSL_CERT_FILE"
+
+# verify that an error is raised when CAfile can't be loaded
+# (it's not the case for $SSL_CERT_FILE, cf. SSL_CTX_load_verify_locations(3ssl))
+with_remote_config <<<"SSL_CAfile = /nonexistent"
+! interimap --debug || error
+grep -Fx "remote: ERROR: SSL_CTX_load_verify_locations()" <"$STDERR" || error
+grep -Fx "remote: IMAP traffic (bytes): recv 0 sent 0" <"$STDERR" || error
+
if [ -f "/etc/ssl/certs/ca-certificates.crt" ]; then
- # the self-signed cert should not be in there
+ # assume our fake root CA is not there
with_remote_config <<<"SSL_CAfile = /etc/ssl/certs/ca-certificates.crt"
unverified_peer
fi
-with_remote_config <<<"SSL_CAfile = $CERT"
+
+# default host (localhost) is the CN (and also subjectAltName)
+with_remote_config <<<"SSL_CAfile = $capath/ca-certificates.crt"
verified_peer
+
+with_remote_config </dev/null
+SSL_CERT_FILE=~/.dovecot/conf.d/ca.crt verified_peer
+grep -Fx "remote: Using default locations for trusted CA certificates" <"$STDERR" || error
+
+# hostnames and IPs included in the subjectAltName should work as well
+for host in "ip6-localhost" "127.0.0.1" "::1"; do
+ with_remote_config <<-EOF
+ host = $host
+ SSL_CAfile = $capath/ca-certificates.crt
+ EOF
+ verified_peer
+done
+
+# but not for other hostnames or IPs
+for host in "ip6-loopback" "127.0.1.1"; do
+ with_remote_config <<-EOF
+ host = $host
+ SSL_CAfile = $capath/ca-certificates.crt
+ EOF
+ unverified_peer
+done
+
step_done
-step_start "SSL_CApath"
+step_start "SSL_CApath/\$SSL_CERT_DIR"
+
if [ -d "/etc/ssl/certs" ]; then
- # the self-signed cert should not be in there
+ # assume our fake root CA is not there
with_remote_config <<<"SSL_CApath = /etc/ssl/certs"
unverified_peer
fi
-capath=$(mktemp --tmpdir="$TMPDIR" --directory capath.XXXXXX)
-cp -t"$capath" "$CERT"
c_rehash "$capath"
+# default host (localhost) is the CN (and also subjectAltName)
with_remote_config <<<"SSL_CApath = $capath"
verified_peer
+
+with_remote_config </dev/null
+SSL_CERT_DIR="$capath" verified_peer
+grep -Fx "remote: Using default locations for trusted CA certificates" <"$STDERR" || error
+
+# hostnames and IPs included in the subjectAltName should work as well
+for host in "ip6-localhost" "127.0.0.1" "::1"; do
+ with_remote_config <<-EOF
+ host = $host
+ SSL_CApath = $capath
+ EOF
+ verified_peer
+done
+
+# but not for other IPs or hostnames
+for host in "ip6-loopback" "127.0.1.1"; do
+ with_remote_config <<-EOF
+ host = $host
+ SSL_CApath = $capath
+ EOF
+ unverified_peer
+done
+
step_done
-# vim: set filetype=sh :
+# vim: set filetype=bash :
diff --git a/tests/tls/t b/tests/tls/t
index dd6d955..fa59ae5 100644
--- a/tests/tls/t
+++ b/tests/tls/t
@@ -1,14 +1,17 @@
+ssl_server_cert_file="$(doveconf -c "$HOME_remote/.dovecot/config" -hx ssl_server/cert_file)"
+X509_SHA256="$(openssl x509 -in "$ssl_server_cert_file" -noout -fingerprint -sha256 \
+ | sed -rn "/^.*=\\s*/ {s///p;q}" | tr -d : | tr "[A-Z]" "[a-z]")"
+
for ((i = 0; i < 32; i++)); do
u="$(shuf -n1 -e "local" "remote")"
sample_message | deliver -u "$u"
done
interimap --debug || error
-grep -Fx "remote: Disabling SSL protocols: SSLv3, TLSv1, TLSv1.1" <"$STDERR" || error
-grep -Fx "remote: Peer certificate fingerprint: sha256\$35944e3bd3300d3ac310bb497a32cc1eef6931482a587ddbc95343740cdf1323" <"$STDERR" || error
-grep "^remote: SSL protocol: TLSv1\.[23] " <"$STDERR" || error
+grep -Fx "remote: Peer certificate fingerprint: sha256\$$X509_SHA256" <"$STDERR" || error
+grep "^remote: SSL protocol: TLSv" <"$STDERR" || error
grep "^remote: SSL cipher: " <"$STDERR" || error
check_mailbox_status "INBOX"
-# vim: set filetype=sh :
+# vim: set filetype=bash :