69 Commits

Author SHA1 Message Date
Timmy Welch
8e9ea901e9 Merge remote-tracking branch 'gh/main' 2025-12-12 01:49:12 -08:00
ThinkChaos
eeb2deb7f6 courier: Don't hardcode path to sleep binary in the tests
On NixOS, `/bin` is basically empty and this causes the courier tests
(which invoke `/bin/sleep`) to fail.

This patch fixes the tests by removing the hardcoded path.

https://github.com/albertito/chasquid/pull/73

Amended-by: Alberto Bertogli <albertito@blitiri.com.ar>
  Adjusted commit message.
2025-08-13 23:10:16 +01:00
Alberto Bertogli
9999a69086 aliases: Implement "via" aliases
This patch implements "via" aliases, which let us explicitly select a
server to use for delivery.

This feature is useful in different scenarios, such as a secondary MX
server that forwards all incoming email to a primary.

For now, it is experimental and the syntax and semantics are subject to
change.
2025-04-12 23:23:21 +01:00
Alberto Bertogli
8e4d31c74c Fix minor typos found by codespell
This patch fixes some minor typos in comments and strings found by
codespell.

While at it, also expand some variable names that were not typos, but
caused false positives, and end up being more readable anyway.
2025-04-06 14:02:56 +01:00
Alberto Bertogli
a996106eee smtpsrv: Strict CRLF enforcement in DATA contents
The RFCs are very clear that in DATA contents:

> CR and LF MUST only occur together as CRLF; they MUST NOT appear
> independently in the body.

https://www.rfc-editor.org/rfc/rfc5322#section-2.3
https://www.rfc-editor.org/rfc/rfc5321#section-2.3.8

Allowing "independent" CR and LF can cause a number of problems.

In particular, there is a new "SMTP smuggling attack" published recently
that involves the server incorrectly parsing the end of DATA marker
`\r\n.\r\n`, which an attacker can exploit to impersonate a server when
email is transmitted server-to-server.

https://www.postfix.org/smtp-smuggling.html
https://sec-consult.com/blog/detail/smtp-smuggling-spoofing-e-mails-worldwide/

Currently, chasquid is vulnerable to this attack, because Go's standard
libraries net/textproto and net/mail do not enforce CRLF strictly.

This patch fixes the problem by introducing a new "dot reader" function
that strictly enforces CRLF when reading dot-terminated data, used in
the DATA input processing.

When an invalid newline terminator is found, the connection is aborted
immediately because we cannot safely recover from that state.

We still keep the internal representation as LF-terminated for
convenience and simplicity.

However, the MDA courier is changed to pass CRLF-terminated lines, since
that is an external program which could be strict when receiving email
messages.

See https://github.com/albertito/chasquid/issues/47 for more details and
discussion.
2023-12-24 10:43:27 +00:00
Alberto Bertogli
fd9c6a748b courier/smtp: Retry over plaintext on STARTTLS errors
When the SMTP courier gets an error on STARTTLS (either because the
command itself failed, or because there was a low-level TLS negotiation
error), today we just fail that attempt.

This can cause messages to never be delivered if the underlying reason
is a server misconfiguration (e.g. a server certificate that Go cannot
parse). This is quite rare in practice, but it can happen.

To prevent this situation, this patch adds logic so that the SMTP
courier retries over plaintext when STARTTLS fails.

This is still subject to security level checks, so this type of failures
cannot be used to downgrade connections to domains we successfully
established a TLS connection previously.

Note that certificate validation issues are NOT included in this
type of failure, so they will not trigger a retry. The certificate
validation handling is unchanged by this patch.
2023-03-03 09:51:48 +00:00
Alberto Bertogli
4a00a83c23 Add tracing annotations
This patch changes several internal packages to receive and pass tracing
annotations, making use of the new tracing library, so we can have
better debugging information.
2022-11-13 11:09:19 +00:00
Alberto Bertogli
3ebe5c5173 Replace uses of ioutil
ioutil package was deprecated in Go 1.16, replace all uses with their
respective replacements.

This patch was generated with a combination of `gofmt -r`, `eg`, and
manually (for `ioutil.ReadDir`).
2022-11-12 20:06:35 +00:00
Alberto Bertogli
c738c7edf9 courier: Move the test fake server into a separate file
In future patches we will use the test fake server in other tests, so
move it to a separate file for clarity.
2022-11-12 16:34:35 +00:00
Alberto Bertogli
776bdc58ab Update Go doc comments to Go 1.19's format
This patch is the result of running Go 1.19's `gofmt` on the codebase,
which automatically updates all Go doc comments to the new format.

https://tip.golang.org/doc/go1.19#go-doc
2022-09-02 11:11:40 +01:00
Alberto Bertogli
e85c31782b Fix misc. linter issues (comments, variable naming, etc.)
We've accumulated a few linter issues around comments and a couple of
variable names.

While none of them is major, this patch cleans them up so it's easier to
go through the linter output, and we can start being more strict about
it.
2022-08-27 23:49:33 +01:00
Alberto Bertogli
02322a74e6 courier: Add tests for STS policy checks
This patch adds tests for STS policy checks in combination with TLS
security levels.

This helps ensure we're detecting mis-matches of TLS status
(plain/insecure/secure) and STS policy enforcement.
2021-11-26 13:25:31 +00:00
Alberto Bertogli
643f7576f0 courier: Use explicit certificate validation in the SMTP courier
When using STARTTLS, the SMTP courier needs to determine whether the
server certificates are valid or not.

Today, that's implemented via connecting once with full certificate
verification, and if that fails, reconnecting with verification
disabled.

This works okay in practice, but it is slower on insecure servers (due
to the reconnection), and some of them even complain because we connect
too frequently, causing delivery problems. The latter has only been
observed once, on the drv-berlin-brandenburg.de MX servers.

To improve on that situation, this patch makes the courier do the TLS
connection only once, and uses the verification results directly.

The behaviour of the server is otherwise unchanged. The only difference
is that when delivering mail to servers that have invalid certificates,
we now connect once instead of twice.

The tests are expanded to increase coverage for this particular case.
2021-10-25 12:41:24 +01:00
Alberto Bertogli
ed38945fca courier: Use DNSError.IsNotFound to identify NXDOMAIN
When resolving MX records, we need to distinguish between "no such
domain" and other kinds of errors. Before Go 1.13, this was not
possible, so we had a workaround that assumed any permanent error was a
"no such domain", which is not great, but functional.

Now that our minimum supported version is Go 1.15, we can remove the
workaround.

This patch replaces the workaround with proper logic using
DNSError.IsNotFound to identify NXDOMAIN results when resolving MX
records.

This requires to adjust a few tests, that used to work on environments
where resolving unknown domains (used for testing) returned a permanent
error, and now they no longer do so. Instead of relying on this
environmental property, we make the affected tests use our own DNS
server, which should make them more hermetic and reproducible.
2021-10-08 23:11:29 +01:00
lordwelch
660da4a85b Allow multiple authentications for the same relay based on from domain
Allow dovecot auth paths to use tcp
Add any domains of authenticated users to localDomains
2021-01-29 14:19:19 -08:00
lordwelch
e685366a28 Add support for sending mail to a specific relay
Update go-cmp and protobuf
Add support for dovecot auth over tcp
2021-01-24 13:27:07 -08:00
Alberto Bertogli
025cb2d96a courier: Rename Procmail to MDA
This patch renames courier.Procmail to courier.MDA, to make it more
obvious that the functionality is not tied to that particular MDA.

It's just for readability, there are no functional changes.
2020-09-17 02:47:42 +01:00
Alberto Bertogli
7fe42a368a monitoring: Add OpenMetrics exporter
This patch makes chasquid's monitoring server expose an OpenMetrics
metrics endpoint.

It adds a new package "expvarom" which implements an HTTP handler that
exports expvar variables in the OpenMetrics text format.

Then, the handler is registered by the monitoring server at /metrics
(where most things expect it to be).

The existing exported variables are also extended with descriptions,
which is optional, but improves the readability of the metrics.
2020-08-21 12:07:33 +01:00
Alberto Bertogli
13ee3ba482 courier: Use the hostname in SMTP HELO
The SMTP courier, which handles outgoing connections, uses the domain of
the envelope's from as the domain in the HELO/EHLO greeting.

This works fine in practice, but ideally the domain used in the greeting
should match the reverse DNS record. This used to be more relevant but
nowadays it is not really enforced; however, it sometimes comes up in
self checks, and might cause some confusion when troubleshooting.

So this patch makes it use the configured hostname instead, which is
under the users' control and more likely to be compliant. It also
simplifies the code.

The documentation of the hostname configuration option is also updated
to mention this behaviour.

Thanks to Jonas Seydel (thor77) for bringing this up.
2020-05-13 20:27:17 +01:00
Alberto Bertogli
d6b512166b Make it explicit when we are intentionally not checking errors
The linter complains that we're not checking for errors, but on some
cases it's on code paths were it is reasonable to do so (e.g. we're
closing the connection and it's a best-effort write).

This patch adjusts the code to make those cases explicit.
2020-04-14 12:01:01 +01:00
Alberto Bertogli
0fd3941cf0 courier: Don't call testing.T.Fatalf from a goroutine
Calling testing.T.Fatalf from a new goroutine is not supported; since
this should be quite exceptional, we just panic instead.
2020-04-14 12:01:01 +01:00
Alberto Bertogli
8fab350b59 courier: DNS temporary errors should cause temporary delivery failures
In the SMTP courier, when the MX lookup fails with a DNS temporary
error, we should propagate that up and cause delivery to fail with a
temporary error too.

That way the delivery can be attempted later by the queue.

However, the code today doesn't make the distinction and will treat any
temporary DNS error as a permanent delivery failure, which is a bug.

The bug was found by ludusrusso and reported in
https://github.com/albertito/chasquid/issues/4

This patch fixes the bug by propagating the error properly.
2020-03-12 22:07:22 +00:00
Alberto Bertogli
6641d858ad courier: Extend TODO for DNS error handling on Go >= 1.13
In https://github.com/albertito/chasquid/issues/4, ludusrusso comments
that the DNS lookup error handling in the SMTP courier could be improved
by using DNSError.IsNotFound.

That is true, but unfortunately it was only added in Go 1.13, and we are
currently trying to support Go 1.11 since that's what in Debian stable.

So this patch updates the TODO to note this, so that when we can use a
newer Go version, we improve this code.
2020-03-12 22:07:11 +00:00
Alberto Bertogli
2943f994e7 Use context.WithTimeout instead of context.WithDeadline
There are a few context.WithDeadline calls that can be simplified by
using context.WithTimeout.

At the time they were added, WithTimeout was too new so we didn't want
to depend on it. But now that the minimum Go version has been raised to
1.9, we can simplify the calls.

This patch does that simplification, which is purely mechanical, and
does not change the logic itself.
2019-07-13 13:44:25 +01:00
Alberto Bertogli
ec95131bb4 Miscellaneous style fixes
This patch has some miscellaneous style fixes to issues found by the
staticcheck tool.

There are no functional changes.
2019-02-10 12:46:15 +00:00
Alberto Bertogli
4db9ffec09 Code style improvements
This patch contains some minor code style improvements, to leave the
linter happier and generally follow best practices in some areas where
things snuck through.
2018-12-01 11:58:50 +00:00
Alberto Bertogli
4296e28074 test: Fix flaky courier test
In the upcoming Go release, logging from a finished testing.T triggers a
panic.  In the courier tests, this is possible because we don't wait for
completion of fakeServer before ending the test.

This patch makes the tests wait for fakeServer to finish before exiting,
removing the race.
2018-11-30 10:03:48 +00:00
Alberto Bertogli
2dfed059e4 MTA-STS is now RFC 8461
MTA-STS has been published as RFC 8461, with no major changes since the
last draft we updated (-18).

This patch updates the documentation accordingly (no code changes).
2018-09-26 21:42:50 +01:00
Alberto Bertogli
ccb9ba47d2 smtp: Remove --experimental__enable_sts flag
This patch removes the --experimental__enable_sts flag, making STS
checking the default.
2018-07-14 10:08:27 +01:00
Alberto Bertogli
8bf584bd86 sts: Don't pre-filter MX list, but skip them if needed
Instead of pre-filtering the MX list based on STS policy, just check
if it's allowed before each attempt, and skip it if not.

This simplifies the code.
2018-07-01 12:19:02 +01:00
Alberto Bertogli
252ab5d3e3 sts: Update to draft-ietf-uta-mta-sts-18
This patch updates the STS implementation from draft version 02 to 18.

The main changes are:

 - Policy is now in an ad-hoc format instead of JSON (😒).
 - Minor policy well-known URL change (now ends in ".txt").
 - Enforce HTTP media type == text/plain, as with the ad-hoc format this
   becomes much more important.
 - Simplify wildcard mx matching (same algorithm), extend test cases.
 - Valid modes are "enforce" (as before), "testing" (replaces "report"),
   and "none" (new).
2018-07-01 12:19:02 +01:00
Alberto Bertogli
23deaf1f88 Reinstate the MTA-STS (Strict Transport Security) implementation
This commit brings back the experimental MTA-STS (Strict Transport
Security) implementation, removed in commit
7f5bedf4aa.

We will continue development in the "sts" branch, subject to rebase,
until it is ready to be integrated into "next" again.
2018-07-01 12:19:02 +01:00
Alberto Bertogli
26017548ef courier: Extend MX lookup tests
This patch adds more tests to the MX lookup process, covering some
unusual cases that were missing.
2018-06-04 00:04:57 +01:00
Alberto Bertogli
f3b01cb493 docs: Add missing docstrings, adjust wording to match standard style
This patch adds a missing docstrings for exported identifiers, and
adjust some of the existing ones to match the standard style.

In some cases, the identifiers were un-exported after noticing they had
no external users.

Besides improving documentation, it also reduces the linter noise
significantly.
2018-03-04 16:00:06 +00:00
Alberto Bertogli
6867859d5c courier: Make the SMTP test not use the network
One of the SMTP tests was doing an external DNS lookup for a
non-existing host, which is reasonably harmless but makes the test less
hermetic.

This patch changes the non-existing host for an invalid address, which
has the same effect but avoids the network lookup.

This is a hack; ideally we would be able to override the resolver, but
Go does not implement that yet, and changing the code is not worth the
additional complexity.
2017-07-16 13:24:40 +01:00
Alberto Bertogli
9864f40f3b test: Tidy up creation and removal of test directories
We have many places in our tests where we create temporary directories,
which we later remove (most of the time). We have at least 3 helpers to
do this, and various places where it's done ad-hoc (and the cleanup is
not always present).

To try to reduce the clutter, and make the tests more uniform and
readable, this patch introduces two helpers in a new "testutil" package:
one for creating and one for removing temporary directories.

These new functions are safer, better tested, and make the tests more
consistent.  All the tests are updated to use them.
2017-07-14 02:02:43 +01:00
Alberto Bertogli
a016d78515 courier: Fix SMTP outgoing security level check
The outgoing security level checks are not being performed, because of a
bug: the courier thinks the "to"'s domain is always empty.

This patch fixes the bug by simplifying the logic, as there's no need
for the conditional (there is always a domain in the "to" address if it
got to the SMTP courier).
2017-07-14 01:06:09 +01:00
Alberto Bertogli
7f5bedf4aa Remove the MTA-STS (Strict Transport Security) implementation
This commit removes the experimental MTA-STS (Strict Transport Security)
implementation for now, as it's not up to date with the latest draft.

Development will continue on the "sts" branch, but this way it won't
block releases until it is ready.

Commits reverted:
 - cb6500b993
 - 0eeb964534
 - e66288e4b4
 - 216cf47ffa
 - d66b06de51
 - fe00750e39
 - 933ab54cd8
2017-04-11 00:58:59 +01:00
Alberto Bertogli
159aa97e8a Change the default MDA binary to "maildrop"
The current default is "procmail" for historical reasons, but the
program has been unmaintained for years and its use is not generally
advisable.

This patch changes the default MDA binary to "maildrop", which is a more
modern and reasonable MDA to use.

We keep the courier.Procmail name for now, as that's internal, but it
may be changed later.  Its documentation is updated to note that the
name is just for reference but it actually works with almost any binary.
2017-03-08 00:19:45 +00:00
Alberto Bertogli
9539cfd34d courier: Consider not finding any MX/A records a permanent error
Currently, if we can't find a mail server for a domain, we consider that
a transient failure (semi-accidentally, as we iterate over the (empty)
list of MXs and fall through the list).

It should be treated as a permanent error, in line with other DNS
issues (which is not ideal but seems to be generally accepted
behaviour).
2017-03-01 00:22:49 +00:00
Alberto Bertogli
216cf47ffa courier: Add STS policy checking to the SMTP courier
This patch extends the SMTP courier to (optionally) do STS policy
checking when delivering mail.

As STS support is currently experimental, we gate this behind a flag and
is disabled by default.
2017-02-28 22:27:15 +00:00
Alberto Bertogli
b8551729db smtp: Try all entries in MX, not just the first one
Currently, we pick the first host in the MX list, and attempt delivery
there. If it fails, we just report the failure to the queue, which will
wait for some time and then try again.

This is not ideal: we should fall back to the other MXs in the list, as
the first host could be having issues for a long time, and not
attempting with the rest just delays delivery.

This patch implements the fallback, so we try all MXs before deciding to
report a failed delivery (unless, of course, an MX returned a permanent
failure).
2017-02-28 22:27:15 +00:00
Alberto Bertogli
249831064f courier: Skip test if test/util/exitcode is not found
When building the Debian package, the tests are run in such a way that
test/util/exitcode is not found, which causes them to fail.

This patch is a workaround which skips the test if test/util/exitcode is
not found; for now we should consider it temporary, until either the
Debian packaging is fixed, or we decide that its environment is
reasonably enough to make it permanent.
:
2016-11-20 18:30:30 +00:00
Alberto Bertogli
1bc111f783 Improve the readability of some log messages
This patch contains a few changes to logging messages, to improve log
readability.

While at it, an obsolete TODO in the SMTP courier is removed.
2016-11-01 23:56:04 +00:00
Alberto Bertogli
60a7932bd3 log: Replace glog with a new logging module
glog works fine and has great features, but it does not play along well
with systemd or standard log rotators (as it does the rotation itself).

So this patch replaces glog with a new logging module "log", which by
default logs to stderr, in a systemd-friendly manner.

Logging to files or syslog is still supported.
2016-11-01 23:56:04 +00:00
Alberto Bertogli
5faffbbfe3 courier: Simplify procmail's execution logic
The way the procmail courier runs the command is unnecessary convoluted,
this patch simplifies it by using the corresponding standard tools.
2016-10-21 22:18:53 +01:00
Alberto Bertogli
c013c98283 domaininfo: New package to track domain (security) information
This patch introduces a new "domaininfo" package, which implements a
database with information about domains.  In particular, it tracks
incoming and outgoing security levels.

That information is used in incoming and outgoing SMTP to prevent
downgrades.
2016-10-21 22:15:09 +01:00
Alberto Bertogli
3e55b0d742 courier/smtp: Reorder EHLO domain logic
The logic that picks the domain we use for EHLO does not need to live
within the TLS retry cycle, and makes it harder to understand.

This patch moves it out of the way, to improve readability.
2016-10-10 00:51:05 +01:00
Alberto Bertogli
c4e8b22fd0 Introduce expvar counters
This patch introduces expvar counters to chasquid and the queue
packages.

For now there's only a handful of counters, but they will be expanded in
future patches.
2016-10-10 00:51:05 +01:00
Alberto Bertogli
08a5d19941 Add missing Close calls
The SMTP courier was not properly closing the connection, and chasquid's
closing of incoming connections was not ideal (it was closing the
underlying one, not necessarily the active one, like in the case of a jump
to TLS).

This patch fixes both by adding the missing calls to Close.
2016-10-10 00:51:05 +01:00