1
0
mirror of https://blitiri.com.ar/repos/chasquid synced 2025-12-17 14:37:02 +00:00
Commit Graph

256 Commits

Author SHA1 Message Date
Alberto Bertogli
3be7cd5160 safeio: Add tests for error conditions
This patch adds tests to verify how safeio behaves when *os.File
operations return various errors.

To do this, we allow the possibility of wrapping os.CreateTemp, so we
can simulate the errors during testing.

This is not pretty, but the code is small enough that the readability
overhead is minimal, and we get a lot of tests from it.
2024-03-07 23:07:37 +00: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
83ae4c3478 userdb: Add support for receive-only users
Some use cases, like receive-only MTAs, need domain users for receiving
emails, but have no real need for passwords since they will never use
submission.

Today, that is not supported, and those use-cases require the
administrator to come up with a password unnecessarily, adding
complexity and possibly risk.

This patch implements "receive-only users", which don't have a valid
password, thus exist for the purposes of delivering mail, but always
fail authentication.

See https://github.com/albertito/chasquid/issues/44 for more details and
rationale.

Thanks to xavierg who suggested this feature on IRC.
2023-12-03 11:59:26 +00:00
Alberto Bertogli
d93d7cae10 config: Quote strings when logging the configuration
When logging the configuration, we currently don't quote the string
values, which can make whitespace-induced problems difficult to identify
and troubleshoot.

This patch changes the formatting to always quote string values when
logging the configuration.
2023-12-02 15:01:31 +00:00
Alberto Bertogli
f5d8c68a39 test: Add the embedded aliases file to the fuzz corpus
This patch adds the embedded aliases file to the fuzz corpus, because it
is trivial to do so, and is a reasonable seed which will be naturally
adjusted over time as the package evolves (as it happened in recent
commits).
2023-10-07 16:41:26 +01:00
Alberto Bertogli
5cdbd9ff6e Fix documentation/comment typos
This patch fixes a variety of typos in documentation and comments, found
by running `codespell`.
2023-10-07 12:41:57 +01:00
Alberto Bertogli
8ded1f6f5e Auto-format protobuf files
This patch runs clang-format on the protobuf files, and also adds a
Makefile target for auto-formatting code (Go and protobuf) for
convenience.
2023-10-03 23:34:23 +01:00
Alberto Bertogli
d57037273c localrpc test: Don't call t.Fatal from a goroutine
The testing package does not allow t.Fatal to be called from a different
goroutine; however, we do that if the testing server fails listen or
accept connections.

Since that is unexpected and rare, this patch turns those calls into
panics.
2023-10-03 23:32:30 +01:00
Alberto Bertogli
74e7c96031 aliases: Drop characters when parsing, and support suffix-specific aliases
Today, when a user sets an alias with drop characters and/or suffixes,
those go unused, since we always "clean" addresses before alias
resolution.

This results in unexpected and surprising behaviour, and it's not
properly documented either.

This patch resolves this unexpected behaviour as follows:

- Drop characters are ignored, both at parsing time and at lookup time.
- Lookups are done including the suffixes first, and if that results in
  no matches, they are retried without suffixes.

This results in aliases working more intuitively for the most common use
cases: of users wanting to have different aliases for specific suffixes,
and not having to care for drop characters.

Hooks can be used to get different behaviour if needed, since the first
lookup is done with the address as-is.

Thanks to znerol@ (lo+github@znerol.ch) for reporting this, and the
discussion on how to fix it, in
https://github.com/albertito/chasquid/issues/41.
2023-09-24 09:33:01 +01:00
Alberto Bertogli
d086fbbb97 aliases: Make parsing functions methods of the resolver
Today, the parsing functions are stand-alone since they don't need
anything from the resolver.

But in future patches, that will change.

In anticipation of that, move those functions to be methods of the
resolver.
2023-09-24 09:32:32 +01:00
Alberto Bertogli
8bbb6118e5 aliases: Exists does not need to return the "clean" address
The aliases.Resolver.Exists function currently returns the "clean"
address (with the drop characters and suffixes removed), which is relied
upon in its only caller.

That, however, makes the logic more difficult to follow, hiding some
of the address manipulation behind what should be a read-only check.

So this patch reorganizes that code a little bit, removing the
"cleaning" of the address as part of Exists, and making it explicit when
needed instead.

This patch does not have any user-visible change in behaviour, it is
just internal reorganization.

This is in preparation for further patches which will improve the
handling of some aliases corner cases.
2023-09-24 09:32:32 +01:00
Alberto Bertogli
ddd1b6d96e chasquid: Run a localrpc server
This patch makes chasquid run a localrpc server, exporting two methods:
alias resolve, and domaininfo clear.

They will be used by chasquid-util in later patches.
2023-07-30 13:21:07 +01:00
Alberto Bertogli
360ac13a73 localrpc: Add a package for local RPC over UNIX sockets
This patch adds a new package for doing local lightweight RPC calls over UNIX
sockets. This will be used in later patches for communication between chasquid
and chasquid-util.
2023-07-30 13:21:07 +01:00
Alberto Bertogli
764c09e94d domaininfo: Add a Clear method to clear information for a given domain
This patch adds a Clear method to the domaininfo database, which removes
information for the given domain.

This can be used to manually make the server forget about a domain, in
case there are operational reasons to do so.

Today, this is done via chasquid-util (which removes the backing file),
but that is hacky, and this is part of replacing it with a cleaner
implementation.
2023-07-30 11:34:27 +01:00
Alberto Bertogli
a3f7914e29 sts: Remove unused struct member
There is no synchronization needed in the sts cache, because changes are
handled atomically on-disk and there is no in-memory persistence.
2023-07-28 10:05:15 +01:00
Alberto Bertogli
2b7e33a615 domaininfo: Do not reload the database periodically
It is not expected that the user modifies the domaininfo database behind
chasquid's back, and reloading it can be somewhat expensive.

So this patch removes the periodic reload, and instead makes it triggered
by SIGHUP so the user can trigger a reload manually if needed.
2023-07-28 10:05:15 +01:00
Alberto Bertogli
01a6d088e2 test: Add a set of tests for handling bad/invalid configs
This patch adds a set of tests to validate chasquid's handling of bad
and invalid configurations, to make sure we fail as expected.
2023-05-17 00:44:54 +01:00
Alberto Bertogli
e85ad54a73 Regenerate auto-generated files
This patch regenerates the auto-generated files.

There are no significant changes, the protobuf just get an updated
comment due to protoc version change, but it is just informational.

Two new TLS ciphers are added, matching the new IANA assignments.
2023-03-03 11:24:40 +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
efefee9fbb test: Update fuzz tests to use the built-in infrastructure
Go 1.18 supports fuzzing natively, so this patch migrates our fuzzing
tests to make use of it.
2023-02-05 12:30:25 +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
9c6661eca2 nettrace: Add a new tracing library
This commit introduces a new tracing library, that replaces
golang.org/x/net/trace, and supports (amongts other thing) nested
traces.

This is a minimal change, future patches will make use of the new
functionality.
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
6dfff9a790 modules: Update Go modules and regenerate protobufs
This patch does a general pass updating Go modules to recent versions, and
regenerates the protobufs accordingly.

The main purpose is to make sure people building from source are using
relatively recent versions of our dependencies.
2022-08-27 23:39:40 +01:00
Alberto Bertogli
5bb17c7066 Update build tag constraints
This patch updates all build tag constraints to add the new format,
alongside the old one, to maintain backwards compatibility.

This was done by using `go fmt`.

See https://go.dev/doc/go1.17#gofmt and
https://golang.org/design/draft-gobuild for more details.
2022-08-08 17:52:34 +01:00
Alberto Bertogli
f303e43082 aliases: Implement catch-all
This patch implements support for catch-all aliases, where users can add
a `*: destination` alias. Mails sent to unknown users (or other aliases)
will not be rejected, but sent to the indicated destination instead.

Please see https://github.com/albertito/chasquid/issues/23 and
https://github.com/albertito/chasquid/pull/24 for more discussion and
background.

Thanks to Alex Ellwein (aellwein@github) for the alternative patch and
help with testing; and to ThinkChaos (ThinkChaos@github) for help with
testing.
2022-03-11 20:51:06 +00:00
Alberto Bertogli
3255ff6801 modules: Update Go modules and regenerate protobufs
This patch does a general pass updating Go modules to recent versions, and
regenerates the protobufs accordingly.

The main purpose is to make sure people building from source are using
relatively recent versions of our dependencies.
2022-03-11 18:28:11 +00:00
Alberto Bertogli
d7ca50c3e0 aliases: Add tracing to Exists and Resolve
This patch adds tracing to aliases' Exist and Resolve functions, to help
troubleshoot problems with alias resolution.
2022-01-21 12:07:34 +00:00
Alberto Bertogli
feb10299be aliases: Skip resolution logic for non-local addresses
This patch skips the resolution logic if the address is not local.
Today, the resolution logic handles that case transparently, and returns
the original email address, so this should be a no-op.

However, having an explicit early check makes the resolution logic more
robust, and will simplify future patches.

Note this also means that the `alias-resolve` hook is no longer run for
non-local aliases, which should also help simplify their implementation.
2022-01-21 12:07:34 +00:00
Alberto Bertogli
67d0064f57 aliases: Simplify lookup logic, remove alias-exists hook
This patch simplifies the internal alias lookup logic, unifying it
across Resolve and Exists.

As part of this, the `alias-exists` hook is removed. It was redundant to
begin with, although it enabled a potential optimization, it isn't worth
the complexity. The timeout for execution of both was the same.

This change should be backwards-compatible because `alias-resolve` is
still used, and the semantics haven't changed.
2022-01-21 12:07:34 +00:00
Alberto Bertogli
fa1db7d81a config: Support "" values for drop_characters and suffix_separators
If the `drop_characters` or `suffix_separators` options are set to "",
currently instead of the empty string, their default value is used instead.

This is a bug, and it also happens on other config options, but because
the others have to be set in order for chasquid to function, it's not a
problem in practice.

Thanks Björn Busse (bbusse@github) for finding and reporting this
problem, on irc and in https://github.com/albertito/chasquid/issues/25.

This patch fixes the problem by marking these fields explicitly
optional, which enables presence testing, as described in the protobuf
documentation:
https://github.com/protocolbuffers/protobuf/blob/master/docs/field_presence.md.
2022-01-21 12:07:34 +00: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
90d385556f testlib: Add GenerateCert function
This patch moves the GenerateCert function from the smtpsrv tests to the
common testlib, so it can be used by other tests in the future.
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
Alberto Bertogli
6633f0785c smtpsrv: Remove obsolete call to tls.BuildNameToCertificate
tls.BuildNameToCertificate has been deprecated, and calling it is no
longer necessary since Go 1.14.

Now that our minimum supported Go version is 1.15, we can remove it.
2021-10-08 23:11:29 +01:00
Alberto Bertogli
f137702f23 trace: Remove restriction on tracing pages
By default, golang.org/x/net/trace currently only allows the tracing
pages to be seen from localhost.

This restriction can be confusing for people accessing the monitoring
server remotely, and adds no value in our environment.

The monitoring server already exports very sensitive information, and
must be enabled with care, and is not on by default. This is well
documented.

This patch removes the restriction, making all the monitoring pages
equally accessible.
2021-06-11 23:29:52 +01:00
Alberto Bertogli
cfe0e48c0a auth: Allow users without a domain
Some deployments already have users that authenticate without a domain.
Today, we refuse to even consider those, and reject them at parsing time.

However, it is a use-case worth supporting, at least with some
restrictions that make the complexity manageable.

This patch changes the auth package to support authenticating users
without an "@domain" part.

Those requests will always be directly passed on to the fallback
authenticator, if available.

The dovecot fallback authenticator can already handle this case just fine.
2021-06-11 20:09:15 +01:00
Alberto Bertogli
099e2e2269 expvarom: Use application/openmetrics-text as content type
The openmetrics proposed standard says we should use the
`application/openmetrics-text` content type when exporting the metrics.

Currently we use `text/plain` for backwards compatibility with
Prometheus, but the new content type is apparently supported since 2018,
so it should be safe to update to match the current proposed standard.
2021-06-11 12:48:45 +01:00
Alberto Bertogli
8c8e64dc29 smtpsrv: Reject HTTP commands
To help with defense-in-depth on cross-protocol attacks (e.g.
https://alpaca-attack.com/), this patch makes chasquid reject HTTP
commands.
2021-06-11 10:35:51 +01:00
Alberto Bertogli
85305f4bd9 smtpsrv: Close the connection after 3 errors (lowering from 10)
Today, we close the connection after 10 errors. While this is fine for
normal use, it is unnecessarily large.

Lowering it to 3 helps with defense-in-depth for cross-protocol attacks
(e.g. https://alpaca-attack.com/), while still being large enough for
useful troubleshooting and normal operation.

As part of this change, we also remove the AUTH-specific failures limit,
because they're covered by the connection limit.
2021-06-11 10:34:20 +01:00
Alberto Bertogli
44eb0b903a smtpsrv: Quote unknown commands for debugging
When we receive unknown commands, we use the first 6 bytes for
troubleshooting (e.g. put them in traces and exported metrics).

While this is safe, since the different places know how to quote them
properly, it makes things more difficult to analyse, since it's not
uncommon to see be binary blobs.

This patch makes us use the ascii-quoted version instead, to make things
easier to analyze.
2021-06-11 10:34:20 +01:00
Alberto Bertogli
b9f147fa8b trace: Use request tracing in auth and domaininfo
This patch adds tracing for the auth and domaininfo modules. In the
latter, we replace the long-running event with the short-term request
tracing, which is more practical and useful.

There are no logic changes, it only adds tracing instrumentation to help
troubleshooting.
2021-06-05 18:37:07 +01:00
Alberto Bertogli
fb680336f0 modules: Update Go modules and regenerate protobufs
This patch does a general pass updating Go modules to recent versions,
and regenerates the protobufs accordingly.

The main purpose is to make sure people building from source are using
relatively recent versions of our dependencies.

We also regenerate protobufs since the newer versions of the liberaries
have a much cleaner dependency tree, which speeds up fetches.
2021-05-31 11:43:06 +01:00
Alberto Bertogli
d3396ace0b smtpsrv: Return a temporary error when we fail to check if a user exists
When we fail to check if a user exists, we currently return a permanent
error, which can be misleading and also make things more difficult to
troubleshoot.

This patch makes chasquid return a temporary error in that case.

Thanks to Thor77 (thor77@thor77.org) for suggesting this change.
2021-05-30 00:39:24 +01:00
Alberto Bertogli
fa651e74e3 dovecot: Retry auto-detect until we find a usable socket pair
Currently, chasquid attempts to auto-detect dovecot sockets when
starting up (if needed). If autodetection fails, chasquid emits an
error, continues serving, and never tries again.

This can be problematic if chasquid starts up before dovecot, and at the
time the dovecot sockets are not present (e.g. after a reboot). In that
case, chasquid will not use dovecot for authentication even after
dovecot has started.

This patch changes the autodetect logic, by doing autodetection at
startup and on each request, until we find a working pair of sockets.
Once we do, they're used consistently.

That way, if dovecot is not ready when chasquid starts, it's not a
problem and chasquid will start using dovecot once it becomes available.

Thanks to Thor77 (thor77@thor77.org) for reporting and helping
troubleshoot this issue.
2021-05-24 10:21:33 +01:00
Alberto Bertogli
34b1f6cf21 expvarom: Add EOF marker, and minor documentation updates
This patch adds the EOF marker as required by the new specification, and
also adds some links to it in the comments, as reference.
2021-01-16 13:08:46 +00:00