1 /*        $NetBSD: tlsrpt_wrapper.c,v 1.2 2025/02/25 19:15:50 christos Exp $    */
2 
3 /*++
4 /* NAME
5 /*        tlsrpt_wrapper 3
6 /* SUMMARY
7 /*        TLSRPT support for the SMTP and TLS protocol engines
8 /* SYNOPSIS
9 /*        #include <tlsrpt_wrapper.h>
10 /*
11 /*        #ifdef USE_TLS
12 /*        #ifdef USE_TLSRPT
13 /*        TLS_RPT *trw_create(
14 /*        const char *rpt_socket_name,
15 /*        const char *rpt_policy_domain,
16 /*        const char *rpt_policy_string,
17 /*        int       skip_reused_hs)
18 /*
19 /*        void      trw_free(
20 /*        TLSRPT_WRAPPER *trw)
21 /*
22 /*        void      trw_set_tls_policy(
23 /*        TLSRPT_WRAPPER *trw,
24 /*        tlsrpt_policy_type_t tls_policy_type,
25 /*        const char *const *tls_policy_strings,
26 /*        const char *tls_policy_domain
27 /*        const char *const *mx_host_patterns)
28 /*
29 /*        void      trw_set_tcp_connection(
30 /*        TLSRPT_WRAPPER *trw,
31 /*        const char *snd_mta_addr,
32 /*        const char *rcv_mta_name,
33 /*        const char *rcv_mta_addr)
34 /*
35 /*        void      trw_set_ehlo_resp(
36 /*        TLSRPT_WRAPPER *trw,
37 /*        const char *rcv_mta_ehlo)
38 /*
39 /*        void      trw_report_failure(
40 /*        TLSRPT_WRAPPER *trw,
41 /*        tlsrpt_failure_t failure_type,
42 /*        const char *additional_info,
43 /*        const char *failure_reason)
44 /*
45 /*        void      trw_report_success(
46 /*        TLSRPT_WRAPPER *trw)
47 /*
48 /*        int       trw_is_reported(
49 /*        TLSRPT_WRAPPER *trw)
50 /*
51 /*        int       trw_is_skip_reused_hs(
52 /*        TLSRPT_WRAPPER *trw)
53 /*
54 /*        tlsrpt_policy_type_t convert_tlsrpt_policy_type(
55 /*        const char *policy_type)
56 /*
57 /*        tlsrpt_failure_t convert_tlsrpt_policy_failure(
58 /*        const char *policy_failure)
59 /*        #endif /* USE_TLS_RPT */
60 /*
61 /*        int       valid_tlsrpt_policy_type(
62 /*        const char *type_name)
63 /*
64 /*        int       valid_tlsrpt_policy_failure(
65 /*        const char *failure_name)
66 /*        #endif /* USE_TLS */
67 /* POSTFIX ARCHITECTURE, BOTTOM-UP VIEW
68 /* .ad
69 /* .fi
70 /*        This module encapsulates TLSRPT support for Postfix's
71 /*        multi-process and multi-layer architecture. The text that follows
72 /*        explains the purpose of this software layer.
73 /*
74 /*        First, Postfix TLSRPT support uses the TLSRPT client library
75 /*        from sys4.de. That library makes the reasonable assumption that
76 /*        all calls concerning one SMTP session will be made from within
77 /*        one process.
78 /*
79 /*        Second, some TLS errors are detected in the SMTP protocol
80 /*        engine (example: a remote SMTP server does not announce STARTTLS
81 /*        support), while other TLS errors are detected in the TLS protocol
82 /*        engine (example: certificate verification error).
83 /*
84 /*        Third, the Postfix TLS protocol engine may be located in a
85 /*        different process than the SMTP protocol engine. And even if the
86 /*        two are located in the same process, the TLS protocol engine knows
87 /*        nothing about SMTP. Hence, there needs to be an abstraction that
88 /*        isolates the TLS protocol engine from the SMTP-specific details
89 /*        of TLSRPT.
90 /*
91 /*        Fourth, Postfix has a pipelined and layered architecture where
92 /*        each process (or architectural layer) handles a problem as it
93 /*        runs into it, instead of reporting problem details back to its
94 /*        pipeline predecessor (or back to a higher architectural layer).
95 /* TLSRPT_WRAPPER IMPLEMENTATION
96 /* .ad
97 /* .fi
98 /*        At a high level, the SMTP protocol engine encapsulates SMTP
99 /*        session and TLS policy information in an opaque TLSRPT_WRAPPER
100 /*        object, and passes that object to the TLS protocol engine. The
101 /*        TLS protocol engine can invoke TLSRPT_WRAPPER methods to report a
102 /*        TLS error through the sys4.de TLSRPT client library. In a similar
103 /*        manner, the SMTP protocol engine can invoke TLSRPT_WRAPPER object
104 /*        methods to report a TLS error or success.
105 /*
106 /*        At a low level, The Postfix SMTP protocol engine (smtp_proto.c)
107 /*        reports TLS errors when TLS support is required but unavailable,
108 /*        or requests the Postfix TLS protocol engine to perform a TLS
109 /*        protocol handshake over an open SMTP connection. The SMTP
110 /*        protocol engine either calls the TLS protocol engine directly,
111 /*        or calls it over local IPC in a tlsproxy(8) process.
112 /*
113 /*        The TLS protocol engine may report a TLS error by invoking
114 /*        TLSRPT_WRAPPER methods, and either returns no TLS session object,
115 /*        or a TLS session object for a completed handshake. The TLS session
116 /*        object will indicate if the TLS protocol engine reported any
117 /*        TLS error through TLSRPT (for example an error that resulted in
118 /*        a successful TLS handshake with a downgraded TLS security level).
119 /*
120 /*        The Postfix SMTP protocol engine reports success or failure
121 /*        by invoking TLSRPT_WRAPPER methods, depending on whether all
122 /*        matching requirements were satisfied. The SMTP protocol engine
123 /*        does not report success or failure by invoking TLSRPT_WRAPPER
124 /*        methods if the TLS protocol engine already reported a failure.
125 /* TLSRPT_WRAPPER API
126 /* .ad
127 /* .fi
128 /*        The functions below must be called in a specific order. All
129 /*        string inputs are copied. If a required call is missing then
130 /*        the request will be ignored, and a warning will be logged,
131 /*        but this not affect email deliveries.
132 /* .PP
133 /*        trw_create() must be called before other trw_xxx() requests can
134 /*        be made. Arguments:
135 /* .IP rpt_socket_name
136 /*        The name of a socket that will be managed by local TLSRPT
137 /*        infrastructure.
138 /* .IP rpt_policy_domain
139 /*        The TLSRPT policy domain name, i.e. the domain that wishes to
140 /*        receive TLSRPT summary reports. An internationalized domain name
141 /*        must be in A-label form (i.e. punycode).
142 /* .IP rpt_policy_string
143 /*        The TLSRPT policy record content, i.e. how to submit TLSRPT
144 /*        summary reports.
145 /* .IP skip_reused_hs
146 /*        If non-zero, do not report the TLSRPT status for TLS handshakes
147 /*        that reuse a previously-negotiated TLS session; such sessions
148 /*        were already reported when they were used for the first time.
149 /* .PP
150 /*        trw_free() destroys storage allocated with other trw_xxx()
151 /*        requests.
152 /* .PP
153 /*        trw_set_tls_policy() must be called by the SMTP protocol engine
154 /*        after it found a DANE, STS, or no policy, and before it tries to
155 /*        establish a new SMTP connection. This function clears information
156 /*        that was specified earlier with trw_set_tls_policy() or
157 /*        trw_set_tcp_connection(), and resets whether trw_report_failure()
158 /*        or trw_report_success() were called. Mapping from arguments to
159 /*        TLSRPT report fields:
160 /* .IP tls_policy_type
161 /*        policies[].policy.policy-type.
162 /* .IP tls_policy_strings (may be null)
163 /*        policies[].policy.policy-string[]. Ignored if the tls_policy_type
164 /*        value is TLSRPT_NO_POLICY_FOUND.
165 /* .IP tls_policy_domain (may be null)
166 /*        policies[].policy.policy-domain.
167 /* .IP mx_host_patterns (may be null)
168 /*        policies[].policy.mx-host[]. Ignored if the tls_policy_type
169 /*        value is TLSRPT_NO_POLICY_FOUND.
170 /* .PP
171 /*        trw_set_tcp_connection() and trw_set_ehlo_resp() are optionally
172 /*        called by the SMTP protcol engine, after it has established
173 /*        a new SMTP connection, before it requests a TLS protocol
174 /*        handshake. Mapping from arguments to TLSRPT report fields:
175 /* .IP snd_mta_addr (may be null)
176 /*        policies[].failure-details[].sending-mta-ip.
177 /* .IP rcv_mta_name (may be null)
178 /*        policies[].failure-details[].receiving-mx-hostname.
179 /* .IP rcv_mta_addr (may be null)
180 /*        policies[].failure-details[].receiving-ip.
181 /* .PP
182 /*        trw_set_ehlo_resp() is optionally called by the SMTP protcol
183 /*        engine to pass on the EHLO response. Presumably this is the EHLO
184 /*        response before STARTTLS (TLSRPT is primarily interested in
185 /*        pre-handshake and handshake errors).
186 /* .IP rcv_mta_ehlo (may be null)
187 /*        policies[].failure-details[].receiving-mx-helo.
188 /* .PP
189 /*        trw_report_failure() is called by the TLS protocol engine or
190 /*        SMTP protocol engine to report a TLS error. The result value
191 /*        is 0 for success, -1 for failure as indicated with the errno
192 /*        value. The call is successfully skipped if information is missing
193 /*        or if failure or success were already reported for the
194 /*        connection. Mapping from arguments to TLSRPT report fields:
195 /* .IP failure_type
196 /*        policies[].failure-details[].result-type.
197 /* .IP additional_info (may be null)
198 /*        policies[].failure-details[].additional-information.
199 /* .IP failure_reason (may be null)
200 /*        policies[].failure-details[].failure-reason-code
201 /* .PP
202 /*        trw_report_success() is called by the SMTP protocol engine
203 /*        to report a successful TLS handshake. The result value is
204 /*        0 for success, -1 for failure with errno indicating the
205 /*        error type. The call is successfully skipped if information if
206 /*        missing or if failure or success were already reported for
207 /*        the connection.
208 /* .PP
209 /*        trw_is_reported() returns non-zero when the contents of the
210 /*        specified TLSRPT_WRAPPER have been reported.
211 /* .PP
212 /*        trw_is_skip_reused_hs() returns non-zero if the skip_reused_hs
213 /*        argument of trw_create() was non-zero.
214 /* .PP
215 /*        convert_tlsrpt_policy_type() and convert_tlsrpt_policy_failure()
216 /*        convert a valid policy type or failure name to the corresponding
217 /*        enum value. The result is < 0 if the name is not valid.
218 /* .PP
219 /*        valid_tlsrpt_policy_type() and valid_tlsrpt_policy_failure()
220 /*        return non-zero if the specified policy type or failure name
221 /*        is valid in TLSRPT. These functions do not require that the
222 /*        module is built with TLSRPT support. This allows the names to
223 /*        be used even if TLSRPT is disabled.
224 /* DIAGNOSTICS
225 /*        Some functions will log a warning when information is missing.
226 /*        Such warnings will not affect the operation of the SMTP or TLS
227 /*        protocol engine.
228 /* BUGS
229 /*        This implementation is suitable to report successful TLS policy
230 /*        compliance, and to report a failure that prevents TLS policy
231 /*        compliance (example: all TLSA records are unusable). Do not use
232 /*        this implementation to report other errors (example: some TLSA
233 /*        record is non-parsable).
234 /* SEE ALSO
235 /*        https://github.com/sys4/tlsrpt, TLSRPT client library
236 /* LICENSE
237 /* .ad
238 /* .fi
239 /*        The Secure Mailer license must be distributed with this software.
240 /* AUTHOR(S)
241 /*        Wietse Venema
242 /*        porcupine.org
243 /*--*/
244 
245 #if defined(USE_TLS)
246 
247  /*
248   * System library.
249   */
250 #include <sys_defs.h>
251 #include <errno.h>
252 #include <string.h>
253 #if defined(USE_TLSRPT)
254 #include <tlsrpt.h>
255 #endif
256 
257 #if !defined(TLSRPT_PREREQ)
258 #define TLSRPT_PREREQ(maj, min) \
259     (defined(TLSRPT_VERSION_MAJOR) && \
260           ((TLSRPT_VERSION_MAJOR << 16) + TLSRPT_VERSION_MINOR >= \
261               ((maj) << 16) + (min)))
262 #endif
263 
264  /*
265   * Utility library.
266   */
267 #include <argv.h>
268 #include <msg.h>
269 #include <mymalloc.h>
270 #include <name_code.h>
271 #include <stringops.h>
272 
273  /*
274   * Some functions are not #ifdef USE_TLSRPT.
275   */
276 #define TLSRPT_WRAPPER_INTERNAL
277 #include <tlsrpt_wrapper.h>
278 #if defined(USE_TLSRPT)
279 
280  /*
281   * Macros to make repetitive code more readable.
282   */
283 #define MYFREE_IF_SET(member) do { \
284           if (member) \
285               myfree(member); \
286     } while (0)
287 
288 #define MYFREE_IF_SET_AND_CLEAR(member, value) do { \
289           if (member) { \
290               myfree(member); \
291               (member) = 0; \
292           } \
293     } while (0)
294 
295 #define MYFREE_IF_SET_AND_COPY(member, value) do { \
296           MYFREE_IF_SET(member); \
297           (member) = (value) ? mystrdup(value) : 0; \
298     } while (0)
299 
300 #define ARGV_FREE_IF_SET(member) do { \
301           if (member) \
302               argv_free(member); \
303     } while (0)
304 
305 #define ARGV_FREE_IF_SET_AND_CLEAR(member) do { \
306           if (member) { \
307               argv_free(member); \
308               (member) = 0; \
309           } \
310     } while (0)
311 
312 #define ARGV_FREE_IF_SET_AND_COPY(member, value) do { \
313           ARGV_FREE_IF_SET(member); \
314           (member) = (value) ? argv_addv((ARGV *) 0, value) : 0; \
315     } while (0)
316 
317 /* trw_create - create initial TLSRPT_WRAPPER instance */
318 
trw_create(const char * rpt_socket_name,const char * rpt_policy_domain,const char * rpt_policy_string,int skip_reused_hs)319 TLSRPT_WRAPPER *trw_create(const char *rpt_socket_name,
320                                          const char *rpt_policy_domain,
321                                          const char *rpt_policy_string,
322                                          int skip_reused_hs)
323 {
324     const char myname[] = "trw_create";
325     TLSRPT_WRAPPER *trw;
326 
327     if (msg_verbose > 1)
328           msg_info("%s(rpt_socket_name=%s, rpt_policy_domain=%s, "
329                      "rpt_policy_string=%s, skip_reused_hs=%d)",
330                      myname, rpt_socket_name, rpt_policy_domain,
331                      rpt_policy_string, skip_reused_hs);
332 
333 #if TLSRPT_PREREQ(0, 6)
334     if (tlsrpt_version_check(TLSRPT_VERSION_MAJOR, TLSRPT_VERSION_MINOR,
335                                    TLSRPT_VERSION_PATCH) == 0)
336           msg_warn("run-time library vs. compile-time header version mismatch: "
337                      "libtlsrpt API version '%s' is not compatible with "
338                      "libtlsrpt API version '%s' ", tlsrpt_version(),
339                      TLSRPT_VERSION_STRING);
340 #endif
341 
342     /*
343      * memset() is not portable for pointer etc. types.
344      */
345     trw = (TLSRPT_WRAPPER *) mymalloc(sizeof(*trw));
346     trw->rpt_socket_name = mystrdup(rpt_socket_name);
347     trw->rpt_policy_domain = mystrdup(rpt_policy_domain);
348     trw->rpt_policy_string = mystrdup(rpt_policy_string);;
349     trw->tls_policy_type = 0;
350     trw->tls_policy_strings = 0;
351     trw->tls_policy_domain = 0;
352     trw->mx_host_patterns = 0;
353     trw->snd_mta_addr = 0;
354     trw->rcv_mta_name = 0;
355     trw->rcv_mta_addr = 0;
356     trw->rcv_mta_ehlo = 0;
357     trw->skip_reused_hs = skip_reused_hs;
358     trw->flags = 0;
359     return (trw);
360 }
361 
362 /* trw_free - destroy TLSRPT_WRAPPER instance. */
363 
trw_free(TLSRPT_WRAPPER * trw)364 void    trw_free(TLSRPT_WRAPPER *trw)
365 {
366     if (msg_verbose > 1)
367           msg_info("trw_free: rpt_socket_name=%s, rpt_policy_domain=%s, ...",
368                      trw->rpt_socket_name, trw->rpt_policy_domain);
369 
370     /* Destroy fields set with trw_create(). */
371     myfree(trw->rpt_socket_name);
372     myfree(trw->rpt_policy_domain);
373     myfree(trw->rpt_policy_string);
374     /* Destroy fields set with trw_set_tls_policy(). */
375     ARGV_FREE_IF_SET(trw->tls_policy_strings);
376     MYFREE_IF_SET(trw->tls_policy_domain);
377     ARGV_FREE_IF_SET(trw->mx_host_patterns);
378     /* Destroy fields set with trw_set_tcp_connection(). */
379     trw_set_tcp_connection(trw, (char *) 0, (char *) 0, (char *) 0);
380     /* Destroy fields set with trw_set_ehlo_resp(). */
381     trw_set_ehlo_resp(trw, (char *) 0);
382     /* That's all. */
383     myfree((void *) trw);
384 }
385 
386 /* trw_set_tls_policy - set TLS policy info, clear SMTP info */
387 
trw_set_tls_policy(TLSRPT_WRAPPER * trw,tlsrpt_policy_type_t tls_policy_type,const char * const * tls_policy_strings,const char * tls_policy_domain,const char * const * mx_host_patterns)388 void    trw_set_tls_policy(TLSRPT_WRAPPER *trw,
389                                          tlsrpt_policy_type_t tls_policy_type,
390                                          const char *const * tls_policy_strings,
391                                          const char *tls_policy_domain,
392                                          const char *const * mx_host_patterns)
393 {
394     const char myname[] = "trw_set_tls_policy";
395 
396 #define STR_OR_NULL(s)        ((s) ? (s) : "(Null)")
397 #define PSTR_OR_NULL(p)       ((p) ? STR_OR_NULL(*p) : "(Null)")
398 
399     if (msg_verbose > 1)
400           msg_info("%s(tlsrpt_policy_type_t=%d, tls_policy_strings=%s..., "
401                      "tls_policy_domain=%s, mx_host_patterns=%s...)",
402                      myname, tls_policy_type,
403                      PSTR_OR_NULL(tls_policy_strings),
404                      STR_OR_NULL(tls_policy_domain),
405                      PSTR_OR_NULL(mx_host_patterns));
406 
407     trw->tls_policy_type = tls_policy_type;
408     MYFREE_IF_SET_AND_COPY(trw->tls_policy_domain, tls_policy_domain);
409     if (tls_policy_type == TLSRPT_NO_POLICY_FOUND) {
410           ARGV_FREE_IF_SET_AND_CLEAR(trw->tls_policy_strings);
411           ARGV_FREE_IF_SET_AND_CLEAR(trw->tls_policy_strings);
412     } else {
413           ARGV_FREE_IF_SET_AND_COPY(trw->tls_policy_strings, tls_policy_strings);
414           ARGV_FREE_IF_SET_AND_COPY(trw->mx_host_patterns, mx_host_patterns);
415     }
416     trw->flags = TRW_FLAG_HAVE_TLS_POLICY;
417     trw_set_tcp_connection(trw, (char *) 0, (char *) 0, (char *) 0);
418     trw_set_ehlo_resp(trw, (char *) 0);
419 }
420 
421 /* trw_set_tcp_connection - set SMTP endpoint info */
422 
trw_set_tcp_connection(TLSRPT_WRAPPER * trw,const char * snd_mta_addr,const char * rcv_mta_name,const char * rcv_mta_addr)423 void    trw_set_tcp_connection(TLSRPT_WRAPPER *trw,
424                                              const char *snd_mta_addr,
425                                              const char *rcv_mta_name,
426                                              const char *rcv_mta_addr)
427 {
428     const char myname[] = "trw_set_tcp_connection";
429 
430     if (msg_verbose > 1 && (snd_mta_addr || rcv_mta_name || rcv_mta_addr))
431           msg_info("%s(snd_mta_addr=%s, rcv_mta_name=%s, rcv_mta_addr=%s)",
432                      myname, STR_OR_NULL(snd_mta_addr),
433                      STR_OR_NULL(rcv_mta_name), STR_OR_NULL(rcv_mta_addr));
434 
435     /*
436      * Sanity check: usage errors are not a show stopper.
437      */
438     if ((snd_mta_addr || rcv_mta_name || rcv_mta_addr)
439           && ((trw->flags & TRW_FLAG_HAVE_TLS_POLICY) == 0
440               || (trw->flags & TRW_FLAG_REPORTED))) {
441           msg_warn("%s: missing trw_set_tls_policy call", myname);
442           return;
443     }
444     MYFREE_IF_SET_AND_COPY(trw->snd_mta_addr, snd_mta_addr);
445     MYFREE_IF_SET_AND_COPY(trw->rcv_mta_name, rcv_mta_name);
446     MYFREE_IF_SET_AND_COPY(trw->rcv_mta_addr, rcv_mta_addr);
447 }
448 
449 /* trw_set_ehlo_resp - set EHLO response */
450 
trw_set_ehlo_resp(TLSRPT_WRAPPER * trw,const char * rcv_mta_ehlo)451 void    trw_set_ehlo_resp(TLSRPT_WRAPPER *trw, const char *rcv_mta_ehlo)
452 {
453     const char myname[] = "trw_set_ehlo_resp";
454 
455     if (msg_verbose > 1 && rcv_mta_ehlo)
456           msg_info("%s(rcv_mta_ehlo=%s)", myname, rcv_mta_ehlo);
457 
458     /*
459      * Sanity check: usage errors are not a show stopper.
460      */
461     if (rcv_mta_ehlo && ((trw->flags & TRW_FLAG_HAVE_TLS_POLICY) == 0
462                                || (trw->flags & TRW_FLAG_REPORTED))) {
463           msg_warn("%s: missing trw_set_tls_policy call", myname);
464           return;
465     }
466     MYFREE_IF_SET_AND_COPY(trw->rcv_mta_ehlo, rcv_mta_ehlo);
467 }
468 
469 /* trw_munge_report_result - helper to map and log libtlsrpt result value */
470 
trw_munge_report_result(int libtlsrpt_errorcode)471 static int trw_munge_report_result(int libtlsrpt_errorcode)
472 {
473     int     err;
474 
475     /*
476      * First, deal with the non-error cases.
477      */
478     if (libtlsrpt_errorcode == 0) {
479           return (0);
480     }
481 
482     /*
483      * Report a tlsrpt library internal error.
484      */
485     else if (tlsrpt_error_code_is_internal(libtlsrpt_errorcode)) {
486           msg_warn("Could not report TLS handshake result to tlsrpt library:"
487                      " %s (error %d)", tlsrpt_strerror(libtlsrpt_errorcode),
488                      libtlsrpt_errorcode);
489           return (-1);
490     }
491 
492     /*
493      * Report a libc error. Do not report success if errno was zero. When
494      * debug logging is enabled, also log some library-internal info.
495      */
496     else {
497           err = tlsrpt_errno_from_error_code(libtlsrpt_errorcode);
498           msg_warn("Could not report TLS handshake result to tlsrpt library:"
499                      " %s (errno %d)", mystrerror(err), err);
500           if (msg_verbose)
501               msg_warn("Error location in tlsrpt library: %s (error %d)",
502                          tlsrpt_strerror(libtlsrpt_errorcode),
503                          libtlsrpt_errorcode);
504           errno = err;
505           return (-1);
506     }
507 }
508 
509 /* trw_tlsrpt_failure_to_string - make debug logging readable */
510 
trw_failure_type_to_string(tlsrpt_failure_t failure_type)511 static const char *trw_failure_type_to_string(tlsrpt_failure_t failure_type)
512 {
513     static const NAME_CODE failure_types[] = {
514           "starttls_not_supported", TLSRPT_STARTTLS_NOT_SUPPORTED,
515           "certificate_host_mismatch", TLSRPT_CERTIFICATE_HOST_MISMATCH,
516           "certificate_not_trusted", TLSRPT_CERTIFICATE_NOT_TRUSTED,
517           "certificate_expired", TLSRPT_CERTIFICATE_EXPIRED,
518           "validation_failure", TLSRPT_VALIDATION_FAILURE,
519           "sts_policy_fetch_error", TLSRPT_STS_POLICY_FETCH_ERROR,
520           "sts_policy_invalid", TLSRPT_STS_POLICY_INVALID,
521           "sts_webpki_invalid", TLSRPT_STS_WEBPKI_INVALID,
522           "tlsa_invalid", TLSRPT_TLSA_INVALID,
523           "dnssec_invalid", TLSRPT_DNSSEC_INVALID,
524           "dane_required", TLSRPT_DANE_REQUIRED,
525           "unfinished_policY", TLSRPT_UNFINISHED_POLICY,
526           0, -1
527     };
528     const char *cp;
529     static VSTRING *buf;
530 
531     if ((cp = str_name_code(failure_types, failure_type)) == 0) {
532           if (buf == 0)
533               buf = vstring_alloc(20);
534           msg_warn("unknown tlsrpt_failure_t value %d", failure_type);
535           vstring_sprintf(buf, "failure_type_%d", failure_type);
536           cp = vstring_str(buf);
537     }
538     return (cp);
539 }
540 
541 /* trw_report_failure - one-shot failure reporter */
542 
trw_report_failure(TLSRPT_WRAPPER * trw,tlsrpt_failure_t failure_type,const char * additional_info,const char * failure_reason)543 int     trw_report_failure(TLSRPT_WRAPPER *trw,
544                                          tlsrpt_failure_t failure_type,
545                                          const char *additional_info,
546                                          const char *failure_reason)
547 {
548     const char myname[] = "trw_report_failure";
549     struct tlsrpt_connection_t *con;
550     int     res;
551 
552     if (msg_verbose > 1)
553           msg_info("%s(failure_type=%d, additional_info=%s, failure_reason=%s)",
554                      myname, failure_type, STR_OR_NULL(additional_info),
555                      STR_OR_NULL(failure_reason));
556 
557     /*
558      * Sanity check: usage errors are not a show stopper.
559      */
560     if ((trw->flags & TRW_FLAG_HAVE_TLS_POLICY) == 0) {
561           msg_warn("%s: missing trw_set_tls_policy call", myname);
562           return (0);
563     }
564 
565     /*
566      * Report a failure only when it is seen first. If a failure was already
567      * reported by a lower-level function close to the root cause, then skip
568      * the less detailed failure report from a later caller who is further
569      * away from the point where trouble was found.
570      *
571      * TODO(wietse) Is it worthwhile to distinguish between failure versus
572      * success already reported?
573      */
574     if (trw->flags & TRW_FLAG_REPORTED) {
575           if (msg_verbose)
576               msg_info("%s: success or failure already reported", myname);
577           return (0);
578     }
579     trw->flags |= TRW_FLAG_REPORTED;
580 
581     /* Give the local admin a clue. */
582     msg_info("TLSRPT: status=failure, domain=%s, receiving_mx=%s[%s],"
583                " failure_type=%s%s%s",
584                trw->rpt_policy_domain, trw->rcv_mta_name, trw->rcv_mta_addr,
585                trw_failure_type_to_string(failure_type),
586                failure_reason ? ", failure_reason=" : "",
587                failure_reason ? failure_reason : "");
588 
589     if ((res = tlsrpt_open(&con, trw->rpt_socket_name)) == 0) {
590           struct tlsrpt_dr_t *dr;
591           char  **cpp;
592 
593           if ((res = tlsrpt_init_delivery_request(&dr, con,
594                                                             trw->rpt_policy_domain,
595                                                       trw->rpt_policy_string)) == 0) {
596               if ((res = tlsrpt_init_policy(dr, trw->tls_policy_type,
597                                                     trw->tls_policy_domain)) == 0) {
598                     if (trw->tls_policy_strings)
599                         for (cpp = trw->tls_policy_strings->argv;
600                                res == 0 && *cpp; cpp++)
601                               res = tlsrpt_add_policy_string(dr, *cpp);
602                     if (trw->mx_host_patterns)
603                         for (cpp = trw->mx_host_patterns->argv;
604                                res == 0 && *cpp; cpp++)
605                               res = tlsrpt_add_mx_host_pattern(dr, *cpp);
606                     if (res == 0)
607                         res = tlsrpt_add_delivery_request_failure(dr,
608                                                      /* failure_code= */ failure_type,
609                                             /* sending_mta_ip= */ trw->snd_mta_addr,
610                                    /* receiving_mx_hostname= */ trw->rcv_mta_name,
611                                          /* receiving_mx_helo= */ trw->rcv_mta_ehlo,
612                                               /* receiving_ip= */ trw->rcv_mta_addr,
613                                     /* additional_information= */ additional_info,
614                                          /* failure_reason_code= */ failure_reason);
615                     if (res == 0)
616                         res = tlsrpt_finish_policy(dr, TLSRPT_FINAL_FAILURE);
617               }
618               if (res == 0) {
619                     res = tlsrpt_finish_delivery_request(&dr);
620               } else {
621                     (void) tlsrpt_cancel_delivery_request(&dr);
622               }
623           }
624           (void) tlsrpt_close(&con);
625     }
626     return (trw_munge_report_result(res));
627 }
628 
629 /* trw_report_success - one-shot success reporter */
630 
trw_report_success(TLSRPT_WRAPPER * trw)631 int     trw_report_success(TLSRPT_WRAPPER *trw)
632 {
633     const char myname[] = "trw_report_success";
634     struct tlsrpt_connection_t *con;
635     int     res;
636 
637     if (msg_verbose > 1)
638           msg_info("trw_report_success");
639 
640     /*
641      * Sanity check: usage errors are not a show stopper.
642      */
643     if ((trw->flags & TRW_FLAG_HAVE_TLS_POLICY) == 0) {
644           msg_warn("%s: missing trw_set_tls_policy call", myname);
645           return (0);
646     }
647     /* This should not happen. Log a warning. */
648     if (trw->flags & TRW_FLAG_REPORTED) {
649           msg_warn("%s: success or failure was already reported", myname);
650           return (0);
651     }
652     trw->flags |= TRW_FLAG_REPORTED;
653 
654     /* Give the local admin a clue. */
655     msg_info("TLSRPT: status=success, domain=%s, receiving_mx=%s[%s]",
656                trw->rpt_policy_domain, trw->rcv_mta_name, trw->rcv_mta_addr);
657 
658     if ((res = tlsrpt_open(&con, trw->rpt_socket_name)) == 0) {
659           struct tlsrpt_dr_t *dr;
660 
661           if ((res = tlsrpt_init_delivery_request(&dr, con,
662                                                             trw->rpt_policy_domain,
663                                                       trw->rpt_policy_string)) == 0) {
664               if ((res = tlsrpt_init_policy(dr, trw->tls_policy_type,
665                                                     trw->tls_policy_domain)) == 0) {
666                     char  **cpp;
667 
668                     if (trw->tls_policy_strings)
669                         for (cpp = trw->tls_policy_strings->argv;
670                                res == 0 && *cpp; cpp++)
671                               res = tlsrpt_add_policy_string(dr, *cpp);
672                     if (trw->mx_host_patterns)
673                         for (cpp = trw->mx_host_patterns->argv;
674                                res == 0 && *cpp; cpp++)
675                               res = tlsrpt_add_mx_host_pattern(dr, *cpp);
676                     if (res == 0)
677                         res = tlsrpt_finish_policy(dr, TLSRPT_FINAL_SUCCESS);
678               }
679               if (res == 0) {
680                     res = tlsrpt_finish_delivery_request(&dr);
681               } else {
682                     (void) tlsrpt_cancel_delivery_request(&dr);
683               }
684           }
685           (void) tlsrpt_close(&con);
686     }
687     return (trw_munge_report_result(res));
688 }
689 
690 /* trw_is_reported - trw_report_success() or trw_report_failure() called */
691 
trw_is_reported(const TLSRPT_WRAPPER * trw)692 int     trw_is_reported(const TLSRPT_WRAPPER *trw)
693 {
694     return (trw->flags & TRW_FLAG_REPORTED);
695 }
696 
697 /* trw_is_skip_reused_hs - don't report TLS handshakes that reuse a session */
698 
trw_is_skip_reused_hs(const TLSRPT_WRAPPER * trw)699 int     trw_is_skip_reused_hs(const TLSRPT_WRAPPER *trw)
700 {
701     return (trw->skip_reused_hs);
702 }
703 
704 #endif                                            /* USE_TLS_RPT */
705 
706  /*
707   * Dummy definitions for builds without the TLSRPT library, so that we can
708   * still validate names.
709   */
710 #if !defined(USE_TLSRPT)
711 #define TLSRPT_POLICY_DANE    0
712 #define TLSRPT_POLICY_STS     0
713 #define TLSRPT_NO_POLICY_FOUND  0
714 
715 #define TLSRPT_VALIDATION_FAILURE       0
716 #define TLSRPT_STS_POLICY_FETCH_ERROR   0
717 #define TLSRPT_STS_POLICY_INVALID       0
718 #define TLSRPT_STS_WEBPKI_INVALID       0
719 #endif
720 
721  /*
722   * Mapping from RFC 8460 string to libtlsrpt enum for policy types and
723   * policy failures. The mapping assumes that all enum values are
724   * non-negative.
725   */
726 const NAME_CODE tlsrpt_policy_type_mapping[] = {
727     "sts", TLSRPT_POLICY_STS,
728     "no-policy-found", TLSRPT_NO_POLICY_FOUND,
729     0, -1,
730 };
731 
732 const NAME_CODE tlsrpt_policy_failure_mapping[] = {
733     "sts-policy-fetch-error", TLSRPT_STS_POLICY_FETCH_ERROR,
734     "sts-policy-invalid", TLSRPT_STS_POLICY_INVALID,
735     "sts-webpki-invalid", TLSRPT_STS_WEBPKI_INVALID,
736     "validation-failure", TLSRPT_VALIDATION_FAILURE,
737     0, -1,
738 };
739 
740 /* valid_tlsrpt_policy_type - validate policy_type attribute value */
741 
valid_tlsrpt_policy_type(const char * policy_type)742 int     valid_tlsrpt_policy_type(const char *policy_type)
743 {
744     return (name_code(tlsrpt_policy_type_mapping, NAME_CODE_FLAG_NONE,
745                           policy_type) >= 0);
746 }
747 
748 /* valid_tlsrpt_policy_failure - validate policy_failure attribute value */
749 
valid_tlsrpt_policy_failure(const char * policy_failure)750 int     valid_tlsrpt_policy_failure(const char *policy_failure)
751 {
752     return (name_code(tlsrpt_policy_failure_mapping, NAME_CODE_FLAG_NONE,
753                           policy_failure) >= 0);
754 }
755 
756 #if defined(USE_TLSRPT)
757 
758 /* convert_tlsrpt_policy_type - convert string to enum */
759 
convert_tlsrpt_policy_type(const char * policy_type)760 tlsrpt_policy_type_t convert_tlsrpt_policy_type(const char *policy_type)
761 {
762     return (name_code(tlsrpt_policy_type_mapping, NAME_CODE_FLAG_NONE,
763                           policy_type));
764 }
765 
766 /* convert_tlsrpt_policy_failure - convert string to enum */
767 
convert_tlsrpt_policy_failure(const char * policy_failure)768 tlsrpt_failure_t convert_tlsrpt_policy_failure(const char *policy_failure)
769 {
770     return (name_code(tlsrpt_policy_failure_mapping, NAME_CODE_FLAG_NONE,
771                           policy_failure));
772 }
773 
774 #endif                                            /* USE_TLSRPT */
775 
776 #endif                                            /* USE_TLS */
777