|
| 1 | +--- |
| 2 | +lastUpdated: "05/10/2025" |
| 3 | +title: "smtp_tls_reporting" |
| 4 | +description: "hook invoked after every TLS events for reporting purpose rfc8460 TLSRPT" |
| 5 | +--- |
| 6 | + |
| 7 | +<a name="hooks.core.smtp_tls_reporting"></a> |
| 8 | +## Name |
| 9 | + |
| 10 | +smtp_tls_reporting - This hook is added in 5.1 and allows you inspect a SMTP TLS reporting data |
| 11 | + point in JSON format. |
| 12 | + |
| 13 | +## Synopsis |
| 14 | + |
| 15 | +`#include "hooks/core/smtp_tls_reporting.h"` |
| 16 | + |
| 17 | +`int core_smtp_tls_reporting(void closure, struct json_object *json)` |
| 18 | + |
| 19 | + |
| 20 | +## Description |
| 21 | + |
| 22 | +This hook is called upon: |
| 23 | +- any TLSRPT (rfc8460) defined failures, before a TLS connection is attempted, |
| 24 | + normally during TLS policy (including MTA-STS, TLSA/DANE) fetching stage. |
| 25 | +- TLS negotiation failures or successes during outbound delivery when MTA-STS or TLSA/DANE is enabled. |
| 26 | + **Currently, only enabled on domains with successfully fetched MTA-STS policies or DANE TLSA records **. |
| 27 | + |
| 28 | +The JSON fields and values are defined in `tlsrpt.h`, with most of the field names the same as |
| 29 | + defined in the RFC: https://datatracker.ietf.org/doc/html/rfc8460. |
| 30 | + |
| 31 | +The following JSON fields are not defined in the RFC: |
| 32 | +* `epoch` - epoch time of when the hook is invoked |
| 33 | +* `type` - whether the data is for a successful TLS connection or a failure. |
| 34 | + `0` - failure; `1` - success |
| 35 | + |
| 36 | +**An example JSON for a success**: |
| 37 | + |
| 38 | +``` |
| 39 | +{ |
| 40 | + "epoch": 1746712864, |
| 41 | + "type": 1, |
| 42 | + "policy": { |
| 43 | + "policy-type": "sts", |
| 44 | + "policy-domain": "test.bird.com", |
| 45 | + "policy-string": [ |
| 46 | + "version: STSv1", |
| 47 | + "mode: enforce", |
| 48 | + "mx: mx.bird.com", |
| 49 | + "mx: server.ectest.OMNITI.com", |
| 50 | + "max_age: 604800" |
| 51 | + ] |
| 52 | + }, |
| 53 | + "sending-mta-ip": "127.0.0.1", |
| 54 | + "receiving-mx-hostname": "server.ectest.OMNITI.com", |
| 55 | + "receiving-ip": "127.0.0.1" |
| 56 | +} |
| 57 | +``` |
| 58 | + |
| 59 | +**An example JSON for a failure**: |
| 60 | + |
| 61 | +``` |
| 62 | +{ |
| 63 | + "epoch": 1746629177, |
| 64 | + "type": 0, |
| 65 | + "policy": { |
| 66 | + "policy-type": "sts", |
| 67 | + "policy-domain": "mismatch.cert.com", |
| 68 | + "policy-string": [ |
| 69 | + "version: STSv1", |
| 70 | + "mode: enforce", |
| 71 | + "mx: test.bird.com", |
| 72 | + "max_age: 86400" |
| 73 | + ] |
| 74 | + }, |
| 75 | + "result-type": "certificate-host-mismatch", |
| 76 | + "failure-reason-code": "4.7.5 [internal] SSL certificate subject does not match host", |
| 77 | + "sending-mta-ip": "127.0.0.1", |
| 78 | + "receiving-mx-hostname": "test.BIRD.com", |
| 79 | + "receiving-ip": "127.0.0.1" |
| 80 | +} |
| 81 | +``` |
| 82 | + |
| 83 | + |
| 84 | +**Return Values** |
| 85 | + |
| 86 | +This hook returns `int`, but for now the return value has no significance. |
| 87 | + |
| 88 | + |
| 89 | +**Threading** |
| 90 | + |
| 91 | +This hook could be called in any thread. Please avoid doing time consuming tasks in the hook's |
| 92 | + implementation. |
| 93 | + |
| 94 | + |
| 95 | +### Example: a Lua implementation of the hook to log the JSON into the `paniclog` |
| 96 | + |
| 97 | +``` |
| 98 | + require("msys.core"); |
| 99 | + require("json") |
| 100 | +
|
| 101 | + local mod = {} |
| 102 | +
|
| 103 | + function mod:core_smtp_tls_reporting(js) |
| 104 | + print("tls report: ", js) -- log the whole JSON |
| 105 | + if js.type == 0 then -- failure |
| 106 | + print(string.format("TLSRPT: %s@%s@%s", js.policy["policy-domain"], |
| 107 | + js.policy["policy-type"], js["result-type"])) |
| 108 | + else -- success |
| 109 | + print(string.format("TLSRPT: %s@%s@%s", js.policy["policy-domain"], |
| 110 | + js.policy["policy-type"], "OK")) |
| 111 | + end |
| 112 | + end |
| 113 | +
|
| 114 | + msys.registerModule("tlsrpt", mod); |
| 115 | +``` |
| 116 | + |
| 117 | +**Example of the paniclog output from the above Lua hook**: |
| 118 | +``` |
| 119 | +1746712864:scriptlet: tls report: { "epoch": 1746712864, "type": 1, "policy": { "policy-type": "sts", "policy-domain": "test.bird.com", "policy-string": [ "version: STSv1", "mode: enforce", "mx: mx.bird.com", "mx: server.ectest.OMNITI.com", "max_age: 604800" ] }, "sending-mta-ip": "127.0.0.1", "receiving-mx-hostname": "server.ectest.OMNITI.com", "receiving-ip": "127.0.0.1" } |
| 120 | +1746712864:scriptlet: TLSRPT: test.bird.com@sts@OK |
| 121 | +1746719856:scriptlet: tls report: { "epoch": 1746719856, "type": 0, "policy": { "policy-type": "sts", "policy-domain": "mismatch.cert.com", "policy-string": [ "version: STSv1", "mode: enforce", "mx: test.bird.com", "max_age: 86400" ] }, "result-type": "certificate-host-mismatch", "failure-reason-code": "4.7.5 [internal] SSL certificate subject does not match host", "sending-mta-ip": "127.0.0.1", "receiving-mx-hostname": "test.BIRD.com", "receiving-ip": "127.0.0.1" } |
| 122 | +1746719856:scriptlet: TLSRPT: mismatch.cert.com@sts@certificate-host-mismatch |
| 123 | +``` |
0 commit comments