meowmail/smtp/auth/inbound

MeowMail — Inbound email authentication (SPF, DKIM, DMARC).

Verifies incoming messages and adds Authentication-Results header. SPF checks the connecting IP against the sender's DNS record. DKIM verifies cryptographic signatures. DMARC checks alignment between SPF and DKIM results.

Types

AuthHeader = object
  spf*: AuthResult
  spfDetail*: string         ## e.g. "smtp.mailfrom=alice@example.com"
  dkim*: AuthResult
  dkimDetail*: string        ## e.g. "header.d=example.com"
  dkimDomains*: seq[string]  ## domains of passing signatures (for DMARC)
  dmarc*: AuthResult
  dmarcDetail*: string
  dmarcReject*: bool         ## DMARC p=reject applied and enforcement on
  combined*: string          ## Full Authentication-Results header value
AuthResult = enum
  arPass, arFail, arSoftFail, arNeutral, arNone, arTempError, arPermError

Procs

proc authenticateMessage(spfServerPtr: pointer; clientIp, heloDomain: string;
                         headers: seq[Header]; body: string;
                         envelopeFrom: string = ""; rawHeaderBlock: string = "";
                         keyLookup: DkimKeyLookup = nil;
                         dmarcFetch: DmarcRecordLookup = nil;
                         dmarcMode = "report"; authHost = "meowmail.local";
                         verifyDkimSigs = true): AuthHeader {.
    ...raises: [Exception], tags: [TimeEffect, RootEffect], forbids: [].}

Run all authentication checks on an incoming message and build the Authentication-Results header value.

SPF is evaluated against the envelope sender (RFC 7208), falling back to the From: domain only when the envelope is unavailable. DKIM signatures are cryptographically verified (RFC 6376) and DMARC is evaluated (RFC 7489) with SPF/DKIM alignment. dmarcMode controls enforcement: "report" never rejects, "quarantine" accepts and reports, "reject" marks dmarcReject when a p=reject policy applies so the caller can refuse the message.

proc renderAuthHeader(auth: AuthHeader): string {....raises: [], tags: [],
    forbids: [].}
Render the Authentication-Results header.
proc verifyDkim(headers: seq[Header]; body: string;
                keyLookup: DkimKeyLookup = nil; rawHeaderBlock: string = ""): (
    AuthResult, seq[string], string) {....raises: [Exception],
                                       tags: [TimeEffect, RootEffect],
                                       forbids: [].}

Cryptographically verify DKIM signature(s) in the message headers. Returns (result, passing domains, detail string).

The raw header block is preferred: simple canonicalization is only byte-exact when the original wire bytes are available. Without it the fields are rebuilt from parsed headers, which is exact for relaxed but best-effort for simple.

proc verifySpf(spfServerPtr: pointer; clientIp, heloDomain, mailFrom: string): (
    AuthResult, string) {....raises: [], tags: [], forbids: [].}
Verify the connecting IP against the sender's SPF record. Returns (result, detail string).