mirror of
https://github.com/mjl-/mox.git
synced 2024-12-27 00:43:48 +03:00
e0c36edb8f
embarrassingly, we didn't accept all reports we generated. after the changed handling of reports about mx/mail host vs recipient domains, would send reports to mail hosts about multiple recipient domains + the mail host. and we included a policy domain of the mail host when sending to a recipient domain. we were still being strict in what we accepted: only a single domain in total in the entire report, and we still enforced that a report sent to the mx host tlsrpt address only contained the mx host as policy domain. and likewise for recipient domains and their tls reporting addresses. those checks would reject reports generated by a mox instance. this probably only happens with dane configured, probably most users haven't seen it because of that. somewhat related to issue #125
335 lines
10 KiB
Go
335 lines
10 KiB
Go
package tlsrptdb
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/mjl-/mox/config"
|
|
"github.com/mjl-/mox/dns"
|
|
"github.com/mjl-/mox/mlog"
|
|
"github.com/mjl-/mox/mox-"
|
|
"github.com/mjl-/mox/tlsrpt"
|
|
)
|
|
|
|
var ctxbg = context.Background()
|
|
var pkglog = mlog.New("tlsrptdb", nil)
|
|
|
|
const reportJSON = `{
|
|
"organization-name": "Company-X",
|
|
"date-range": {
|
|
"start-datetime": "2016-04-01T00:00:00Z",
|
|
"end-datetime": "2016-04-01T23:59:59Z"
|
|
},
|
|
"contact-info": "sts-reporting@company-x.example",
|
|
"report-id": "5065427c-23d3-47ca-b6e0-946ea0e8c4be",
|
|
"policies": [{
|
|
"policy": {
|
|
"policy-type": "sts",
|
|
"policy-string": ["version: STSv1","mode: testing",
|
|
"mx: *.mail.company-y.example","max_age: 86400"],
|
|
"policy-domain": "test.xmox.nl",
|
|
"mx-host": ["*.mail.company-y.example"]
|
|
},
|
|
"summary": {
|
|
"total-successful-session-count": 5326,
|
|
"total-failure-session-count": 303
|
|
},
|
|
"failure-details": [{
|
|
"result-type": "certificate-expired",
|
|
"sending-mta-ip": "2001:db8:abcd:0012::1",
|
|
"receiving-mx-hostname": "mx1.mail.company-y.example",
|
|
"failed-session-count": 100
|
|
}, {
|
|
"result-type": "starttls-not-supported",
|
|
"sending-mta-ip": "2001:db8:abcd:0013::1",
|
|
"receiving-mx-hostname": "mx2.mail.company-y.example",
|
|
"receiving-ip": "203.0.113.56",
|
|
"failed-session-count": 200,
|
|
"additional-information": "https://reports.company-x.example/report_info ? id = 5065427 c - 23 d3# StarttlsNotSupported "
|
|
}, {
|
|
"result-type": "validation-failure",
|
|
"sending-mta-ip": "198.51.100.62",
|
|
"receiving-ip": "203.0.113.58",
|
|
"receiving-mx-hostname": "mx-backup.mail.company-y.example",
|
|
"failed-session-count": 3,
|
|
"failure-reason-code": "X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED"
|
|
}]
|
|
}]
|
|
}`
|
|
|
|
const reportMultipleJSON = `{
|
|
"organization-name": "remote.example",
|
|
"date-range": {
|
|
"start-datetime": "2024-02-25T00:00:00Z",
|
|
"end-datetime": "2024-02-25T23:59:59Z"
|
|
},
|
|
"contact-info": "postmaster@remote.example",
|
|
"report-id": "20240225.mail.mox.example@remote.example",
|
|
"policies": [
|
|
{
|
|
"policy": {
|
|
"policy-type": "tlsa",
|
|
"policy-string": [
|
|
"3 1 1 206d5f55ecb9f8389bc57b5ba14716dd5b23d0834fd2c99fd402f0bda32e9523",
|
|
"3 1 1 4201e4b741c746b62ff806c142158c35ecbbbd9ac56b6d791f760e272736f8d0"
|
|
],
|
|
"policy-domain": "test2.xmox.nl",
|
|
"mx-host": [
|
|
"mail.mox.example"
|
|
]
|
|
},
|
|
"summary": {
|
|
"total-successful-session-count": 1,
|
|
"total-failure-session-count": 0
|
|
},
|
|
"failure-details": []
|
|
},
|
|
{
|
|
"policy": {
|
|
"policy-type": "tlsa",
|
|
"policy-string": [
|
|
"3 1 1 206d5f55ecb9f8389bc57b5ba14716dd5b23d0834fd2c99fd402f0bda32e9523",
|
|
"3 1 1 4201e4b741c746b62ff806c142158c35ecbbbd9ac56b6d791f760e272736f8d0"
|
|
],
|
|
"policy-domain": "test.xmox.nl",
|
|
"mx-host": [
|
|
"mail.mox.example"
|
|
]
|
|
},
|
|
"summary": {
|
|
"total-successful-session-count": 1,
|
|
"total-failure-session-count": 0
|
|
},
|
|
"failure-details": []
|
|
}
|
|
]
|
|
}
|
|
`
|
|
|
|
const reportMixedJSON = `{
|
|
"organization-name": "remote.example",
|
|
"date-range": {
|
|
"start-datetime": "2024-02-25T00:00:00Z",
|
|
"end-datetime": "2024-02-25T23:59:59Z"
|
|
},
|
|
"contact-info": "postmaster@remote.example",
|
|
"report-id": "20240225.test.xmox.nl@remote.example",
|
|
"policies": [
|
|
{
|
|
"policy": {
|
|
"policy-type": "tlsa",
|
|
"policy-string": [
|
|
"3 1 1 206d5f55ecb9f8389bc57b5ba14716dd5b23d0834fd2c99fd402f0bda32e9523",
|
|
"3 1 1 4201e4b741c746b62ff806c142158c35ecbbbd9ac56b6d791f760e272736f8d0"
|
|
],
|
|
"policy-domain": "mail.mox.example",
|
|
"mx-host": []
|
|
},
|
|
"summary": {
|
|
"total-successful-session-count": 1,
|
|
"total-failure-session-count": 0
|
|
},
|
|
"failure-details": []
|
|
},
|
|
{
|
|
"policy": {
|
|
"policy-type": "sts",
|
|
"policy-string": [
|
|
"version: STSv1",
|
|
"mode: enforce",
|
|
"max_age: 86400",
|
|
"mx: mail.mox.example"
|
|
],
|
|
"policy-domain": "unknown.xmox.nl",
|
|
"mx-host": [
|
|
"mail.mox.example"
|
|
]
|
|
},
|
|
"summary": {
|
|
"total-successful-session-count": 1,
|
|
"total-failure-session-count": 0
|
|
},
|
|
"failure-details": []
|
|
},
|
|
{
|
|
"policy": {
|
|
"policy-type": "sts",
|
|
"policy-string": [
|
|
"version: STSv1",
|
|
"mode: enforce",
|
|
"max_age: 86400",
|
|
"mx: mail.mox.example"
|
|
],
|
|
"policy-domain": "test.xmox.nl",
|
|
"mx-host": [
|
|
"mail.mox.example"
|
|
]
|
|
},
|
|
"summary": {
|
|
"total-successful-session-count": 1,
|
|
"total-failure-session-count": 0
|
|
},
|
|
"failure-details": []
|
|
}
|
|
]
|
|
}
|
|
`
|
|
|
|
const reportUnknownJSON = `{
|
|
"organization-name": "remote.example",
|
|
"date-range": {
|
|
"start-datetime": "2024-02-25T00:00:00Z",
|
|
"end-datetime": "2024-02-25T23:59:59Z"
|
|
},
|
|
"contact-info": "postmaster@remote.example",
|
|
"report-id": "20240225.test.xmox.nl@remote.example",
|
|
"policies": [
|
|
{
|
|
"policy": {
|
|
"policy-type": "tlsa",
|
|
"policy-string": [
|
|
"3 1 1 206d5f55ecb9f8389bc57b5ba14716dd5b23d0834fd2c99fd402f0bda32e9523",
|
|
"3 1 1 4201e4b741c746b62ff806c142158c35ecbbbd9ac56b6d791f760e272736f8d0"
|
|
],
|
|
"policy-domain": "unknown.mox.example",
|
|
"mx-host": []
|
|
},
|
|
"summary": {
|
|
"total-successful-session-count": 1,
|
|
"total-failure-session-count": 0
|
|
},
|
|
"failure-details": []
|
|
},
|
|
{
|
|
"policy": {
|
|
"policy-type": "sts",
|
|
"policy-string": [
|
|
"version: STSv1",
|
|
"mode: enforce",
|
|
"max_age: 86400",
|
|
"mx: mail.mox.example"
|
|
],
|
|
"policy-domain": "unknown.xmox.nl",
|
|
"mx-host": [
|
|
"unknown.mox.example"
|
|
]
|
|
},
|
|
"summary": {
|
|
"total-successful-session-count": 1,
|
|
"total-failure-session-count": 0
|
|
},
|
|
"failure-details": []
|
|
}
|
|
]
|
|
}
|
|
`
|
|
|
|
func TestReport(t *testing.T) {
|
|
mox.Context = ctxbg
|
|
mox.Shutdown, mox.ShutdownCancel = context.WithCancel(ctxbg)
|
|
mox.ConfigStaticPath = filepath.FromSlash("../testdata/tlsrpt/fake.conf")
|
|
mox.Conf.Static.HostnameDomain = dns.Domain{ASCII: "mail.mox.example"}
|
|
mox.Conf.Static.DataDir = "."
|
|
// Recognize as configured domain.
|
|
mox.Conf.Dynamic.Domains = map[string]config.Domain{
|
|
"test.xmox.nl": {},
|
|
"test2.xmox.nl": {},
|
|
}
|
|
|
|
dbpath := mox.DataDirPath("tlsrpt.db")
|
|
os.MkdirAll(filepath.Dir(dbpath), 0770)
|
|
defer os.Remove(dbpath)
|
|
defer os.Remove(mox.DataDirPath("tlsrptresult.db"))
|
|
|
|
if err := Init(); err != nil {
|
|
t.Fatalf("init database: %s", err)
|
|
}
|
|
defer Close()
|
|
|
|
files, err := os.ReadDir("../testdata/tlsreports")
|
|
if err != nil {
|
|
t.Fatalf("listing reports: %s", err)
|
|
}
|
|
for _, file := range files {
|
|
f, err := os.Open("../testdata/tlsreports/" + file.Name())
|
|
if err != nil {
|
|
t.Fatalf("open %q: %s", file, err)
|
|
}
|
|
reportJSON, err := tlsrpt.ParseMessage(pkglog.Logger, f)
|
|
f.Close()
|
|
if err != nil {
|
|
t.Fatalf("parsing TLSRPT from message %q: %s", file.Name(), err)
|
|
}
|
|
report := reportJSON.Convert()
|
|
if err := AddReport(ctxbg, pkglog, dns.Domain{ASCII: "mox.example"}, "tlsrpt@mox.example", false, &report); err != nil {
|
|
t.Fatalf("adding report to database: %s", err)
|
|
}
|
|
}
|
|
|
|
reportJSON, err := tlsrpt.Parse(strings.NewReader(reportJSON))
|
|
if err != nil {
|
|
t.Fatalf("parsing report: %v", err)
|
|
}
|
|
report := reportJSON.Convert()
|
|
if err := AddReport(ctxbg, pkglog, dns.Domain{ASCII: "company-y.example"}, "tlsrpt@company-y.example", false, &report); err != nil {
|
|
t.Fatalf("adding report to database: %s", err)
|
|
}
|
|
|
|
records, err := Records(ctxbg)
|
|
if err != nil {
|
|
t.Fatalf("fetching records: %s", err)
|
|
}
|
|
for _, r := range records {
|
|
if r.FromDomain != "company-y.example" {
|
|
continue
|
|
}
|
|
if !reflect.DeepEqual(r.Report, report) {
|
|
t.Fatalf("report, got %#v, expected %#v", r.Report, report)
|
|
}
|
|
if _, err := RecordID(ctxbg, r.ID); err != nil {
|
|
t.Fatalf("get record by id: %v", err)
|
|
}
|
|
}
|
|
|
|
start, _ := time.Parse(time.RFC3339, "2016-04-01T00:00:00Z")
|
|
end, _ := time.Parse(time.RFC3339, "2016-04-01T23:59:59Z")
|
|
records, err = RecordsPeriodDomain(ctxbg, start, end, dns.Domain{ASCII: "test.xmox.nl"})
|
|
if err != nil || len(records) != 1 {
|
|
t.Fatalf("got err %v, records %#v, expected no error with 1 record", err, records)
|
|
}
|
|
|
|
// Add report with multiple recipient domains.
|
|
reportJSON, err = tlsrpt.Parse(strings.NewReader(reportMultipleJSON))
|
|
if err != nil {
|
|
t.Fatalf("parsing report: %v", err)
|
|
}
|
|
report = reportJSON.Convert()
|
|
if err := AddReport(ctxbg, pkglog, dns.Domain{ASCII: "remote.example"}, "postmaster@remote.example", false, &report); err != nil {
|
|
t.Errorf("adding report to database: %s", err)
|
|
}
|
|
|
|
// Add report with mixed host and domain policies. The unknown domain is ignored.
|
|
reportJSON, err = tlsrpt.Parse(strings.NewReader(reportMixedJSON))
|
|
if err != nil {
|
|
t.Fatalf("parsing report: %v", err)
|
|
}
|
|
report = reportJSON.Convert()
|
|
if err := AddReport(ctxbg, pkglog, dns.Domain{ASCII: "remote.example"}, "postmaster@remote.example", false, &report); err != nil {
|
|
t.Errorf("adding report to database: %s", err)
|
|
}
|
|
|
|
// All unknown domains in report should cause error.
|
|
reportJSON, err = tlsrpt.Parse(strings.NewReader(reportUnknownJSON))
|
|
if err != nil {
|
|
t.Fatalf("parsing report: %v", err)
|
|
}
|
|
report = reportJSON.Convert()
|
|
if err := AddReport(ctxbg, pkglog, dns.Domain{ASCII: "remote.example"}, "postmaster@remote.example", false, &report); err == nil {
|
|
t.Errorf("adding report with all unknown domains, expected error")
|
|
}
|
|
}
|