Home | History | Annotate | Line # | Download | only in tls
      1 /*	$NetBSD: tlsrpt_wrapper.c,v 1.3 2026/05/09 18:49:21 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. If null, this defaults to the
    167 /*	TLSRPT policy domain.
    168 /* .IP mx_host_patterns (may be null)
    169 /*	policies[].policy.mx-host[]. Ignored if the tls_policy_type
    170 /*	value is TLSRPT_NO_POLICY_FOUND.
    171 /* .PP
    172 /*	trw_set_tcp_connection() and trw_set_ehlo_resp() are optionally
    173 /*	called by the SMTP protcol engine, after it has established
    174 /*	a new SMTP connection, before it requests a TLS protocol
    175 /*	handshake. Mapping from arguments to TLSRPT report fields:
    176 /* .IP snd_mta_addr (may be null)
    177 /*	policies[].failure-details[].sending-mta-ip.
    178 /* .IP rcv_mta_name (may be null)
    179 /*	policies[].failure-details[].receiving-mx-hostname.
    180 /* .IP rcv_mta_addr (may be null)
    181 /*	policies[].failure-details[].receiving-ip.
    182 /* .PP
    183 /*	trw_set_ehlo_resp() is optionally called by the SMTP protcol
    184 /*	engine to pass on the EHLO response. Presumably this is the EHLO
    185 /*	response before STARTTLS (TLSRPT is primarily interested in
    186 /*	pre-handshake and handshake errors).
    187 /* .IP rcv_mta_ehlo (may be null)
    188 /*	policies[].failure-details[].receiving-mx-helo.
    189 /* .PP
    190 /*	trw_report_failure() is called by the TLS protocol engine or
    191 /*	SMTP protocol engine to report a TLS error. The result value
    192 /*	is 0 for success, -1 for failure as indicated with the errno
    193 /*	value. The call is successfully skipped if information is missing
    194 /*	or if failure or success were already reported for the
    195 /*	connection. Mapping from arguments to TLSRPT report fields:
    196 /* .IP failure_type
    197 /*	policies[].failure-details[].result-type.
    198 /* .IP additional_info (may be null)
    199 /*	policies[].failure-details[].additional-information.
    200 /* .IP failure_reason (may be null)
    201 /*	policies[].failure-details[].failure-reason-code
    202 /* .PP
    203 /*	trw_report_success() is called by the SMTP protocol engine
    204 /*	to report a successful TLS handshake. The result value is
    205 /*	0 for success, -1 for failure with errno indicating the
    206 /*	error type. The call is successfully skipped if information if
    207 /*	missing or if failure or success were already reported for
    208 /*	the connection.
    209 /* .PP
    210 /*	trw_is_reported() returns non-zero when the contents of the
    211 /*	specified TLSRPT_WRAPPER have been reported.
    212 /* .PP
    213 /*	trw_is_skip_reused_hs() returns non-zero if the skip_reused_hs
    214 /*	argument of trw_create() was non-zero.
    215 /* .PP
    216 /*	convert_tlsrpt_policy_type() and convert_tlsrpt_policy_failure()
    217 /*	convert a valid policy type or failure name to the corresponding
    218 /*	enum value. The result is < 0 if the name is not valid.
    219 /* .PP
    220 /*	valid_tlsrpt_policy_type() and valid_tlsrpt_policy_failure()
    221 /*	return non-zero if the specified policy type or failure name
    222 /*	is valid in TLSRPT. These functions do not require that the
    223 /*	module is built with TLSRPT support. This allows the names to
    224 /*	be used even if TLSRPT is disabled.
    225 /* DIAGNOSTICS
    226 /*	Some functions will log a warning when information is missing.
    227 /*	Such warnings will not affect the operation of the SMTP or TLS
    228 /*	protocol engine.
    229 /* BUGS
    230 /*	This implementation is suitable to report successful TLS policy
    231 /*	compliance, and to report a failure that prevents TLS policy
    232 /*	compliance (example: all TLSA records are unusable). Do not use
    233 /*	this implementation to report other errors (example: some TLSA
    234 /*	record is non-parsable).
    235 /* SEE ALSO
    236 /*	https://github.com/sys4/tlsrpt, TLSRPT client library
    237 /* LICENSE
    238 /* .ad
    239 /* .fi
    240 /*	The Secure Mailer license must be distributed with this software.
    241 /* AUTHOR(S)
    242 /*	Wietse Venema
    243 /*	porcupine.org
    244 /*--*/
    245 
    246 #if defined(USE_TLS)
    247 
    248  /*
    249   * System library.
    250   */
    251 #include <sys_defs.h>
    252 #include <errno.h>
    253 #include <string.h>
    254 #if defined(USE_TLSRPT)
    255 #include <tlsrpt.h>
    256 #endif
    257 
    258 #if !defined(TLSRPT_PREREQ)
    259 #define TLSRPT_PREREQ(maj, min) \
    260     (defined(TLSRPT_VERSION_MAJOR) && \
    261 	((TLSRPT_VERSION_MAJOR << 16) + TLSRPT_VERSION_MINOR >= \
    262 	    ((maj) << 16) + (min)))
    263 #endif
    264 
    265  /*
    266   * Utility library.
    267   */
    268 #include <argv.h>
    269 #include <msg.h>
    270 #include <mymalloc.h>
    271 #include <name_code.h>
    272 #include <stringops.h>
    273 
    274  /*
    275   * Some functions are not #ifdef USE_TLSRPT.
    276   */
    277 #define TLSRPT_WRAPPER_INTERNAL
    278 #include <tlsrpt_wrapper.h>
    279 #if defined(USE_TLSRPT)
    280 
    281  /*
    282   * Macros to make repetitive code more readable.
    283   */
    284 #define MYFREE_IF_SET(member) do { \
    285 	if (member) \
    286 	    myfree(member); \
    287     } while (0)
    288 
    289 #define MYFREE_IF_SET_AND_CLEAR(member, value) do { \
    290 	if (member) { \
    291 	    myfree(member); \
    292 	    (member) = 0; \
    293 	} \
    294     } while (0)
    295 
    296 #define MYFREE_IF_SET_AND_COPY(member, value) do { \
    297 	MYFREE_IF_SET(member); \
    298 	(member) = (value) ? mystrdup(value) : 0; \
    299     } while (0)
    300 
    301 #define ARGV_FREE_IF_SET(member) do { \
    302 	if (member) \
    303 	    argv_free(member); \
    304     } while (0)
    305 
    306 #define ARGV_FREE_IF_SET_AND_CLEAR(member) do { \
    307 	if (member) { \
    308 	    argv_free(member); \
    309 	    (member) = 0; \
    310 	} \
    311     } while (0)
    312 
    313 #define ARGV_FREE_IF_SET_AND_COPY(member, value) do { \
    314 	ARGV_FREE_IF_SET(member); \
    315 	(member) = (value) ? argv_addv((ARGV *) 0, value) : 0; \
    316     } while (0)
    317 
    318 /* trw_create - create initial TLSRPT_WRAPPER instance */
    319 
    320 TLSRPT_WRAPPER *trw_create(const char *rpt_socket_name,
    321 			           const char *rpt_policy_domain,
    322 			           const char *rpt_policy_string,
    323 			           int skip_reused_hs)
    324 {
    325     const char myname[] = "trw_create";
    326     TLSRPT_WRAPPER *trw;
    327 
    328     if (msg_verbose > 1)
    329 	msg_info("%s(rpt_socket_name=%s, rpt_policy_domain=%s, "
    330 		 "rpt_policy_string=%s, skip_reused_hs=%d)",
    331 		 myname, rpt_socket_name, rpt_policy_domain,
    332 		 rpt_policy_string, skip_reused_hs);
    333 
    334 #if TLSRPT_PREREQ(0, 6)
    335     if (tlsrpt_version_check(TLSRPT_VERSION_MAJOR, TLSRPT_VERSION_MINOR,
    336 			     TLSRPT_VERSION_PATCH) == 0)
    337 	msg_warn("run-time library vs. compile-time header version mismatch: "
    338 		 "libtlsrpt API version '%s' is not compatible with "
    339 		 "libtlsrpt API version '%s' ", tlsrpt_version(),
    340 		 TLSRPT_VERSION_STRING);
    341 #endif
    342 
    343     /*
    344      * memset() is not portable for pointer etc. types.
    345      */
    346     trw = (TLSRPT_WRAPPER *) mymalloc(sizeof(*trw));
    347     trw->rpt_socket_name = mystrdup(rpt_socket_name);
    348     trw->rpt_policy_domain = mystrdup(rpt_policy_domain);
    349     trw->rpt_policy_string = mystrdup(rpt_policy_string);;
    350     trw->tls_policy_type = 0;
    351     trw->tls_policy_strings = 0;
    352     trw->tls_policy_domain = 0;
    353     trw->mx_host_patterns = 0;
    354     trw->snd_mta_addr = 0;
    355     trw->rcv_mta_name = 0;
    356     trw->rcv_mta_addr = 0;
    357     trw->rcv_mta_ehlo = 0;
    358     trw->skip_reused_hs = skip_reused_hs;
    359     trw->flags = 0;
    360     return (trw);
    361 }
    362 
    363 /* trw_free - destroy TLSRPT_WRAPPER instance. */
    364 
    365 void    trw_free(TLSRPT_WRAPPER *trw)
    366 {
    367     if (msg_verbose > 1)
    368 	msg_info("trw_free: rpt_socket_name=%s, rpt_policy_domain=%s, ...",
    369 		 trw->rpt_socket_name, trw->rpt_policy_domain);
    370 
    371     /* Destroy fields set with trw_create(). */
    372     myfree(trw->rpt_socket_name);
    373     myfree(trw->rpt_policy_domain);
    374     myfree(trw->rpt_policy_string);
    375     /* Destroy fields set with trw_set_tls_policy(). */
    376     ARGV_FREE_IF_SET(trw->tls_policy_strings);
    377     MYFREE_IF_SET(trw->tls_policy_domain);
    378     ARGV_FREE_IF_SET(trw->mx_host_patterns);
    379     /* Destroy fields set with trw_set_tcp_connection(). */
    380     trw_set_tcp_connection(trw, (char *) 0, (char *) 0, (char *) 0);
    381     /* Destroy fields set with trw_set_ehlo_resp(). */
    382     trw_set_ehlo_resp(trw, (char *) 0);
    383     /* That's all. */
    384     myfree((void *) trw);
    385 }
    386 
    387 /* trw_set_tls_policy - set TLS policy info, clear SMTP info */
    388 
    389 void    trw_set_tls_policy(TLSRPT_WRAPPER *trw,
    390 			           tlsrpt_policy_type_t tls_policy_type,
    391 			           const char *const * tls_policy_strings,
    392 			           const char *tls_policy_domain,
    393 			           const char *const * mx_host_patterns)
    394 {
    395     const char myname[] = "trw_set_tls_policy";
    396 
    397 #define STR_OR_NULL(s)	((s) ? (s) : "(Null)")
    398 #define PSTR_OR_NULL(p)	((p) ? STR_OR_NULL(*p) : "(Null)")
    399 
    400     if (msg_verbose > 1)
    401 	msg_info("%s(tlsrpt_policy_type_t=%d, tls_policy_strings=%s..., "
    402 		 "tls_policy_domain=%s, mx_host_patterns=%s...)",
    403 		 myname, tls_policy_type,
    404 		 PSTR_OR_NULL(tls_policy_strings),
    405 		 STR_OR_NULL(tls_policy_domain),
    406 		 PSTR_OR_NULL(mx_host_patterns));
    407 
    408     trw->tls_policy_type = tls_policy_type;
    409     if (tls_policy_domain == 0)
    410 	tls_policy_domain = trw->rpt_policy_domain;
    411     MYFREE_IF_SET_AND_COPY(trw->tls_policy_domain, tls_policy_domain);
    412     if (tls_policy_type == TLSRPT_NO_POLICY_FOUND) {
    413 	ARGV_FREE_IF_SET_AND_CLEAR(trw->tls_policy_strings);
    414 	ARGV_FREE_IF_SET_AND_CLEAR(trw->mx_host_patterns);
    415     } else {
    416 	ARGV_FREE_IF_SET_AND_COPY(trw->tls_policy_strings, tls_policy_strings);
    417 	ARGV_FREE_IF_SET_AND_COPY(trw->mx_host_patterns, mx_host_patterns);
    418     }
    419     trw->flags = TRW_FLAG_HAVE_TLS_POLICY;
    420     trw_set_tcp_connection(trw, (char *) 0, (char *) 0, (char *) 0);
    421     trw_set_ehlo_resp(trw, (char *) 0);
    422 }
    423 
    424 /* trw_set_tcp_connection - set SMTP endpoint info */
    425 
    426 void    trw_set_tcp_connection(TLSRPT_WRAPPER *trw,
    427 			               const char *snd_mta_addr,
    428 			               const char *rcv_mta_name,
    429 			               const char *rcv_mta_addr)
    430 {
    431     const char myname[] = "trw_set_tcp_connection";
    432 
    433     if (msg_verbose > 1 && (snd_mta_addr || rcv_mta_name || rcv_mta_addr))
    434 	msg_info("%s(snd_mta_addr=%s, rcv_mta_name=%s, rcv_mta_addr=%s)",
    435 		 myname, STR_OR_NULL(snd_mta_addr),
    436 		 STR_OR_NULL(rcv_mta_name), STR_OR_NULL(rcv_mta_addr));
    437 
    438     /*
    439      * Sanity check: usage errors are not a show stopper.
    440      */
    441     if ((snd_mta_addr || rcv_mta_name || rcv_mta_addr)
    442 	&& ((trw->flags & TRW_FLAG_HAVE_TLS_POLICY) == 0
    443 	    || (trw->flags & TRW_FLAG_REPORTED))) {
    444 	msg_warn("%s: missing trw_set_tls_policy call", myname);
    445 	return;
    446     }
    447     MYFREE_IF_SET_AND_COPY(trw->snd_mta_addr, snd_mta_addr);
    448     MYFREE_IF_SET_AND_COPY(trw->rcv_mta_name, rcv_mta_name);
    449     MYFREE_IF_SET_AND_COPY(trw->rcv_mta_addr, rcv_mta_addr);
    450 }
    451 
    452 /* trw_set_ehlo_resp - set EHLO response */
    453 
    454 void    trw_set_ehlo_resp(TLSRPT_WRAPPER *trw, const char *rcv_mta_ehlo)
    455 {
    456     const char myname[] = "trw_set_ehlo_resp";
    457 
    458     if (msg_verbose > 1 && rcv_mta_ehlo)
    459 	msg_info("%s(rcv_mta_ehlo=%s)", myname, rcv_mta_ehlo);
    460 
    461     /*
    462      * Sanity check: usage errors are not a show stopper.
    463      */
    464     if (rcv_mta_ehlo && ((trw->flags & TRW_FLAG_HAVE_TLS_POLICY) == 0
    465 			 || (trw->flags & TRW_FLAG_REPORTED))) {
    466 	msg_warn("%s: missing trw_set_tls_policy call", myname);
    467 	return;
    468     }
    469     MYFREE_IF_SET_AND_COPY(trw->rcv_mta_ehlo, rcv_mta_ehlo);
    470 }
    471 
    472 /* trw_munge_report_result - helper to map and log libtlsrpt result value */
    473 
    474 static int trw_munge_report_result(int libtlsrpt_errorcode)
    475 {
    476     int     err;
    477 
    478     /*
    479      * First, deal with the non-error cases.
    480      */
    481     if (libtlsrpt_errorcode == 0) {
    482 	return (0);
    483     }
    484 
    485     /*
    486      * Report a tlsrpt library internal error.
    487      */
    488     else if (tlsrpt_error_code_is_internal(libtlsrpt_errorcode)) {
    489 	msg_warn("Could not report TLS handshake result to tlsrpt library:"
    490 		 " %s (error %d)", tlsrpt_strerror(libtlsrpt_errorcode),
    491 		 libtlsrpt_errorcode);
    492 	return (-1);
    493     }
    494 
    495     /*
    496      * Report a libc error. Do not report success if errno was zero. When
    497      * debug logging is enabled, also log some library-internal info.
    498      */
    499     else {
    500 	err = tlsrpt_errno_from_error_code(libtlsrpt_errorcode);
    501 	msg_warn("Could not report TLS handshake result to tlsrpt library:"
    502 		 " %s (errno %d)", mystrerror(err), err);
    503 	if (msg_verbose)
    504 	    msg_warn("Error location in tlsrpt library: %s (error %d)",
    505 		     tlsrpt_strerror(libtlsrpt_errorcode),
    506 		     libtlsrpt_errorcode);
    507 	errno = err;
    508 	return (-1);
    509     }
    510 }
    511 
    512 /* trw_tlsrpt_failure_to_string - make debug logging readable */
    513 
    514 static const char *trw_failure_type_to_string(tlsrpt_failure_t failure_type)
    515 {
    516     static const NAME_CODE failure_types[] = {
    517 	"starttls_not_supported", TLSRPT_STARTTLS_NOT_SUPPORTED,
    518 	"certificate_host_mismatch", TLSRPT_CERTIFICATE_HOST_MISMATCH,
    519 	"certificate_not_trusted", TLSRPT_CERTIFICATE_NOT_TRUSTED,
    520 	"certificate_expired", TLSRPT_CERTIFICATE_EXPIRED,
    521 	"validation_failure", TLSRPT_VALIDATION_FAILURE,
    522 	"sts_policy_fetch_error", TLSRPT_STS_POLICY_FETCH_ERROR,
    523 	"sts_policy_invalid", TLSRPT_STS_POLICY_INVALID,
    524 	"sts_webpki_invalid", TLSRPT_STS_WEBPKI_INVALID,
    525 	"tlsa_invalid", TLSRPT_TLSA_INVALID,
    526 	"dnssec_invalid", TLSRPT_DNSSEC_INVALID,
    527 	"dane_required", TLSRPT_DANE_REQUIRED,
    528 	"unfinished_policY", TLSRPT_UNFINISHED_POLICY,
    529 	0, -1
    530     };
    531     const char *cp;
    532     static VSTRING *buf;
    533 
    534     if ((cp = str_name_code(failure_types, failure_type)) == 0) {
    535 	if (buf == 0)
    536 	    buf = vstring_alloc(20);
    537 	msg_warn("unknown tlsrpt_failure_t value %d", failure_type);
    538 	vstring_sprintf(buf, "failure_type_%d", failure_type);
    539 	cp = vstring_str(buf);
    540     }
    541     return (cp);
    542 }
    543 
    544 /* trw_report_failure - one-shot failure reporter */
    545 
    546 int     trw_report_failure(TLSRPT_WRAPPER *trw,
    547 			           tlsrpt_failure_t failure_type,
    548 			           const char *additional_info,
    549 			           const char *failure_reason)
    550 {
    551     const char myname[] = "trw_report_failure";
    552     struct tlsrpt_connection_t *con;
    553     int     res;
    554 
    555     if (msg_verbose > 1)
    556 	msg_info("%s(failure_type=%d, additional_info=%s, failure_reason=%s)",
    557 		 myname, failure_type, STR_OR_NULL(additional_info),
    558 		 STR_OR_NULL(failure_reason));
    559 
    560     /*
    561      * Sanity check: usage errors are not a show stopper.
    562      */
    563     if ((trw->flags & TRW_FLAG_HAVE_TLS_POLICY) == 0) {
    564 	msg_warn("%s: missing trw_set_tls_policy call", myname);
    565 	return (0);
    566     }
    567 
    568     /*
    569      * Report a failure only when it is seen first. If a failure was already
    570      * reported by a lower-level function close to the root cause, then skip
    571      * the less detailed failure report from a later caller who is further
    572      * away from the point where trouble was found.
    573      *
    574      * TODO(wietse) Is it worthwhile to distinguish between failure versus
    575      * success already reported?
    576      */
    577     if (trw->flags & TRW_FLAG_REPORTED) {
    578 	if (msg_verbose)
    579 	    msg_info("%s: success or failure already reported", myname);
    580 	return (0);
    581     }
    582     trw->flags |= TRW_FLAG_REPORTED;
    583 
    584     /* Give the local admin a clue. */
    585     msg_info("TLSRPT: status=failure, domain=%s, receiving_mx=%s[%s],"
    586 	     " failure_type=%s%s%s",
    587 	     trw->rpt_policy_domain, trw->rcv_mta_name, trw->rcv_mta_addr,
    588 	     trw_failure_type_to_string(failure_type),
    589 	     failure_reason ? ", failure_reason=" : "",
    590 	     failure_reason ? failure_reason : "");
    591 
    592     if ((res = tlsrpt_open(&con, trw->rpt_socket_name)) == 0) {
    593 	struct tlsrpt_dr_t *dr;
    594 	char  **cpp;
    595 
    596 	if ((res = tlsrpt_init_delivery_request(&dr, con,
    597 						trw->rpt_policy_domain,
    598 					    trw->rpt_policy_string)) == 0) {
    599 	    if ((res = tlsrpt_init_policy(dr, trw->tls_policy_type,
    600 					  trw->tls_policy_domain)) == 0) {
    601 		if (trw->tls_policy_strings)
    602 		    for (cpp = trw->tls_policy_strings->argv;
    603 			 res == 0 && *cpp; cpp++)
    604 			res = tlsrpt_add_policy_string(dr, *cpp);
    605 		if (trw->mx_host_patterns)
    606 		    for (cpp = trw->mx_host_patterns->argv;
    607 			 res == 0 && *cpp; cpp++)
    608 			res = tlsrpt_add_mx_host_pattern(dr, *cpp);
    609 		if (res == 0)
    610 		    res = tlsrpt_add_delivery_request_failure(dr,
    611 					   /* failure_code= */ failure_type,
    612 				    /* sending_mta_ip= */ trw->snd_mta_addr,
    613 			     /* receiving_mx_hostname= */ trw->rcv_mta_name,
    614 				 /* receiving_mx_helo= */ trw->rcv_mta_ehlo,
    615 				      /* receiving_ip= */ trw->rcv_mta_addr,
    616 			      /* additional_information= */ additional_info,
    617 				 /* failure_reason_code= */ failure_reason);
    618 		if (res == 0)
    619 		    res = tlsrpt_finish_policy(dr, TLSRPT_FINAL_FAILURE);
    620 	    }
    621 	    if (res == 0) {
    622 		res = tlsrpt_finish_delivery_request(&dr);
    623 	    } else {
    624 		(void) tlsrpt_cancel_delivery_request(&dr);
    625 	    }
    626 	}
    627 	(void) tlsrpt_close(&con);
    628     }
    629     return (trw_munge_report_result(res));
    630 }
    631 
    632 /* trw_report_success - one-shot success reporter */
    633 
    634 int     trw_report_success(TLSRPT_WRAPPER *trw)
    635 {
    636     const char myname[] = "trw_report_success";
    637     struct tlsrpt_connection_t *con;
    638     int     res;
    639 
    640     if (msg_verbose > 1)
    641 	msg_info("trw_report_success");
    642 
    643     /*
    644      * Sanity check: usage errors are not a show stopper.
    645      */
    646     if ((trw->flags & TRW_FLAG_HAVE_TLS_POLICY) == 0) {
    647 	msg_warn("%s: missing trw_set_tls_policy call", myname);
    648 	return (0);
    649     }
    650     /* This should not happen. Log a warning. */
    651     if (trw->flags & TRW_FLAG_REPORTED) {
    652 	msg_warn("%s: success or failure was already reported", myname);
    653 	return (0);
    654     }
    655     trw->flags |= TRW_FLAG_REPORTED;
    656 
    657     /* Give the local admin a clue. */
    658     msg_info("TLSRPT: status=success, domain=%s, receiving_mx=%s[%s]",
    659 	     trw->rpt_policy_domain, trw->rcv_mta_name, trw->rcv_mta_addr);
    660 
    661     if ((res = tlsrpt_open(&con, trw->rpt_socket_name)) == 0) {
    662 	struct tlsrpt_dr_t *dr;
    663 
    664 	if ((res = tlsrpt_init_delivery_request(&dr, con,
    665 						trw->rpt_policy_domain,
    666 					    trw->rpt_policy_string)) == 0) {
    667 	    if ((res = tlsrpt_init_policy(dr, trw->tls_policy_type,
    668 					  trw->tls_policy_domain)) == 0) {
    669 		char  **cpp;
    670 
    671 		if (trw->tls_policy_strings)
    672 		    for (cpp = trw->tls_policy_strings->argv;
    673 			 res == 0 && *cpp; cpp++)
    674 			res = tlsrpt_add_policy_string(dr, *cpp);
    675 		if (trw->mx_host_patterns)
    676 		    for (cpp = trw->mx_host_patterns->argv;
    677 			 res == 0 && *cpp; cpp++)
    678 			res = tlsrpt_add_mx_host_pattern(dr, *cpp);
    679 		if (res == 0)
    680 		    res = tlsrpt_finish_policy(dr, TLSRPT_FINAL_SUCCESS);
    681 	    }
    682 	    if (res == 0) {
    683 		res = tlsrpt_finish_delivery_request(&dr);
    684 	    } else {
    685 		(void) tlsrpt_cancel_delivery_request(&dr);
    686 	    }
    687 	}
    688 	(void) tlsrpt_close(&con);
    689     }
    690     return (trw_munge_report_result(res));
    691 }
    692 
    693 /* trw_is_reported - trw_report_success() or trw_report_failure() called */
    694 
    695 int     trw_is_reported(const TLSRPT_WRAPPER *trw)
    696 {
    697     return (trw->flags & TRW_FLAG_REPORTED);
    698 }
    699 
    700 /* trw_is_skip_reused_hs - don't report TLS handshakes that reuse a session */
    701 
    702 int     trw_is_skip_reused_hs(const TLSRPT_WRAPPER *trw)
    703 {
    704     return (trw->skip_reused_hs);
    705 }
    706 
    707 #endif					/* USE_TLS_RPT */
    708 
    709  /*
    710   * Dummy definitions for builds without the TLSRPT library, so that we can
    711   * still validate names.
    712   */
    713 #if !defined(USE_TLSRPT)
    714 #define TLSRPT_POLICY_DANE	0
    715 #define TLSRPT_POLICY_STS	0
    716 #define TLSRPT_NO_POLICY_FOUND  0
    717 
    718 #define TLSRPT_VALIDATION_FAILURE       0
    719 #define TLSRPT_STS_POLICY_FETCH_ERROR   0
    720 #define TLSRPT_STS_POLICY_INVALID       0
    721 #define TLSRPT_STS_WEBPKI_INVALID       0
    722 #endif
    723 
    724  /*
    725   * Mapping from RFC 8460 string to libtlsrpt enum for policy types and
    726   * policy failures. The mapping assumes that all enum values are
    727   * non-negative.
    728   */
    729 const NAME_CODE tlsrpt_policy_type_mapping[] = {
    730     "sts", TLSRPT_POLICY_STS,
    731     "no-policy-found", TLSRPT_NO_POLICY_FOUND,
    732     0, -1,
    733 };
    734 
    735 const NAME_CODE tlsrpt_policy_failure_mapping[] = {
    736     "sts-policy-fetch-error", TLSRPT_STS_POLICY_FETCH_ERROR,
    737     "sts-policy-invalid", TLSRPT_STS_POLICY_INVALID,
    738     "sts-webpki-invalid", TLSRPT_STS_WEBPKI_INVALID,
    739     "validation-failure", TLSRPT_VALIDATION_FAILURE,
    740     0, -1,
    741 };
    742 
    743 /* valid_tlsrpt_policy_type - validate policy_type attribute value */
    744 
    745 int     valid_tlsrpt_policy_type(const char *policy_type)
    746 {
    747     return (name_code(tlsrpt_policy_type_mapping, NAME_CODE_FLAG_NONE,
    748 		      policy_type) >= 0);
    749 }
    750 
    751 /* valid_tlsrpt_policy_failure - validate policy_failure attribute value */
    752 
    753 int     valid_tlsrpt_policy_failure(const char *policy_failure)
    754 {
    755     return (name_code(tlsrpt_policy_failure_mapping, NAME_CODE_FLAG_NONE,
    756 		      policy_failure) >= 0);
    757 }
    758 
    759 #if defined(USE_TLSRPT)
    760 
    761 /* convert_tlsrpt_policy_type - convert string to enum */
    762 
    763 tlsrpt_policy_type_t convert_tlsrpt_policy_type(const char *policy_type)
    764 {
    765     return (name_code(tlsrpt_policy_type_mapping, NAME_CODE_FLAG_NONE,
    766 		      policy_type));
    767 }
    768 
    769 /* convert_tlsrpt_policy_failure - convert string to enum */
    770 
    771 tlsrpt_failure_t convert_tlsrpt_policy_failure(const char *policy_failure)
    772 {
    773     return (name_code(tlsrpt_policy_failure_mapping, NAME_CODE_FLAG_NONE,
    774 		      policy_failure));
    775 }
    776 
    777 #endif					/* USE_TLSRPT */
    778 
    779 #endif					/* USE_TLS */
    780