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.
Testing
Go tests
All Go packages have their own test suite, which provides easy and portable tests with decent enough coverage.
Integration tests
In the test/ directory there is a set of end to end integration tests,
written usually in a combination of bash and Python 3.
They're not expected to be portable, as that gets impractical very quickly, but should be usable in most Linux environments.
They provide critical coverage and integration tests for real life scenarios, as well as interactions with other software (like Exim or Dovecot).
Dependencies
The tests depend on the following things being installed on the system (listed as Debian package, for consistency):
msmtputil-linux(for/usr/bin/setsid)
Some individual tests have additional dependencies, and the tests are skipped if the dependencies are not found:
t-02-eximExim interaction tests:gettext-base(for/usr/bin/envsubst)- The
eximbinary available somewhere, but it doesn't have to be installed. There's a scriptget-exim4-debian.shto get it from the archives.
t-11-dovecotDovecot interaction tests:dovecot
t-15-driusan_dkimDKIM integration tests:- The
dkimsign dkimverify dkimkeygenbinaries, from driusan/dkim (no Debian package yet).
- The
t-18-haproxyHAProxy integration tests:haproxy
t-19-dkimpy: dkimpy integration tests:python3-dkim
For some tests, python >= 3.5 is required; they will be skipped if it's not available.
Most tests depend on the
$HOSTALIASES
environment variable being functional, and will be skipped if it isn't. This
works by default in most Linux systems, but note that the use of
systemd-resolved can prevent it from working properly.
Stress tests
Also in the test/ directory there is a set of stress tests, which generate
load against chasquid to measure performance and resource consumption.
While they are not exhaustive, they are useful to catch regressions and track improvements on the main code paths.
Fuzz tests
Some Go packages also have instrumentation to run fuzz testing against them, using the Go native fuzzing support.
This is critical for packages that handle sensitive user input, such as authentication encoding, aliases files, or username normalization.
Command-line tool tests
Each command-line tool has their own set of tests, see the test.sh file on
their corresponding directories.
Docker
The test/Dockerfile can be used to set up a suitable isolated environment to
run the integration and stress tests.
This is very useful for automated tests, or running the integration tests in constrained or non supported environments.
Automated tests
There are two sets of automated tests which are run on every commit to upstream, and also weekly:
-
Github Actions, configured in the
.githubdirectory, runs the Go tests, the integration tests, checks for vulnerabilities, and finally also builds the public Docker images. -
Cirrus CI, configured in the
.cirrus.ymlfile, runs Go tests on FreeBSD.
Coverage
The test/cover.sh script runs the integration tests in coverage mode, and
produces a code coverage report in HTML format, for ease of analysis.
The target is to keep coverage of the chasquid binary above 90%.