author | Alberto Bertogli
<albertito@blitiri.com.ar> 2016-07-22 00:52:27 UTC |
committer | Alberto Bertogli
<albertito@blitiri.com.ar> 2016-09-12 03:06:56 UTC |
parent | 92d16a0ca90ee57e0297cf6fb37fdbc16199e922 |
test/t-simple_local/config/chasquid.conf | +5 | -0 |
test/t-simple_local/config/domains/testserver/users | +2 | -0 |
test/t-simple_local/content | +4 | -0 |
test/t-simple_local/hosts | +1 | -0 |
test/t-simple_local/msmtprc | +21 | -0 |
test/t-simple_local/run.sh | +29 | -0 |
test/util/generate_cert.go | +159 | -0 |
test/util/lib.sh | +74 | -0 |
test/util/mail_diff | +34 | -0 |
test/util/test-mda | +14 | -0 |
diff --git a/test/t-simple_local/config/chasquid.conf b/test/t-simple_local/config/chasquid.conf new file mode 100644 index 0000000..1302399 --- /dev/null +++ b/test/t-simple_local/config/chasquid.conf @@ -0,0 +1,5 @@ +address: ":1025" +monitoring_address: ":1099" + +mail_delivery_agent_bin: "test-mda" +mail_delivery_agent_args: "%user%@%domain%" diff --git a/test/t-simple_local/config/domains/testserver/users b/test/t-simple_local/config/domains/testserver/users new file mode 100644 index 0000000..bd031fe --- /dev/null +++ b/test/t-simple_local/config/domains/testserver/users @@ -0,0 +1,2 @@ +#chasquid-userdb-v1 +user SCRYPT@n:14,r:8,p:1,l:32,r00XqNmRkV505R2X6KT8+Q== aAiBBIVNNzmDXwxLLdJezFuxGtc2/wcHsy3FiOMAH4c= diff --git a/test/t-simple_local/content b/test/t-simple_local/content new file mode 100644 index 0000000..76a8b16 --- /dev/null +++ b/test/t-simple_local/content @@ -0,0 +1,4 @@ +Subject: Prueba desde el test + +Crece desde el test el futuro +Crece desde el test diff --git a/test/t-simple_local/hosts b/test/t-simple_local/hosts new file mode 100644 index 0000000..2b9b623 --- /dev/null +++ b/test/t-simple_local/hosts @@ -0,0 +1 @@ +testserver localhost diff --git a/test/t-simple_local/msmtprc b/test/t-simple_local/msmtprc new file mode 100644 index 0000000..00190f0 --- /dev/null +++ b/test/t-simple_local/msmtprc @@ -0,0 +1,21 @@ +account default + +host testserver +port 1025 + +tls on +tls_trust_file config/domains/testserver/cert.pem + +from user@testserver + +auth on +user user@testserver +password secretpassword + +account baduser : default +user unknownuser@testserver +password secretpassword + +account badpasswd : default +user user@testserver +password badsecretpassword diff --git a/test/t-simple_local/run.sh b/test/t-simple_local/run.sh new file mode 100755 index 0000000..eb8f501 --- /dev/null +++ b/test/t-simple_local/run.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +set -e +. $(dirname ${0})/../util/lib.sh + +init + +generate_certs_for testserver + +chasquid -v=2 --log_dir=.logs --config_dir=config & +wait_until_ready 1025 + +run_msmtp someone@testserver < content + +wait_for_file .mail/someone@testserver + +mail_diff content .mail/someone@testserver + +if run_msmtp -a baduser someone@testserver < content 2> /dev/null; then + echo "ERROR: successfully sent an email with a bad password" + exit 1 +fi + +if run_msmtp -a badpasswd someone@testserver < content 2> /dev/null; then + echo "ERROR: successfully sent an email with a bad password" + exit 1 +fi + +success diff --git a/test/util/generate_cert.go b/test/util/generate_cert.go new file mode 100644 index 0000000..e488792 --- /dev/null +++ b/test/util/generate_cert.go @@ -0,0 +1,159 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// Generate a self-signed X.509 certificate for a TLS server. Outputs to +// 'cert.pem' and 'key.pem' and will overwrite existing files. + +package main + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "flag" + "fmt" + "log" + "math/big" + "net" + "os" + "strings" + "time" +) + +var ( + host = flag.String("host", "", "Comma-separated hostnames and IPs to generate a certificate for") + validFrom = flag.String("start-date", "", "Creation date formatted as Jan 1 15:04:05 2011") + validFor = flag.Duration("duration", 365*24*time.Hour, "Duration that certificate is valid for") + isCA = flag.Bool("ca", false, "whether this cert should be its own Certificate Authority") + rsaBits = flag.Int("rsa-bits", 2048, "Size of RSA key to generate. Ignored if --ecdsa-curve is set") + ecdsaCurve = flag.String("ecdsa-curve", "", "ECDSA curve to use to generate a key. Valid values are P224, P256, P384, P521") +) + +func publicKey(priv interface{}) interface{} { + switch k := priv.(type) { + case *rsa.PrivateKey: + return &k.PublicKey + case *ecdsa.PrivateKey: + return &k.PublicKey + default: + return nil + } +} + +func pemBlockForKey(priv interface{}) *pem.Block { + switch k := priv.(type) { + case *rsa.PrivateKey: + return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)} + case *ecdsa.PrivateKey: + b, err := x509.MarshalECPrivateKey(k) + if err != nil { + fmt.Fprintf(os.Stderr, "Unable to marshal ECDSA private key: %v", err) + os.Exit(2) + } + return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b} + default: + return nil + } +} + +func main() { + flag.Parse() + + if len(*host) == 0 { + log.Fatalf("Missing required --host parameter") + } + + var priv interface{} + var err error + switch *ecdsaCurve { + case "": + priv, err = rsa.GenerateKey(rand.Reader, *rsaBits) + case "P224": + priv, err = ecdsa.GenerateKey(elliptic.P224(), rand.Reader) + case "P256": + priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + case "P384": + priv, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader) + case "P521": + priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader) + default: + fmt.Fprintf(os.Stderr, "Unrecognized elliptic curve: %q", *ecdsaCurve) + os.Exit(1) + } + if err != nil { + log.Fatalf("failed to generate private key: %s", err) + } + + var notBefore time.Time + if len(*validFrom) == 0 { + notBefore = time.Now() + } else { + notBefore, err = time.Parse("Jan 2 15:04:05 2006", *validFrom) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to parse creation date: %s\n", err) + os.Exit(1) + } + } + + notAfter := notBefore.Add(*validFor) + + serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) + serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) + if err != nil { + log.Fatalf("failed to generate serial number: %s", err) + } + + template := x509.Certificate{ + SerialNumber: serialNumber, + Subject: pkix.Name{ + Organization: []string{"Acme Co"}, + }, + NotBefore: notBefore, + NotAfter: notAfter, + + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + } + + hosts := strings.Split(*host, ",") + for _, h := range hosts { + if ip := net.ParseIP(h); ip != nil { + template.IPAddresses = append(template.IPAddresses, ip) + } else { + template.DNSNames = append(template.DNSNames, h) + } + } + + if *isCA { + template.IsCA = true + template.KeyUsage |= x509.KeyUsageCertSign + } + + derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv) + if err != nil { + log.Fatalf("Failed to create certificate: %s", err) + } + + certOut, err := os.Create("cert.pem") + if err != nil { + log.Fatalf("failed to open cert.pem for writing: %s", err) + } + pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) + certOut.Close() + + keyOut, err := os.OpenFile("key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) + if err != nil { + log.Fatalf("failed to open key.pem for writing:", err) + return + } + pem.Encode(keyOut, pemBlockForKey(priv)) + keyOut.Close() +} diff --git a/test/util/lib.sh b/test/util/lib.sh new file mode 100644 index 0000000..07c4913 --- /dev/null +++ b/test/util/lib.sh @@ -0,0 +1,74 @@ +# Library to write the shell scripts in the tests. + +function init() { + if [ "$V" == "1" ]; then + set -v + fi + + export TBASE="$(realpath `dirname ${0}`)" + cd ${TBASE} + + export UTILDIR="$(realpath ${TBASE}/../util/)" + + # Remove the directory where test-mda will deliver mail, so previous + # runs don't interfere with this one. + rm -rf .mail + + # Set traps to kill our subprocesses when we exit (for any reason). + # https://stackoverflow.com/questions/360201/ + trap "exit" INT TERM + trap "kill 0" EXIT +} + +function generate_cert() { + go run ${UTILDIR}/generate_cert.go "$@" +} + +function chasquid() { + # HOSTALIASES: so we "fake" hostnames. + # PATH: so chasquid can call test-mda without path issues. + HOSTALIASES=${TBASE}/hosts \ + PATH=${UTILDIR}:${PATH} \ + go run ${TBASE}/../../chasquid.go "$@" +} + +function run_msmtp() { + # msmtp will check that the rc file is only user readable. + chmod 600 msmtprc + + HOSTALIASES=${TBASE}/hosts \ + msmtp -C msmtprc "$@" +} + +function mail_diff() { + ${UTILDIR}/mail_diff "$@" +} + +function success() { + echo "SUCCESS" +} + +# Wait until there's something listening on the given port. +function wait_until_ready() { + PORT=$1 + + while ! nc -z localhost $PORT; do + sleep 0.1 + done +} + +# Wait for the given file to exist. +function wait_for_file() { + while ! [ -e ${1} ]; do + sleep 0.1 + done +} + +# Generate certs for the given domain. +function generate_certs_for() { + mkdir -p config/domains/${1} + ( + cd config/domains/${1} + generate_cert -ca -duration=1h -host=${1} + ) +} diff --git a/test/util/mail_diff b/test/util/mail_diff new file mode 100755 index 0000000..5e9667e --- /dev/null +++ b/test/util/mail_diff @@ -0,0 +1,34 @@ +#!/usr/bin/env python + +import difflib +import email.parser +import mailbox +import sys + +f1, f2 = sys.argv[1:3] + +expected = email.parser.Parser().parse(open(f1)) + +mbox = mailbox.mbox(f2, create=False) +msg = mbox[0] + +diff = False + +for h, val in expected.items(): + if h not in msg: + print("Header missing: %r" % h) + diff = True + continue + if msg[h] != val: + print("Header %r differs: %r != %r" % (h, val, msg[h])) + diff = True + +if expected.get_payload() != msg.get_payload(): + diff = True + exp = expected.get_payload().splitlines() + got = msg.get_payload().splitlines() + print("Payload differs:") + for l in difflib.ndiff(exp, got): + print(l) + +sys.exit(0 if not diff else 1) diff --git a/test/util/test-mda b/test/util/test-mda new file mode 100755 index 0000000..617e393 --- /dev/null +++ b/test/util/test-mda @@ -0,0 +1,14 @@ +#!/bin/bash + +set -e + +mkdir -p .mail + +# TODO: use flock to lock the file, to prevent atomic writes. +echo "From ${1}" >> .mail/.tmp-${1} +cat >> .mail/.tmp-${1} +X=$? +if [ -e .mail/.tmp-${1} ]; then + mv .mail/.tmp-${1} .mail/${1} +fi +exit $X