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