]> git.ipfire.org Git - thirdparty/dovecot/core.git/log
thirdparty/dovecot/core.git
5 hours agoglobal: Remove lib-otp main
Marco Bettini [Tue, 28 Jul 2026 13:27:39 +0000 (13:27 +0000)] 
global: Remove lib-otp

5 hours agolib-sasl: Remove set-credentials support
Marco Bettini [Tue, 28 Jul 2026 14:02:54 +0000 (14:02 +0000)] 
lib-sasl: Remove set-credentials support

The OTP mechanism was the only mechanism that updated the
stored passdb credentials, so with it gone nothing calls
sasl_server_request_set_credentials() anymore.

5 hours agolib-sasl: Remove OTP SASL mechanism
Marco Bettini [Tue, 28 Jul 2026 13:27:39 +0000 (13:27 +0000)] 
lib-sasl: Remove OTP SASL mechanism

lib-smtp uses dsasl_client and therefore links libsasl.la, which was the
reason its unit tests had to link libotp.la as well.

5 hours agolib-auth: Remove {OTP} password scheme
Marco Bettini [Tue, 28 Jul 2026 13:21:26 +0000 (13:21 +0000)] 
lib-auth: Remove {OTP} password scheme

5 hours agoauth: Remove passdb set-credentials support
Marco Bettini [Tue, 28 Jul 2026 13:55:57 +0000 (13:55 +0000)] 
auth: Remove passdb set-credentials support

The OTP mechanism was the only user of the passdb credentials update path.
This also drops the passdb_sql_update_query setting, which was only ever
used by the removed set-credentials query.

5 hours agoauth: Remove OTP authentication mechanism
Marco Bettini [Tue, 28 Jul 2026 13:21:26 +0000 (13:21 +0000)] 
auth: Remove OTP authentication mechanism

This festure and code have not been actively maintained
or tested in a long while, and likewise they have not been
used by anyone either since.

4 days agolib-dcrypt: test-crypto - Use constants for algorithm names
Aki Tuomi [Thu, 23 Apr 2026 08:03:27 +0000 (11:03 +0300)] 
lib-dcrypt: test-crypto - Use constants for algorithm names

4 days agolib-dcrypt: Support AEAD with dovecot key encryption
Aki Tuomi [Thu, 23 Apr 2026 07:14:16 +0000 (10:14 +0300)] 
lib-dcrypt: Support AEAD with dovecot key encryption

4 days agolib-dcrypt: Run empty and short tests for all key algorithms too
Aki Tuomi [Fri, 29 May 2026 12:33:20 +0000 (12:33 +0000)] 
lib-dcrypt: Run empty and short tests for all key algorithms too

4 days agolib-dcrypt: Parameterize test_write_read_v2_short/empty
Aki Tuomi [Fri, 29 May 2026 12:32:34 +0000 (12:32 +0000)] 
lib-dcrypt: Parameterize test_write_read_v2_short/empty

4 days agolib-dcrypt: Add ML-KEM tests
Aki Tuomi [Mon, 20 Apr 2026 11:14:14 +0000 (14:14 +0300)] 
lib-dcrypt: Add ML-KEM tests

test-crypto: generate/encap/decap round-trip and Dovecot-format store/reload
for all three variants. test-stream: full encrypt/decrypt stream round-trip
across all cipher algorithms for ML-KEM-512/768/1024.

4 days agolib-dcrypt: Add ML-KEM support to encrypt/decrypt streams
Aki Tuomi [Mon, 20 Apr 2026 11:14:08 +0000 (14:14 +0300)] 
lib-dcrypt: Add ML-KEM support to encrypt/decrypt streams

KEM and ECDH share the same post-derivation code (pbkdf2 + symmetric
encrypt/decrypt). Unified into a single branch; only the secret derivation
call differs.

4 days agolib-dcrypt: Add ML-KEM support in OpenSSL3 backend
Aki Tuomi [Mon, 20 Apr 2026 11:14:00 +0000 (14:14 +0300)] 
lib-dcrypt: Add ML-KEM support in OpenSSL3 backend

Generate, store and load ML-KEM-512/768/1024 private and public keys using
EVP_PKEY_Q_keygen, EVP_PKEY_encapsulate and EVP_PKEY_decapsulate. Key type
detection uses NID-based IS_KEM() macro (and EVP_PKEY_is_a() for keys
created via the provider API). All code guarded with HAVE_OPENSSL_KEM.

4 days agotest-crypto: Replace i_panic() with i_fatal()
Aki Tuomi [Thu, 23 Apr 2026 07:45:39 +0000 (10:45 +0300)] 
test-crypto: Replace i_panic() with i_fatal()

4 days agolib-dcrypt: Include OpenSSL error with fatal in dcrypt_openssl_private_to_public_key()
Aki Tuomi [Tue, 2 Jun 2026 10:35:27 +0000 (13:35 +0300)] 
lib-dcrypt: Include OpenSSL error with fatal in dcrypt_openssl_private_to_public_key()

4 days agolib-dcrypt: Ensure obj is not NULL in dcrypt_openssl_store_private_key_dovecot()
Aki Tuomi [Thu, 23 Apr 2026 06:49:10 +0000 (09:49 +0300)] 
lib-dcrypt: Ensure obj is not NULL in dcrypt_openssl_store_private_key_dovecot()

4 days agolib-dcrypt: Use EVP_PKEY_get_octet_string_param() as default in dcrypt_openssl_store_...
Aki Tuomi [Thu, 23 Apr 2026 05:53:42 +0000 (08:53 +0300)] 
lib-dcrypt: Use EVP_PKEY_get_octet_string_param() as default in dcrypt_openssl_store_private_key_dovecot()

4 days agolib-dcrypt: ostream-encrypt - Clear correct buffer on error
Aki Tuomi [Thu, 23 Apr 2026 06:33:17 +0000 (09:33 +0300)] 
lib-dcrypt: ostream-encrypt - Clear correct buffer on error

4 days agolib-dcrypt: Rename ecdh_derive_secret functions to derive_secret
Aki Tuomi [Thu, 23 Apr 2026 06:13:17 +0000 (09:13 +0300)] 
lib-dcrypt: Rename ecdh_derive_secret functions to derive_secret

Simplifies next change.

5 days agoimap/pop3-login: Tolerate SASL continuations "+" without trailing space
Marco Bettini [Fri, 24 Jul 2026 15:47:31 +0000 (15:47 +0000)] 
imap/pop3-login: Tolerate SASL continuations "+" without trailing space

4 days agolib-master: Fix kick reason when KICK-USER-SIGNAL command arrives late
Timo Sirainen [Wed, 29 Jul 2026 06:45:29 +0000 (06:45 +0000)] 
lib-master: Fix kick reason when KICK-USER-SIGNAL command arrives late

anvil kicks processes with client_limit=1 by connecting to the process's
master-admin socket, writing the KICK-USER-SIGNAL command and sending
SIGTERM. The command is normally read by the SIGTERM handler itself,
which accept()s the connection. If the process's ioloop happens to
accept the connection first, master_admin_client_initial_read() waits
for the command for 100 ms with SIGTERM blocked.

If the command wasn't received within those 100 ms, the kick was lost:
the SIGTERM handler's accept() failed with EAGAIN, because the
connection had already been accepted, and last_kick_signal_user was
still unset. The process then shut down as if it wasn't kicked at all,
logging "Killed with signal 15" and disconnecting the client with
"Server shutting down" instead of "User kicked".

The same happened even when the command was already readable: the
delayed SIGTERM handler stops the ioloop, which stops running the rest
of the io callbacks in the current ioloop iteration, so the master-admin
connection's input was never handled.

Fix this by waiting a while for the pending master-admin commands in the
delayed SIGTERM handler before deciding that the process wasn't kicked,
and by doing the username match already in
master_service_set_last_kick_signal_user() when the SIGTERM was handled
before the command was received.

4 days agolib-master: Add MASTER_ADMIN_CLIENT_WAIT_MSECS
Timo Sirainen [Wed, 29 Jul 2026 06:45:22 +0000 (06:45 +0000)] 
lib-master: Add MASTER_ADMIN_CLIENT_WAIT_MSECS

Replaces the hardcoded master-admin connection command wait timeout.

4 days agolib-master: Fix parsing master-admin-client minor version in SIGTERM handler
Timo Sirainen [Wed, 29 Jul 2026 06:40:25 +0000 (06:40 +0000)] 
lib-master: Fix parsing master-admin-client minor version in SIGTERM handler

5 days agologin-proxy: Log admin kick reason when exiting due to a kicked proxy
Timo Sirainen [Sun, 5 Jul 2026 17:06:42 +0000 (17:06 +0000)] 
login-proxy: Log admin kick reason when exiting due to a kicked proxy

In high-security mode a kicked proxy connection terminates the login
process via SIGTERM, and the proxy is freed in login_proxy_deinit() with
the generic "Process shutting down" reason. Use "Kicked by admin" instead
when the process was killed by a user kick, matching the reason logged by
the admin-socket kick path.

5 days agologin-proxy: Fix doveadm kick for proxied connections in high-security mode
Timo Sirainen [Sun, 5 Jul 2026 17:06:15 +0000 (17:06 +0000)] 
login-proxy: Fix doveadm kick for proxied connections in high-security mode

When imap-login runs with client_limit=1 (restart_request_count=1, the
community-edition default), a detached proxy connection is kicked via
SIGTERM + KICK-USER-SIGNAL. The master service signal handler matches the
kick against current_user, which login processes never set, so the signal
was treated as a mismatch and ignored: doveadm reported the connection
kicked while the client stayed connected.

Set current_user to the proxied user while proxying with client_limit=1,
so the kick is recognized and terminates the connection. With
client_limit>1 the kick uses the admin socket and current_user stays unset.

5 days agolib: ostream-file - Avoid memcpy(NULL) assert with max_buffer_size=0
Markus Valentin [Wed, 29 Jul 2026 00:52:50 +0000 (00:52 +0000)] 
lib: ostream-file - Avoid memcpy(NULL) assert with max_buffer_size=0

o_stream_set_max_buffer_size(output, 0) on a stream that has never
buffered anything leaves fstream->buffer NULL, because
o_stream_grow_buffer() caps the allocation to max_buffer_size. If a
write to the fd could then not be fully flushed, o_stream_add() did a
zero-length memcpy() into the NULL buffer:

  Panic: file lib.h: line 75 (i_memcpy): assertion failed: (dest != NULL && src != NULL)

This is not triggered by any of the current max_buffer_size=0 callers.

o_stream_add() also already returned the correct 0 here and the
memcpy() copied nothing, so this only became fatal with the
assert-crashing memcpy() macro in 38dbeec767ad10021020e3833b1dbc3d04442f85.

5 days agolib: test-ostream-multiplex - Add STREAM format backpressure and buffering tests
Timo Sirainen [Tue, 14 Jul 2026 05:41:06 +0000 (05:41 +0000)] 
lib: test-ostream-multiplex - Add STREAM format backpressure and buffering tests

Cover the recent ostream-multiplex STREAM fixes and some previously
untested paths:

- STREAM must write through a max_buffer_size==0 parent instead of
  wedging the data in the channel buffer.
- A single sendv larger than IO_BLOCK_SIZE to such a parent must make
  forward progress rather than repeatedly buffering nothing and
  busy-looping.
- A congested socket must apply backpressure so o_stream_send_istream()
  stops and waits, instead of buffering the whole body into the parent
  without bound. A regression spins forever, so this runs in a
  subprocess whose watchdog kills the hung child and turns the hang into
  a clean failure.
- A max_buffer_size==0 PACKET parent must fail its assert loudly.
- The STREAM_CONTINUE format had no coverage at all.

6 days agolib-mail: message-parser - Check both dashes of the epilogue boundary
Timo Sirainen [Wed, 29 Jul 2026 01:00:24 +0000 (01:00 +0000)] 
lib-mail: message-parser - Check both dashes of the epilogue boundary

preparsed_parse_epilogue_boundary() failed to verify the second dash's
existence when epilogue starts with "\r\n--".

6 days agolib-mail: message-parser - Fix skipping the epilogue boundary line
Timo Sirainen [Wed, 29 Jul 2026 01:00:24 +0000 (01:00 +0000)] 
lib-mail: message-parser - Fix skipping the epilogue boundary line

preparsed_parse_epilogue_boundary() looks for the end of the close
delimiter line with memchr(). When it isn't found the code asks for more
data, but stops asking once it holds BOUNDARY_END_MAX_LEN bytes, the
input buffer is full, or no more data is coming. It then fell through
to:

ctx->skip = cur - data + 1;

with cur == NULL. That is undefined behavior, and in practice yields a
skip of roughly 2^64 - (address of data). The skip is applied by the
next message_parser_read_more(), whose i_stream_skip() then seeks the
stream far past any real offset. The walk ends at EOF with
broken_reason unset, so message_parser_deinit_from_parts() reports
success for a truncated parse. If the multipart's parent had a further
sibling, preparsed_parse_next_header_init() would instead see the
stream past that sibling's cached physical_pos.

The forward parser doesn't stop looking for the line's end:
parse_next_body_skip_boundary_line() consumes whole blocks until it
finds the newline. Do the same here, in a new
preparsed_parse_epilogue_skip_boundary_line() state, so replaying a
cached tree returns the same blocks the forward parser returns for the
same input. Give up only if the line doesn't end within the part, which
is the pre-existing "Epilogue boundary end not at expected position"
check.

This wasn't reachable with the current callers: the epilogue is only
parsed with MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS, and the
from-parts callers (index-mail-headers, message-search) do not set it.

Add tests for the ways the line's end can be missing: a close delimiter
with no trailing newline that ends exactly at the multipart's end, a
stream truncated inside the close delimiter line, and a line whose
newline is further away than the input buffer can reach -- the last one
comparing the returned blocks against a forward parse of the same input.

6 days agolib-mail: message-part-serialize - Enforce message part offset consistency
Timo Sirainen [Mon, 27 Jul 2026 11:25:20 +0000 (11:25 +0000)] 
lib-mail: message-part-serialize - Enforce message part offset consistency

The message_part tree is serialized to the cache and later deserialized
and walked by message_parser_init_from_parts(). That walk requires the
parts to be laid out in order with non-overlapping siblings, but neither
end enforced it fully:

 - message_part_deserialize() advanced its expected-position check only
   for parts that have children, never past a leaf body. Overlapping
   leaf siblings therefore passed validation and could later crash the
   from-parts walk. Advance the position past leaf bodies too so such a
   tree is rejected as corrupted (leading to a re-parse).

   This also makes the existing "child part location exceeds our size"
   check effective for child lists ending in a leaf: previously the
   position was still at the parent's body start when the recursion
   returned, so a last child overrunning its parent's body was accepted.

 - message_part_serialize() only checked that non-composite parts have
   no children. Its input is freshly parsed in-memory data, so an
   inconsistent tree is a parser/programming bug, not corrupted input:
   assert the same ordering the deserializer requires. This catches a
   bad producer at write time rather than as a confusing read-side
   failure, and guarantees anything serialized will deserialize.

The position is only used for validation and is never stored into the
returned parts, so trees that still deserialize are unchanged.

Add tests for the deserializer rejecting overlapping leaf siblings and
for the serializer asserting on them.

6 days agolib-mail: message-parser - Handle inconsistent cached parts in from-parts
Timo Sirainen [Mon, 27 Jul 2026 11:25:11 +0000 (11:25 +0000)] 
lib-mail: message-parser - Handle inconsistent cached parts in from-parts

message_parser_init_from_parts() walks a cached message_part tree using
its cached physical positions and sizes. If the tree is internally
inconsistent -- a sibling part recorded as starting before the previous
part ends -- preparsed_parse_next_header_init() finds the stream already
past the part's cached physical_pos and i_assert-panics:

  Panic: ... preparsed_parse_next_header_init: assertion failed:
  (ctx->part->physical_pos >= ctx->input->v_offset)

The sibling body_init and epilogue_init paths already treat the same
condition as a broken message. Do the same here: set broken_reason and
return -1 instead of asserting, so a corrupt cache degrades to a
re-parse instead of crashing the process.

Add a test that parses tricky multipart messages at every input buffer
size and checks the resulting tree offsets never overlap and always
re-parse, plus a test that a manually overlapped tree fails the
from-parts walk gracefully.

6 days agolib: test-iostream-pump - Fix duplicated test name
Timo Sirainen [Tue, 28 Jul 2026 23:44:38 +0000 (23:44 +0000)] 
lib: test-iostream-pump - Fix duplicated test name

test_iostream_pump_failure_end_read() printed itself as "failure mid-read",
which is what test_iostream_pump_failure_mid_read() is already called. This
made it ambiguous which of the two tests a CI failure belonged to.

6 days agolib: test-iostream-pump - Fix flakiness on stalled machines
Timo Sirainen [Tue, 28 Jul 2026 23:44:01 +0000 (23:44 +0000)] 
lib: test-iostream-pump - Fix flakiness on stalled machines

The tests used a 3000 ms ioloop timeout as a watchdog for the pump to
finish. The ioloop compensates for time moving forwards only while it is
waiting, not while timeout and IO callbacks are running. When the wall
clock jumps forwards during that window, or the process doesn't get
scheduled, every timeout that became due is called in the same run, so the
watchdog fires before the pump has had the chance to make any progress.

Use a 30 second watchdog instead, so that only a machine that is stalled
that long can trigger it. Keep alarm() a bit above the watchdog, so that it
still only triggers when the ioloop itself is stuck.

6 days agolib: test-lib-signals - Fix flakiness on stalled machines
Timo Sirainen [Tue, 28 Jul 2026 23:43:48 +0000 (23:43 +0000)] 
lib: test-lib-signals - Fix flakiness on stalled machines

The tests used a 400 ms (or 100 ms) ioloop timeout as a watchdog for a
delayed signal handler that was expected to be called much sooner. The
ioloop compensates for time moving forwards only while it is waiting, not
while timeout and IO callbacks are running. When the wall clock jumps
forwards during that window, or the process doesn't get scheduled, every
timeout that became due is called in the same run in the order it expired.
The watchdog then runs immediately after the timeout that sent the signal,
and the test fails before the ioloop ever gets to call the delayed signal
handler.

Use a 30 second watchdog instead, so that only a machine that is stalled
that long can trigger it. The timeouts whose expiry is the expected outcome
of the test are left as they were.

6 days agoconfig: Fix doveconf hiding secrets
Aki Tuomi [Mon, 1 Jun 2026 11:15:31 +0000 (11:15 +0000)] 
config: Fix doveconf hiding secrets

Secrets should be properly hidden when asking for doveconf and doveconf
section/

6 days agoconfig: Implement heredoc support
Aki Tuomi [Fri, 1 May 2026 19:41:50 +0000 (22:41 +0300)] 
config: Implement heredoc support

Adds <<MARKER heredoc syntax to the Dovecot config file format,
allowing multi-line values to be set without escaping:

  key = <<EOD
  line one
  line two
  EOD

The marker is arbitrary (any C identifier) and is preserved through
the binary config format so doveconf reproduces the exact marker in
its output (e.g. <<FOO â†’ doveconf outputs <<FOO).

Storage: values set via heredoc are stored with prefix byte
CONFIG_VALUE_PREFIX_HEREDOC (4) followed by the marker, a newline,
and the value. SET_FILE settings use HEREDOC|FILE_INLINE (12) to
preserve the marker while flagging the content as inline.

The "inline:" assignment form (ssl_ca = inline:cert) is still
accepted for backward compatibility but is now stored as
CONFIG_VALUE_PREFIX_FILE_INLINE (8). doveconf always outputs
SET_FILE inline content using heredoc syntax; the "inline:" prefix
is no longer emitted. The "inline:" is still accepted, and not
deprecated from passdb/userdb result.

6 days agoconfig: Replace config_value_prefix #defines with enum
Aki Tuomi [Fri, 1 May 2026 19:41:38 +0000 (22:41 +0300)] 
config: Replace config_value_prefix #defines with enum

Replaces the three CONFIG_VALUE_PREFIX_* character constants with a
proper enum config_value_prefix using powers-of-two values so that
prefix flags can be combined with bitwise OR.

Also adds CONFIG_VALUE_PREFIX_FILE_INLINE (8) for SET_FILE inline
content, allowing it to be stored directly without the previous
\001\n two-byte hack. The HEREDOC|FILE_INLINE (12) combination
preserves both the heredoc marker and the inline-content flag.

9 days agoglobal: Drop TIME_T_SIGNED define
Aki Tuomi [Fri, 24 Jul 2026 10:22:14 +0000 (10:22 +0000)] 
global: Drop TIME_T_SIGNED define

10 days agom4: Require signed time_t
Aki Tuomi [Tue, 14 Jul 2026 07:12:02 +0000 (07:12 +0000)] 
m4: Require signed time_t

An unsigned time_t is not forbidden by POSIX, but it is essentially
nonexistent on POSIX systems. Requiring a signed time_t simplifies
overflow checks in time arithmetic.

10 days agolib: ioloop - Use time_max_safe_value() for infinite-wait sentinel
Aki Tuomi [Sat, 11 Jul 2026 18:53:40 +0000 (18:53 +0000)] 
lib: ioloop - Use time_max_safe_value() for infinite-wait sentinel

Reuse the shared bound instead of duplicating the TIME_T_MAX_BITS
computation. Widens the sentinel from 2^(TIME_T_MAX_BITS-1)-1 to
2^TIME_T_MAX_BITS-1, matching time_max_safe_value()'s bound.

10 days agolib-imap: Use time helpers for date arithmetic overflow checks
Aki Tuomi [Sat, 11 Jul 2026 17:54:16 +0000 (17:54 +0000)] 
lib-imap: Use time helpers for date arithmetic overflow checks

tm_is_too_large() now shares its max-time_t bound via
time_max_safe_value() instead of duplicating the #if TIME_T_MAX_BITS
block. imap_parse_datetime() and imap_to_datetime_tz() use
time_sub_secs()/time_add_secs() instead of unchecked time_t
arithmetic when applying the timezone offset.

10 days agolib-http: Use time_add_secs() for Retry-After parsing
Aki Tuomi [Sat, 11 Jul 2026 16:18:05 +0000 (19:18 +0300)] 
lib-http: Use time_add_secs() for Retry-After parsing

Found by fuzzers. Fixes overflow when parsing too large
retry-fater values.

10 days agolib: Add time_add_secs()/time_sub_secs() overflow-safe helpers
Aki Tuomi [Sat, 11 Jul 2026 17:53:00 +0000 (17:53 +0000)] 
lib: Add time_add_secs()/time_sub_secs() overflow-safe helpers

Adding/subtracting seconds to/from a time_t without an overflow check
is a recurring bug source (e.g. Retry-After parsing). Add shared
helpers that reject out-of-range results instead of invoking UB.

10 days agolib-http: Document why client response parser is lenient
Aki Tuomi [Mon, 13 Jul 2026 21:09:14 +0000 (21:09 +0000)] 
lib-http: Document why client response parser is lenient

Record why http_response_parser_init() is intentionally not passed
STRICT here: the client parses responses from arbitrary upstream
servers, some of which send legitimately non-compliant framing, and
the response is consumed internally rather than re-emitted
downstream, so the response-splitting precondition that STRICT
guards against doesn't apply to this connection's use.

No behavior change.

10 days agolib/uri-util: Fix out-of-bounds reads on unterminated buffers
Aki Tuomi [Mon, 13 Jul 2026 21:09:07 +0000 (21:09 +0000)] 
lib/uri-util: Fix out-of-bounds reads on unterminated buffers

uri_parse_ip_literal() scanned for the closing ']' with memchr()
starting at cur+1 but passing length end-cur, one byte too many.
uri_parse_pct_encoded_data() dereferenced both bytes after a '%'
before checking either against pend.

Both are masked when parsing a '\0'-terminated string (the extra
read lands on the terminator), which covers every caller currently
in tree, but uri_parser_init_data() is public API that allows a
bounded, non-terminated buffer, so a real out-of-bounds read is
possible via that path.

Add a regression test using uri_check_data() with an exact-size,
non-terminated heap buffer so valgrind can catch a reintroduced
overread; a '\0'-terminated test table would pass unchanged whether
or not the bug is present.

10 days agolib-http: Reject bare LF as line terminator in strict mode
Aki Tuomi [Mon, 13 Jul 2026 21:08:55 +0000 (21:08 +0000)] 
lib-http: Reject bare LF as line terminator in strict mode

Every CR-handling state in the header parser, request-line parser
and chunked transfer parser treated CR as optional, so a lone LF
always terminated the line even with STRICT set. This let an
untrusted peer choose between CRLF and LF at each line boundary,
which is a request-smuggling-relevant desync risk against an
upstream that requires full CRLF.

Add a strict flag to the chunked transfer istream (previously it had
no strict/lenient distinction at all) and thread it from the message
parser, so server-side (request) parsing rejects bare LF while
client-side (response) parsing stays lenient, matching the existing
header parser split.

2 weeks agologin-common: Add unit test for 4d6e5744bb5 bugfix
Michael M Slusarz [Fri, 10 Jul 2026 19:41:07 +0000 (13:41 -0600)] 
login-common: Add unit test for 4d6e5744bb5 bugfix

2 weeks agologin-common: Fix memory leak of proxy_last_host
Markus Valentin [Thu, 16 Jul 2026 11:16:52 +0000 (11:16 +0000)] 
login-common: Fix memory leak of proxy_last_host

Broken in 6335dab970771b13689918637122360ff7021662.

2 weeks agolib-smtp: address - Fix assert on empty broken localpart
Aki Tuomi [Tue, 14 Jul 2026 05:55:27 +0000 (05:55 +0000)] 
lib-smtp: address - Fix assert on empty broken localpart

smtp_address_parse_path_full() with an end pointer and
ALLOW_BAD_LOCALPART could leave the separator search pointer one before
the parse position for an empty address (e.g. a leading space),
underflowing into p_strdup_until() and aborting with an assertion. Clamp
the pointer to the parse position. Add a test covering this case.

2 weeks agosubmission-login: proxy - Fail cleanly on malformed AUTH reply
Aki Tuomi [Tue, 14 Jul 2026 05:52:10 +0000 (05:52 +0000)] 
submission-login: proxy - Fail cleanly on malformed AUTH reply

When the backend answered the proxied AUTH command with a malformed
status line, the AUTHENTICATE state broke out into the shared failure
block, which assumes proxy_reply was already created. That assumption
does not hold for an invalid line, so the login process aborted with an
assertion. Handle the invalid line as a protocol failure and return
immediately instead.

2 weeks agosubmission-login: proxy - Cap AUTH reply continuation lines
Aki Tuomi [Tue, 14 Jul 2026 05:47:11 +0000 (05:47 +0000)] 
submission-login: proxy - Cap AUTH reply continuation lines

A malicious or compromised backend could answer the proxied AUTH command
with an endless multi-line reply, growing proxy_reply without bound until
the login process ran out of memory (only loosely bounded by the login
timeout). Limit the number of continuation lines and fail the proxy
connection with a protocol error when the cap is exceeded.

2 weeks agoplugins/quota-clone: Register flushing quota clone on recalc
Karl Fleischmann [Mon, 13 Jul 2026 13:22:15 +0000 (15:22 +0200)] 
plugins/quota-clone: Register flushing quota clone on recalc

2 weeks agoplugins/quota-clone: Split off quota_clone_changed_user()
Karl Fleischmann [Mon, 13 Jul 2026 12:51:30 +0000 (14:51 +0200)] 
plugins/quota-clone: Split off quota_clone_changed_user()

2 weeks agoplugins/quota: Trigger recalc-callbacks on doveadm quota recalc
Karl Fleischmann [Mon, 13 Jul 2026 13:15:38 +0000 (15:15 +0200)] 
plugins/quota: Trigger recalc-callbacks on doveadm quota recalc

2 weeks agoplugins/quota: Add mechanism to register recalc-callbacks
Karl Fleischmann [Mon, 13 Jul 2026 13:06:29 +0000 (15:06 +0200)] 
plugins/quota: Add mechanism to register recalc-callbacks

These allow a call-site to hook into the quota plugin recalculating the
quota.

2 weeks agolib-mail: test-message-parser - Add LF-only empty preamble test
Aki Tuomi [Tue, 14 Jul 2026 07:28:09 +0000 (07:28 +0000)] 
lib-mail: test-message-parser - Add LF-only empty preamble test

2 weeks agolib-mail: message-parser - Fix out-of-bounds read on empty preamble
nishat-06 [Tue, 14 Jul 2026 07:08:48 +0000 (12:38 +0530)] 
lib-mail: message-parser - Fix out-of-bounds read on empty preamble

2 weeks agoimap: cmd_getmetadata() - Don't panic on GETMETADATA with invalid UTF-8 mailbox pattern
Marco Bettini [Tue, 14 Jul 2026 09:17:03 +0000 (09:17 +0000)] 
imap: cmd_getmetadata() - Don't panic on GETMETADATA with invalid UTF-8 mailbox pattern

2 weeks agolib-storage: mailbox_list_default_get_vname_part() - Sanitise mailbox vname when...
Marco Bettini [Tue, 7 Jul 2026 17:10:46 +0000 (17:10 +0000)] 
lib-storage: mailbox_list_default_get_vname_part() - Sanitise mailbox vname when it can't be escaped

With no escape char set, replace invalid bytes with U+FFFD so callers
don't panic.

2 weeks agolib-storage: mailbox_list_name_escape() - Escape invalid UTF-8 bytes in mailbox vname
Marco Bettini [Tue, 7 Jul 2026 17:09:20 +0000 (17:09 +0000)] 
lib-storage: mailbox_list_name_escape() - Escape invalid UTF-8 bytes in mailbox vname

Escape invalid bytes as <esc><hex> so vnames stay valid UTF-8 even with
an escape char set.

2 weeks agodoveadm-dsync: dsync_mailbox_name_to_parts() - Keep invalid UTF-8 bytes escaped in...
Marco Bettini [Mon, 13 Jul 2026 12:24:09 +0000 (12:24 +0000)] 
doveadm-dsync: dsync_mailbox_name_to_parts() - Keep invalid UTF-8 bytes escaped in mailbox vnames

2 weeks agolib: Add hex2dec_case()
Marco Bettini [Mon, 13 Jul 2026 12:19:51 +0000 (12:19 +0000)] 
lib: Add hex2dec_case()

2 weeks agoimap: Don't report FETCH output stream errors as storage errors
Markus Valentin [Tue, 14 Jul 2026 06:47:25 +0000 (08:47 +0200)] 
imap: Don't report FETCH output stream errors as storage errors

When no storage error is set and the output stream has failed do not
report "BUG: Unknown internal error".

2 weeks agolib: ostream-multiplex - Fix panic when destroying corked channel 0 after parent...
Markus Valentin [Mon, 13 Jul 2026 13:46:32 +0000 (15:46 +0200)] 
lib: ostream-multiplex - Fix panic when destroying corked channel 0 after parent failed

If the parent ostream fails (e.g. EPIPE when the client disconnects)
while the multiplex has corked it for sending, the uncork at the end of
o_stream_multiplex_sendv() is a no-op on the failed stream, so
parent_corked stays TRUE. Destroying the still-corked channel 0
afterwards tried to transfer the cork back to the parent and panicked.

Fixes:

Panic: file ostream-multiplex.c: line 518 (o_stream_multiplex_try_destroy): assertion failed: (!mstream->parent_corked)

Broken by f368d9e40650ca91ec8a4be1cdb361a0a42c33d9.

2 weeks agodoveadm: fs delete - Fix crash with concurrent async deletes
Timo Sirainen [Fri, 10 Jul 2026 07:08:48 +0000 (07:08 +0000)] 
doveadm: fs delete - Fix crash with concurrent async deletes

When deleting multiple objects asynchronously (doveadm fs delete -n), an
error on one file could leave another file's async lookup still running
when it was deinitialized. The still-pending EAGAIN was masked by the
errored file's ret=-1, and the drain loop in
doveadm_fs_delete_async_finish() was gated on doveadm_exit_code==0, so it
skipped draining once any file failed. Deinitializing a file mid-lookup
tripped the object_id_lookup_state != RUNNING assert in fs-dictmap.

Track pending separately from ret so it's no longer masked, and always
drain pending async operations before deinit, regardless of errors. Also
deinit files that hit a terminal error immediately, so they aren't
retried - otherwise the retry restarts their async lookup and keeps the
drain loop pending forever.

2 weeks agolib: idna - Implement code point context rules
Stephan Bosch [Fri, 4 Apr 2025 02:26:10 +0000 (04:26 +0200)] 
lib: idna - Implement code point context rules

2 weeks agolib: unicode-data - Add data for IDNA code point context rules
Stephan Bosch [Thu, 3 Apr 2025 02:37:59 +0000 (04:37 +0200)] 
lib: unicode-data - Add data for IDNA code point context rules

2 weeks agolib: idna - Implement Bidi rule checks
Stephan Bosch [Fri, 4 Apr 2025 02:20:38 +0000 (04:20 +0200)] 
lib: idna - Implement Bidi rule checks

2 weeks agolib: unicode-data - Add Bidi data needed for IDNA Bidi rules
Stephan Bosch [Fri, 4 Apr 2025 02:06:29 +0000 (04:06 +0200)] 
lib: unicode-data - Add Bidi data needed for IDNA Bidi rules

2 weeks agolib: test-unicode-data - Add support for parsing data from comments
Stephan Bosch [Sun, 23 Nov 2025 07:17:13 +0000 (08:17 +0100)] 
lib: test-unicode-data - Add support for parsing data from comments

2 weeks agolib: idna-punycode - Remove idna_punycode_decode_utf8()
Stephan Bosch [Tue, 7 Jul 2026 14:42:53 +0000 (16:42 +0200)] 
lib: idna-punycode - Remove idna_punycode_decode_utf8()

It is no longer used nor useful.

2 weeks agolib-mail: rfc822-parser - Remove rfc822_decode_punycode()
Stephan Bosch [Tue, 7 Jul 2026 14:40:19 +0000 (16:40 +0200)] 
lib-mail: rfc822-parser - Remove rfc822_decode_punycode()

It is no longer used nor useful.

2 weeks agolib-mail: rfc8222-parser - Use IDNA rather than punycode decoder directly
Stephan Bosch [Tue, 7 Jul 2026 14:38:49 +0000 (16:38 +0200)] 
lib-mail: rfc8222-parser - Use IDNA rather than punycode decoder directly

2 weeks agolib: Implement basic IDNA support
Stephan Bosch [Tue, 7 Jul 2026 17:14:06 +0000 (19:14 +0200)] 
lib: Implement basic IDNA support

2 weeks agolib: test-idna-punycode - Use idna_punycode_decode() directly
Stephan Bosch [Tue, 7 Jul 2026 17:13:20 +0000 (19:13 +0200)] 
lib: test-idna-punycode - Use idna_punycode_decode() directly

2 weeks agolib: unicode-data - Add IDNA code point data
Stephan Bosch [Mon, 24 Mar 2025 01:36:08 +0000 (02:36 +0100)] 
lib: unicode-data - Add IDNA code point data

2 weeks agolib: idna-punycode - Add idna_punycode_encode()
Stephan Bosch [Fri, 13 Mar 2026 00:45:44 +0000 (01:45 +0100)] 
lib: idna-punycode - Add idna_punycode_encode()

2 weeks agolib: idna-punycode - Rename 'label' variable in idna_punycode_decode() to 'output'
Stephan Bosch [Tue, 7 Jul 2026 14:32:11 +0000 (16:32 +0200)] 
lib: idna-punycode - Rename 'label' variable in idna_punycode_decode() to 'output'

2 weeks agolib: idna-punycode - Split off idna_punycode_decode() to use fixed uint32_t buffers
Stephan Bosch [Fri, 13 Mar 2026 00:35:20 +0000 (01:35 +0100)] 
lib: idna-punycode - Split off idna_punycode_decode() to use fixed uint32_t buffers

2 weeks agolib: idna-punycode - Rename idna_punycode_decode() to idna_punycode_decode_utf8()
Stephan Bosch [Fri, 13 Mar 2026 00:00:12 +0000 (01:00 +0100)] 
lib: idna-punycode - Rename idna_punycode_decode() to idna_punycode_decode_utf8()

2 weeks agolib: idna-punycode - Rename 'len' parameter in idna_punycode_decode() to 'in_len'
Stephan Bosch [Tue, 7 Jul 2026 13:43:45 +0000 (15:43 +0200)] 
lib: idna-punycode - Rename 'len' parameter in idna_punycode_decode() to 'in_len'

2 weeks agolib: idna-punycode - Rename 'input' parameter in idna_punycode_decode() to 'in'
Stephan Bosch [Wed, 11 Mar 2026 21:13:33 +0000 (22:13 +0100)] 
lib: idna-punycode - Rename 'input' parameter in idna_punycode_decode() to 'in'

2 weeks agolib: idna-punycode - Rename 'out' variable in idna_punycode_decode() to 'out_pos'
Stephan Bosch [Wed, 11 Mar 2026 21:00:56 +0000 (22:00 +0100)] 
lib: idna-punycode - Rename 'out' variable in idna_punycode_decode() to 'out_pos'

2 weeks agolib: idna-punycode - Use explicit uint32_t rather than unsigned int
Stephan Bosch [Wed, 11 Mar 2026 20:32:27 +0000 (21:32 +0100)] 
lib: idna-punycode - Use explicit uint32_t rather than unsigned int

2 weeks agolib-mail: test-rfc822-parser - Consolidate domain tests in one test function
Stephan Bosch [Wed, 11 Mar 2026 20:10:27 +0000 (21:10 +0100)] 
lib-mail: test-rfc822-parser - Consolidate domain tests in one test function

2 weeks agolib: Rename punycode to idna-punycode
Stephan Bosch [Wed, 11 Mar 2026 19:41:18 +0000 (20:41 +0100)] 
lib: Rename punycode to idna-punycode

2 weeks ago.gitignore - Add /build-aux/test-driver
Stephan Bosch [Thu, 11 Dec 2025 19:57:56 +0000 (20:57 +0100)] 
.gitignore - Add /build-aux/test-driver

2 weeks agolib: punycode - Add proper attribution for original source code
Stephan Bosch [Tue, 7 Jul 2026 14:04:00 +0000 (16:04 +0200)] 
lib: punycode - Add proper attribution for original source code

2 weeks agolib: Reformat punycode.c
Stephan Bosch [Tue, 7 Jul 2026 13:10:24 +0000 (15:10 +0200)] 
lib: Reformat punycode.c

2 weeks agolib-mail: rfc822-parser - Fix punycode decoding in rfc822_parse_domain()
Stephan Bosch [Wed, 11 Mar 2026 22:15:07 +0000 (23:15 +0100)] 
lib-mail: rfc822-parser - Fix punycode decoding in rfc822_parse_domain()

3 weeks agolib-oauth2: jwt - Add test for many non-singular sibling fields
Aki Tuomi [Mon, 13 Jul 2026 06:09:33 +0000 (06:09 +0000)] 
lib-oauth2: jwt - Add test for many non-singular sibling fields

Covers oauth2_jwt_copy_fields() with many array/object siblings in a
row, exercising the array_append_space() relocation path fixed in
72f87bf4f6.

3 weeks agolib-oauth2: jwt - Avoid use-after-realloc of jwt_node front pointer
rootvector2 [Thu, 28 May 2026 06:34:25 +0000 (12:04 +0530)] 
lib-oauth2: jwt - Avoid use-after-realloc of jwt_node front pointer

oauth2_jwt_copy_fields() captures subroot from array_front(&nodes)
then calls array_append_space(&nodes) inside the inner loop. When the
append cannot extend the buffer in place it relocates it, so subroot
aliases the previous slot and later subroot->prefix / subroot->array
reads operate on the abandoned location. Refresh subroot after the
append.

3 weeks agoconfig: Delay module dlclose() until after lib_deinit()
Aki Tuomi [Mon, 13 Jul 2026 05:27:23 +0000 (05:27 +0000)] 
config: Delay module dlclose() until after lib_deinit()

module_dir_unload() ran deinit() and dlclose()d config modules before
master_service_deinit()/lib_deinit(). If a module (e.g. the
managesieve settings plugin, which pulls in libdovecot-sieve.so and
registers an event category) got dlclose()d, its memory was unmapped
before lib_event_deinit()'s fuzzing-build category cleanup ran,
causing a crash on process exit.

Split module_dir_unload() into module_dir_deinit() (while lib is
still alive) followed by master_service_deinit(), and only then
dlclose() the modules via module_dir_unload().

3 weeks agoimap: Change imap_compress_on_proxy default to yes
Markus Valentin [Thu, 9 Jul 2026 09:15:45 +0000 (09:15 +0000)] 
imap: Change imap_compress_on_proxy default to yes

3 weeks agovalgrind: Add openssl suppressions
Aki Tuomi [Sun, 12 Jul 2026 05:28:25 +0000 (08:28 +0300)] 
valgrind: Add openssl suppressions

3 weeks agolib-ssl-iostream: Don't call OPENSSL_cleanup() for openssl 4.0
Hector Cao [Sat, 11 Jul 2026 08:04:51 +0000 (10:04 +0200)] 
lib-ssl-iostream: Don't call OPENSSL_cleanup() for openssl 4.0

With OpenSSL 4.0 the lib-dcrypt unit tests (test-crypto, test-stream)
segfault at process exit.

dovecot_openssl_common_global_ref() installs custom OpenSSL memory
functions via CRYPTO_set_mem_functions(); their code lives inside the
dynamically loaded OpenSSL/dcrypt module. It also passes
OPENSSL_INIT_NO_ATEXIT so that libcrypto does not register its own
atexit(OPENSSL_cleanup) handler.

dovecot_openssl_common_global_unref() then called OPENSSL_cleanup()
explicitly. On OpenSSL 4.0 this explicit cleanup re-arms an internal
exit-time cleanup that runs after main() returns [1], i.e. after the module
(and therefore the custom memory functions) has been dlclose()d, so
libcrypto calls the now-unmapped free() function and the process crashes
with SIGSEGV.

[1] https://openssl-library.org/post/2026-03-10-remove-atexit/index.html

Signed-off-by: Hector Cao <hector.cao@canonical.com>
3 weeks agolib-http: chunked - Fix UB on chunk-size overflow
Aki Tuomi [Fri, 10 Jul 2026 07:49:32 +0000 (07:49 +0000)] 
lib-http: chunked - Fix UB on chunk-size overflow

Overflow check ran after the shift, so chunk_size <<= 4 itself
overflowed once the top 4 bits were set, triggering UBSan. Check
before shifting instead, same as str_parse_uintmax_hex() in strnum.c.

Add a test case with an oversized chunk-size to catch this.

3 weeks agolib: ostream-multiplex - Fix busy-loop on a single sendv larger than IO_BLOCK_SIZE
Timo Sirainen [Thu, 9 Jul 2026 20:29:29 +0000 (20:29 +0000)] 
lib: ostream-multiplex - Fix busy-loop on a single sendv larger than IO_BLOCK_SIZE

o_stream_multiplex_ochannel_sendv() grows the effective avail to
IO_BLOCK_SIZE when the channel buffer is small and the parent has no
un-flushed data, so a max_buffer_size==0 parent (which always reports
avail==0) can still be written to. But the following "avail < total"
block then re-read avail from the real buffer (0 again) and returned 0,
discarding the grown value. When a single sendv was larger than
IO_BLOCK_SIZE this buffered nothing and returned 0, so the caller made
no forward progress.

With o_stream_send_istream() of an in-memory istream - which returns all
of its data in one read - a field larger than IO_BLOCK_SIZE is sent as
one oversized sendv. The channel kept accepting nothing while the parent
socket stayed writable, so the flush was rescheduled immediately and the
process busy-looped at ~100% CPU.

3 weeks agoplugins/welcome: For Pro edition, add service/welcome/unix_listener defaults
alexandre.roux [Fri, 19 Dec 2025 03:04:44 +0000 (12:04 +0900)] 
plugins/welcome: For Pro edition, add service/welcome/unix_listener defaults

3 weeks agoplugins/welcome: Add service/welcome as default
alexandre.roux [Thu, 11 Dec 2025 10:30:36 +0000 (19:30 +0900)] 
plugins/welcome: Add service/welcome as default