1package mox
2
3import (
4 "bytes"
5 "context"
6 "fmt"
7 "strings"
8 "time"
9
10 "github.com/mjl-/mox/config"
11 "github.com/mjl-/mox/dkim"
12 "github.com/mjl-/mox/dns"
13 "github.com/mjl-/mox/mlog"
14 "github.com/mjl-/mox/smtp"
15)
16
17// DKIMSelectors returns the selectors to use for signing.
18func DKIMSelectors(dkimConf config.DKIM) []dkim.Selector {
19 var l []dkim.Selector
20 for _, sign := range dkimConf.Sign {
21 sel := dkimConf.Selectors[sign]
22 s := dkim.Selector{
23 Hash: sel.HashEffective,
24 HeaderRelaxed: sel.Canonicalization.HeaderRelaxed,
25 BodyRelaxed: sel.Canonicalization.BodyRelaxed,
26 Headers: sel.HeadersEffective,
27 SealHeaders: !sel.DontSealHeaders,
28 Expiration: time.Duration(sel.ExpirationSeconds) * time.Second,
29 PrivateKey: sel.Key,
30 Domain: sel.Domain,
31 }
32 l = append(l, s)
33 }
34 return l
35}
36
37// DKIMSign looks up the domain for "from", and uses its DKIM configuration to
38// generate DKIM-Signature headers, for inclusion in a message. The
39// DKIM-Signatur headers, are returned. If no domain was found an empty string and
40// nil error is returned.
41func DKIMSign(ctx context.Context, log mlog.Log, from smtp.Path, smtputf8 bool, data []byte) (string, error) {
42 // Add DKIM signature for domain, even if higher up than the full mail hostname.
43 // This helps with an assumed (because default) relaxed DKIM policy. If the DMARC
44 // policy happens to be strict, the signature won't help, but won't hurt either.
45 fd := from.IPDomain.Domain
46 var zerodom dns.Domain
47 for fd != zerodom {
48 confDom, ok := Conf.Domain(fd)
49 if !ok {
50 var nfd dns.Domain
51 _, nfd.ASCII, _ = strings.Cut(fd.ASCII, ".")
52 _, nfd.Unicode, _ = strings.Cut(fd.Unicode, ".")
53 fd = nfd
54 continue
55 }
56
57 selectors := DKIMSelectors(confDom.DKIM)
58 dkimHeaders, err := dkim.Sign(ctx, log.Logger, from.Localpart, fd, selectors, smtputf8, bytes.NewReader(data))
59 if err != nil {
60 return "", fmt.Errorf("dkim sign for domain %s: %v", fd, err)
61 }
62 return dkimHeaders, nil
63 }
64 return "", nil
65}
66