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