Commit Graph

918 Commits

Author SHA1 Message Date
a553a107f0 Cleanup temporary files created during IMAP APPEND command.
Since a recent change (likely since implementing MULTIAPPEND), the temporary
files weren't removed any more. When changing it, I must have had the wrong
mental model about the MessageAdd method, assuming it would remove the temp
file.

Noticed during tests.
2025-03-10 09:26:24 +01:00
0857e81a6c Prevent spurious warnings about thread ids not being correct for messages that are expunged but not yet erased.
Erasing a message (removing the message file from the file system) was made a
separate step a few days ago. The verifydata command checks for consistency of
the data, but didn't correctly skip checking expunged-but-not-yet-erased
messages, leading to the warning.

A similar consistency check in store/account.go does check for that.

I was warned by my nightly backup+veridata script.
2025-03-08 09:03:41 +01:00
2314397078 Fix recently introduced bug when authentication with password.
In case the precis check failed, our return of a nil account cleared acc, and
we were then trying to close it, returning in a nil pointer dereference.

Rewrite the return statements so we don't overwrite the named return variables.
2025-03-07 21:30:20 +01:00
1c58d38280 webmail: When completing a recipient address, quote the "name" if necessary for proper interpretation.
Especially relevant when the name contains a comma, e.g. "lastname, firstname".
Or when it contains parentheses, e.g. "(organization)".

When sending to an address with a comma that isn't quoted, we would actually
interpret it as two addresses: One without an "@" before the comma, and the
second part after the comma with half of the name and the email addrss. This
resulted in an error message.

When sending to a recipient with unquoted parentheses in the name, those
parentheses would be interpreted as an generic email header comment, and left
out.

For issue #305 by mattfbacon.
2025-03-07 15:48:24 +01:00
9a8bb1134b Allow multiple localpart catch all separators, e.g. both "+" and "-", for addresses you+anything@example.com and you-anything@example.com
The original config option stays, and we still use it for the common case where
we have a single separator. The "+" is configured by default. It is optional,
just like the new option "LocalpartCatchallSeparators" (plural).

When parsing the config file, we combine LocalpartCatchallSeparator and
LocalpartCatchallSeparators into a single list
LocalpartCatchallSeparatorsEffective, which we use throughout the code.

For issue #301 by janc13
2025-03-07 14:42:19 +01:00
d0b241499f smtpserver: In localserve mode, don't reject messages "From" domain "localhost" if it doesn't resolve to an IP
Mox does not look up names from the /etc/hosts file, only through DNS. But
"localhost" may not resolve through DNS, or when offline a DNS server may not
even be available. We will want deliveries to work in "mox localserve" mode.

Found by dstotijn.
2025-03-07 11:39:24 +01:00
2fc75b5b7b When adding a new domain, only set up RSA DKIM keys, not ed25519.
We'll need RSA DKIM keys for a long time to come because many systems don't
support ed25519 DKIM signatures. We've been adding both types of keys when
adding a new domain, and adding both two DKIM signatures to outgoing messages.
This works fine in practice, other mail servers are correctly ignoring the
ed25519 signature if they don't understand it. Unfortunately, it causess noise
in DMARC reports: Systems will warn that a DKIM check failed.  Sometimes with a
vague message about a missing key, or a 0-bit key. Sometimes they leave the
selector out of the report, making it hard to understand what's going on.  This
causes postmasters to investigate because they think something is wrong, only
to eventually find out it's all fine. So we're causing needless chores for
postmasters. By having only an RSA DKIM signature, we skip that noise. This
also reduces the number of DNS records postmasters have to add for a domain.

The small ed25519 DKIM DNS TXT records would make them preferrable over the
long multi-string RSA DKIM DNS TXT records (which are often hard to add
correctly through DNS operator web interfaces), but as mentioned, we'll have to
add the RSA DKIM keys anyway.

Another reason why RSA keys _may_ be preferrable over ed25519 keys is that with
RSA, signing is more computationally expensive than verifying, while it's the
other way around for ed25519 keys.

Admins can always add an ed25519 DKIM key to their domain. And we can always
switch back to adding them to new domains by default in the future.

For issue #299.
2025-03-07 11:15:29 +01:00
d78aa9d1d7 Fix previous commit, add missing error check and minor test refactor.
Unclear how I botched this up at the last minute before committing...
2025-03-07 10:30:55 +01:00
51f58a52c9 When opening an account, check for unexpected message files in the file system, and adjust the next message ID autoincrement sequence in the database to prevent future message delivery failures.
Just to be cautious. This hasn't happened yet in practice that I'm aware of.
But in theory, mox could crash after it has written the message file during
delivery, but before the database transaction was committed. In that case, a
message file for the "next message id" is already present. Any future delivery
attempts will get assigned the same message id by the database, but writing the
file will fail because there already is one, causing delivery to fail (until
the file is moved away).

When opening an account, we now check in the file system if newer files exist
than we expect based on the last existing message in the database. If so, we
adjust the message ID the database will assign next.
2025-03-07 10:15:27 +01:00
493cfee3e1 Mention NLnet funding continued in 2024/2025. 2025-03-06 20:26:25 +01:00
64f2f788b1 Run modernize to rewrite some older go constructs to newer ones
Mostly using slice.Sort, using min/max, slices.Concat, range of int and
fmt.Appendf for byte slices instead of strings.
2025-03-06 17:33:06 +01:00
f6132bdbc0 imapserver: Disable compress=deflate extension
It still blocks on reading partial flushes from clients, preventing progress
and eventually timing out. The flate library needs more changes to make this
work.

Connections from iOS mail sometimes timed out, not always.

The extension is simply not announced, code is still present.
2025-03-06 11:36:33 +01:00
e572d01341 Don't allow mailboxes named "." or ".." and normalize names during imports too
It only serves to confuse. When exporting such mailboxes in zip files or tar
files, extracting will cause trouble.
2025-03-06 11:36:33 +01:00
7872b138a5 Use consistent lower-case names when logging tls version and ciphersuite
Less shouty than upper case names.
2025-03-06 11:36:33 +01:00
aa2b24d861 webserver: don't raise a 500 server error for static file requests with overlong names
The Open call returns an errno ENAMETOOLONG. We didn't handle that specially,
so turned it into a "500 internal server error" response. When serving static
files, we should just return "404 file not found" errors. The file obviously
does not exist.

Saw a few overlong requests from bad bots not recognizing "data:" uri's inlined
in html files, trying to request them.
2025-03-06 11:36:33 +01:00
06b7c8bba0 Fix fuzzing for imapserver
Broken since introducing LoginAttempts. The fuzzing functions didn't get the
store.Init() call, and would hang on trying to send to the loginattemptwriter.
2025-03-06 11:36:33 +01:00
edfc24a701 rename a few variables for code consistency 2025-03-06 11:36:33 +01:00
96667a87eb Run go test with the -fullpath flag
Makes it easy to open the file in subpackages when an error occurs.
2025-03-06 11:36:29 +01:00
a5c64e4361 make code less indented 2025-03-06 11:35:44 +01:00
577944310c Improve expunged message/UID tracking in IMAP sessions, track synchronization history for mailboxes/annotations.
Keeping the message files around, and the message details in the database, is
useful for IMAP sessions that haven't seen/processed the removal of a message
yet and try to fetch it. Before, we would return errors. Similarly, a session
that has a mailbox selected that is removed can (at least in theory) still read
messages.

The mechanics to do this need keeping removed mailboxes around too. JMAP needs
that anyway, so we now keep modseq/createseq/expunged history for mailboxes
too. And while we're at it, for annotations as well.

For future JMAP support, we now also keep the mailbox parent id around for a
mailbox, with an upgrade step to set the field for existing mailboxes and
fixing up potential missing parents (which could possibly have happened in an
obscure corner case that I doubt anyone ran into).
2025-03-06 11:35:44 +01:00
684c716e4d Add missing wlocks around message delivery to account, mostly for tests. 2025-03-06 11:35:43 +01:00
2da280f2bb Fail tests if unhandled panics happened.
We normally recover from those situations, printing stack traces instead of
crashing the program. But during tests, we're not looking at the prometheus
metrics or all the output. Without these checks, such panics could go
unnoticed. Seems like a reasonable thing to add, unhandled panics haven't been
encountered in tests.
2025-03-06 11:35:43 +01:00
bc50c3bf7f In imapserver with RENAME of Inbox, we didn't check for the metadata quota.
Rename of Inbox is special, it copies the mailbox including metadata.
2025-03-06 11:35:43 +01:00
f5b67b5d3d Clean up the loginattemptclear goroutine with store.Close()
It is called a lot from the test code, so it would spawn lots of those goroutines.
2025-03-06 11:35:43 +01:00
2beb30cc20 Refactor how messages are added to mailboxes
DeliverMessage() is now MessageAdd(), and it takes a Mailbox object that it
modifies but doesn't write to the database (the caller must do it, and plenty
of times can do it more efficiently by doing it once for multiple messages).
The new AddOpts let the caller influence how many checks and how much of the
work MessageAdd() does. The zero-value AddOpts enable all checks and all the
work, but callers can take responsibility of some of the checks/work if it can
do it more efficiently itself.

This simplifies the code in most places, and makes it more efficient. The
checks to update per-mailbox keywords is a bit simpler too now.

We are also more careful to close the junk filter without saving it in case of
errors.

Still part of more upcoming changes.
2025-03-06 11:35:43 +01:00
7855a32852 switch from docker-compose to "docker compose"
now that my laptop doesn't have docker-compose anymore
2025-03-06 11:35:43 +01:00
82371ad15b simplify cleaning up temp files in gentestdata.go 2025-03-06 11:35:43 +01:00
9ce552368b Minor tweaks. 2025-03-06 11:35:43 +01:00
ea64936a67 Cleanup message file when DeliverMailbox fails.
Part of larger changes.
2025-03-06 11:35:43 +01:00
5ba51adb14 When retraining ham/spam messages, don't make existence of the messages optional.
If messages that should exist don't, that's a real error we don't want to hide.
Part of larger changes.
2025-03-06 11:35:43 +01:00
3b731b7afe various nits 2025-03-06 11:35:43 +01:00
7756150a69 Small tweak to LinkOrCopy, including defer for error handling 2025-03-06 11:35:43 +01:00
ffc7ed96bc When delivering a message to a mailbox, remember last dir we delivered to
In the common case, it's the same as the previous delivery. That means we don't
have to try to create the directory (fewer syscalls) and that we can sync the
dir to disk.

This also tweaks the defer handling in case of a late failure.
2025-03-06 11:35:43 +01:00
1037a756fa when delivering a message to a mailbox, lazily parse the parsed form of the message
it isn't always needed, so this can improve performance a bit.
come up as part of other refactoring.
2025-03-06 11:35:43 +01:00
3050baa15a consistently use store.CloseRemoveTempFile for closing and removing temp files 2025-03-06 11:35:43 +01:00
b822533df3 imapserver: Don't keep account write-locked during IMAP FETCH command
We effectively held the account write-locked by using a writable transaction
while processing the FETCH command. We did this because we may have to update
\Seen flags, for non-PEEK attribute fetches. This meant other FETCHes would
block, and other write access to the account too.

We now read the messages in a read-only transaction. We gather messages that
need marking as \Seen, and make that change in one (much shorter) database
transaction at the end of the FETCH command.

In practice, it doesn't seem too sensible to mark messages as seen
automatically. Most clients probably use the PEEK-variant of attribute fetches.

Related to issue #128.
2025-03-06 11:35:43 +01:00
caaace403a Add package smtp as fuzzing target since its addition in previous commit
The previous commit fixed an array out of bounds access that resulted in a
panic on an smtpserver connection. The panic is recovered and marked as
"unhandled panic" in metrics and the connection closed.
2025-03-06 11:15:25 +01:00
f10bb2c1ae smtp: add data reader fuzzer + fix OOB read 2025-03-06 09:57:13 +01:00
44d37892b8 imapserver: REPLACE commands when in read-only mode should fail 2025-02-26 18:39:41 +01:00
d7bd50b5a5 imapserver: fix spurious test failure due to recently added account consistency check
By removing message file while holding the account wlock. We were seeing
messages that weren't removed yet.
2025-02-26 18:33:01 +01:00
f235b6ad83 imapclient: log traces of sensitive data with traceauth, and of bulk data with tracedata
Similar to the imapserver. This also fixes tracing of APPEND messages, which
was completely absent before.
2025-02-26 18:13:20 +01:00
9c40205343 imapserver: Prevent spurious test failures due to compression layer being closed and TLS close-writes failing 2025-02-26 15:41:46 +01:00
062c3ac182 when writing updated word counts to the junk filter, remove entries where both counts are 0
no point in keeping them around.
also pass on error when getting a word from database returned an error.
2025-02-26 15:07:27 +01:00
394bdef39d In storage consistency checks, verify the junk filter has the expected word counts
Fix up a test or two. Simplify the XOR logic when we train the junk filter:
Only if junk or nonjunk is set, but not when both (or none are set). i.e. when
the values aren't the same.

Locking the account when we do consistency checks prevents spurious test
failures that may have been introduced in the previous commit.
2025-02-26 14:44:05 +01:00
aa85baf511 add consistency check (enabled during tests only) for unexpected message files in the account message directory 2025-02-26 11:40:36 +01:00
17de90e29d imapserver: Prevent spurious unhandled panics for connections with compress=deflate that break
Writing to a connection goes through the flate library to compress. That writes
the compressed bytes to the underlying connection. But that underlying
connection is wrapped to raise a panic with an i/o error instead of returning a
normal error.  Jumping out of flate leaves the internal state of the compressor
in undefined state. So far so good. But as part of cleaning up the connection,
we could try to flush output again. Specifically: If we were writing user data,
we had switched from tracing of protocol data to tracing of user data, and we
registered a defer that restored the tracing kind and flushed (to ensure data
was traced at the right level). That flush would cause a write into the
compressor again, which could panic with an out of bounds slice access due to
its inconsistent internal state.

This fix prevents that compressor panic in two ways:

1. We wrap the flate.Writer with a moxio.FlateWriter that keeps track of
   whether a panic came out of an operation on it. If so, any further operation
   raises the same panic. This prevents access to the inconsistent internal flate
   state entirely.
2. Once we raise an i/o error, we mark the connection as broken and that makes
   flushes a no-op.
2025-02-26 11:26:54 +01:00
ea55c85938 for trace logging, log size of the data (but not for redacted auth data, could be a password) 2025-02-26 10:14:07 +01:00
92a87acfcb Implement IMAP REPLACE extension, RFC 8508.
REPLACE can be used to update draft messages as you are editing. Instead of
requiring an APPEND and STORE of \Deleted and EXPUNGE. REPLACE works
atomically.

It has a syntax similar to APPEND, just allows you to specify the message to
replace that's in the currently selected mailbox. The regular REPLACE-command
works on a message sequence number, the UID REPLACE commands on a uid. The
destination mailbox, of the updated message, can be different. For example to
move a draft message from the Drafts folder to the Sent folder.

We have to do quite some bookkeeping, e.g. for updating (message) counts for
the mailbox, checking quota, un/retraining the junk filter. During a
synchronizing literal, we check the parameters early and reject if the replace
would fail (eg over quota, bad destination mailbox).
2025-02-25 23:27:19 +01:00
1066eb4c9f imapclient: add a type Append for messages for the APPEND-command, and accept multiple for servers with MULTIAPPEND capability
and a few nits.
2025-02-25 23:24:37 +01:00
88a68e9143 imapserver: properly accept literal8 for APPEND, since we claim to implement the BINARY extension
it's not just for the APPEND with "UTF8()", also any regular append needs to
accept literal8. found testing with pimalaya.
2025-02-25 23:07:56 +01:00