This patch implements a post-DATA hook, which is run after receiving the
data but before sending a reply.
It can be used to implement content filtering when receiving email, for
example for passing the email through an anti-spam or an anti-virus.
This patch moves chasquid's Server and Conn structures to their own
smtpsrv package, to make chasquid.go a bit more readable. It also helps
clarify the relation between Server and Conn.
There are no functional changes.
Note that git can still track the history across this commit (e.g. git
gui blame shows the right data).
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.
This patch is the result of running go vet, go fmt -s and the linter,
and fixing some of the things they noted/suggested.
There shouldn't be any significant logic changes, it's mostly
readability improvements.
Add some links to the monitoring HTML index, to reflect the new
additions.
Also reorder to make it more practical, and default to the expanded view
for the tracer links.
HELO and EHLO both take a mandatory parameter, which also should be used
in the Received header.
This patch tracks and enforces that parameter, and also updates the
Received header generation to use it.
https://tools.ietf.org/html/rfc5321#section-4.4
If a connection has accumulated 10 errors, it's very likely that
something has gone significantly wrong, or they're just probing/abusing
the service.
This patch makes chasquid break the connection after 10 errors.
The number is arbitrary, we may adjust it later.
We should ignore the domains' case, and treat them uniformly, specially when it
comes to local domains.
This patch extends the existing normalization (IDNA, keeping domains as
UTF8 internally) to include case conversion and NFC form for
consistency.
This patch implements local username normalization using PRECIS
(https://tools.ietf.org/html/rfc7564,
https://tools.ietf.org/html/rfc7613)
It makes chasquid accept local email and authentication regardless of
the case. It covers both userdb and aliases.
Note that non-local usernames remain untouched.
Currently, we do SPF checks for all connections.
However, authenticated users will be sending email from different
locations, applying SPF to them will result in false positives.
So this patch makes chasquid skip SPF checking if the connection is
authenticated.
This patch implements some measures against email loops, such as keeping
a limit on the lenght of an address, and rejecting email that has too
many Received headers.
It's not perfect (a server could be actively removing Received headers),
but it should cover the normal accidents and misconfigurations.
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.
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.
This patch tidies up the MAIL FROM and RCPT TO handling, in particular:
- Preserve the case on received email. It could be outgoing and we
should not change it.
- Accept (but ignore) RCPT TO options, instead of failing.
- Fix some error codes to make them follow the RFC.
This patch reviews various debug and informational messages, making more
uniform use of tracing, and extends the monitoring http server with
useful information like an index and a queue dump.
This patch adds initial support for SMTPUTF8, which for now consists of just
advertising it.
We support most of it, but sending emails over SMTP requires further work, as
the SMTP courier does not support this yet (it's not in Go's standard
library). That will come in subsequent patches, along with IDNA handling.
https://tools.ietf.org/html/rfc6531.html
This patch implements the first steps of support for IDNA (Internationalized
Domain Names for Applications).
Internally, we maintain the byte-agnostic representation, including
configuration.
We support receiving IDNA mail over SMTP, which we convert to unicode for
internal handling.
Local deliveries are still kept agnostic.
For sending over SMTP, we use IDNA for DNS resolution, but there are some
corner cases pending in the SMTP courier that are tied with SMTPUTF8 and will
be fixed in subsequent patches.
There are cases, like email bounces and forwarding, where a remote server may
use an address within our domain as "MAIL FROM".
The current test at MAIL FROM will block them, which can be quite an
inconvenience as those cases are not that rare.
It's a nice test but doesn't add much, as we don't really pass the validation
along, and we still do relay and user checks on RCPT TO.
So this patch removes that test.
Having the certificates inside the domain directory may cause some confusion,
as it's possible they're not for the same name (they should be for the MX we
serve as, not the domain itself).
So it's not a problem if we have domains with no certificates (we could be
their MX with another name), and we could have more than one certificate per
"domain" (if we act as MXs with different names).
So this patch moves the certificates out of the domains into a new certs/
directory, where we do a one-level deep lookup for the files.
While at it, change the names of the files to "fullchain.pem" and
"privkey.pem", which match the names generated by the letsencrypt client, to
make it easier to set up. There's no general convention for these names
anyway.
This patch performs some minor cleanups for things detected by "go vet":
- Remove one line of unreachable code.
- Don't leak contexts until their deadline expires, cancel them.
It's more convenient and in line with standard practice to fail RCPT TO if the
user does not exist.
This involves making the server and client aware of aliases, but it doesn't
end up being very convoluted, and simplifies other code.
Today, if the aliases file does not exist when chasquid starts up, the entire
domain will be skipped from aliases resolution.
That's a bug, as it means we don't perform character and suffix replacements
for known domains, and is also an inconvenience as it forces us to reload the
daemon when adding a file for a known domain.
This patch fixes this by adding them unconditionally, even if the file does
not exist.
When we permanently failed to deliver to one or more recipients, send delivery
status notifications back to the sender.
To do this, we need to extend a couple of internal structures, to keep track
of the original destinations (so we can include them in the message, for
reference), and the hostname we're identifying ourselves as (this is arguable
but we're going with it for now, may change later).
This patch removes chasquid-userdb and adds a more generic and extensive
chasquid-util, that supports various operations on user databases as well as
aliases lookups.
The code is not very pretty but for now I took a more practical approach, the
tool is ancillary and can be tidied up later.
This patch tidies up the Procmail courier:
- Move the configuration options to the courier instance, instead of using
global variables.
- Implement more useful string replacement options.
- Use exec.CommandContext for running the command with a timeout.
As a consequence of the first item, the queue now takes the couriers via its
constructor.
This patch makes the queue read and write items to disk.
It uses protobuf for serialization. We serialize to text format to make
manual troubleshooting easier, as the performance difference is not very
relevant for us.
In some cases, it's be useful to have references to directories relative
to the configuration itself.
So this patch makes chasquid do a Chdir into it, so we can assume it in
the rest of the code and config.
This patch adds checks that verify:
- The envelope from must match the authenticated user. This prevents
impersonation at the envelope level (while still allowing bounces, of
course).
- If the destination is remote, then the user must have completed
authentication. This prevents unauthorized relaying.
The patch ends up adjusting quite a few tests, as they were not written
considering these restrictions so they have to be changed accordingly.
We want to be able to distinguish between connections for SMTP and connections
for submission, so we can make different policy decisions.
To do that, we first make the configuration aware of the different kinds of
addresses. This is done in this patch in a backwards-incompatible way, but at
this point in time it is ok to do so.
Then, we extend systemd's socket passing library to support socket naming, so
we can tell the different sockets apart. This is done via the
LISTEN_FDNAMES/FileDescriptorName mechanism.
And finally we make the server and connection types aware of the socket mode.
This patch does various minor style and simplification cleanups, fixing things
detected by tools such as go vet, gofmt -s, and golint.
There are no functional changes, this change is purely cosmetic, but will
enable us to run those tools more regularly now that their output is clean.
This patch makes chasquid flush logs periodically, and also before exiting,
even if it gets killed via the usual signals (including Ctrl-C).
Both things help troubleshooting when there is not much traffic, or we kill
the server manually (including during automated tests).
MAIL FROM commands usually come in the form of:
MAIL FROM:<from@from> BODY=8BITMIME
Note that there's extra parameters after the address, which for now we want to
ignore.
The current parser doesn't ignore them, and relies on mail.ParseAddress doing
so (that is, on mail.ParseAddress("<from> BODY=8BITMIME") working).
However, in go 1.7, the parser will get more strict and start to fail these
cases.
To fix this, we change the way we parse the line to use fmt.Sprintf, which is
much nicer than splitting by hand, and is more readable as well.
The routing courier is a nice idea in theory, but at least for now, we want
the queue to be aware of when a destination is local so we can implement
differentiated logic.
This may change in the future, though, but at the moment it's not clear that
the abstractions will be worth it.
So this patch removes it, and makes the queue do the routing. There is no
difference in how the two are handled yet, those will come in subsequent
patches.
This patch implements the AUTH SMTP command, using per-domain user databases.
Note that we don't really use or check the validation for anything, this is
just implementing the command itself.
This patch adds configuration options for the MDA binary and command line
arguments, and changes the (soon to be renamed) procmail courier to make use
of them.
We implement the VRFY and EXPN commands, but as no-ops. The RFC allows this,
and most implementations seem to do it this way too.
While at it, merge the tests for simple commands like these into one.