1 /* $NetBSD: tls_verify.c,v 1.6 2026/05/09 18:49:21 christos Exp $ */ 2 3 /*++ 4 /* NAME 5 /* tls_verify 3 6 /* SUMMARY 7 /* peer name and peer certificate verification 8 /* SYNOPSIS 9 /* #define TLS_INTERNAL 10 /* #include <tls.h> 11 /* 12 /* int tls_verify_certificate_callback(ok, ctx) 13 /* int ok; 14 /* X509_STORE_CTX *ctx; 15 /* 16 /* int tls_log_verify_error(TLScontext, tlsrpt) 17 /* TLS_SESS_STATE *TLScontext; 18 /* struct TLSRPT_WRAPPER *tlsrpt; 19 /* 20 /* char *tls_peer_CN(peercert, TLScontext) 21 /* X509 *peercert; 22 /* TLS_SESS_STATE *TLScontext; 23 /* 24 /* char *tls_issuer_CN(peercert, TLScontext) 25 /* X509 *peercert; 26 /* TLS_SESS_STATE *TLScontext; 27 /* DESCRIPTION 28 /* tls_verify_certificate_callback() is called several times (directly 29 /* or indirectly) from crypto/x509/x509_vfy.c. It collects errors 30 /* and trust information at each element of the trust chain. 31 /* The last call at depth 0 sets the verification status based 32 /* on the cumulative winner (lowest depth) of errors vs. trust. 33 /* We always return 1 (continue the handshake) and handle trust 34 /* and peer-name verification problems at the application level. 35 /* 36 /* tls_log_verify_error() (called only when we care about the 37 /* peer certificate, that is not when opportunistic) logs the 38 /* reason why the certificate failed to be verified. 39 /* 40 /* tls_peer_CN() returns the text CommonName for the peer 41 /* certificate subject, or an empty string if no CommonName was 42 /* found. The result is allocated with mymalloc() and must be 43 /* freed by the caller; it contains UTF-8 without non-printable 44 /* ASCII characters. 45 /* 46 /* tls_issuer_CN() returns the text CommonName for the peer 47 /* certificate issuer, or an empty string if no CommonName was 48 /* found. The result is allocated with mymalloc() and must be 49 /* freed by the caller; it contains UTF-8 without non-printable 50 /* ASCII characters. 51 /* 52 /* Arguments: 53 /* .IP ok 54 /* Result of prior verification: non-zero means success. In 55 /* order to reduce the noise level, some tests or error reports 56 /* are disabled when verification failed because of some 57 /* earlier problem. 58 /* .IP ctx 59 /* SSL application context. This links to the Postfix TLScontext 60 /* with enforcement and logging options. 61 /* .IP gn 62 /* An OpenSSL GENERAL_NAME structure holding a DNS subjectAltName 63 /* to be decoded and checked for validity. 64 /* .IP peercert 65 /* Server or client X.509 certificate. 66 /* .IP TLScontext 67 /* Server or client context for warning messages. 68 /* DIAGNOSTICS 69 /* tls_peer_CN() and tls_issuer_CN() log a warning when 1) the requested 70 /* information is not available in the specified certificate, 2) the 71 /* result exceeds a fixed limit, 3) the result contains NUL characters or 72 /* the result contains non-printable or non-ASCII characters. 73 /* LICENSE 74 /* .ad 75 /* .fi 76 /* This software is free. You can do with it whatever you want. 77 /* The original author kindly requests that you acknowledge 78 /* the use of his software. 79 /* AUTHOR(S) 80 /* Originally written by: 81 /* Lutz Jaenicke 82 /* BTU Cottbus 83 /* Allgemeine Elektrotechnik 84 /* Universitaetsplatz 3-4 85 /* D-03044 Cottbus, Germany 86 /* 87 /* Updated by: 88 /* Wietse Venema 89 /* IBM T.J. Watson Research 90 /* P.O. Box 704 91 /* Yorktown Heights, NY 10598, USA 92 /* 93 /* Victor Duchovni 94 /* Morgan Stanley 95 /* 96 /* Wietse Venema 97 /* porcupine.org 98 /*--*/ 99 100 /* System library. */ 101 102 #include <sys_defs.h> 103 #include <ctype.h> 104 105 #ifdef USE_TLS 106 #include <string.h> 107 108 /* Utility library. */ 109 110 #include <msg.h> 111 #include <mymalloc.h> 112 #include <stringops.h> 113 114 /* TLS library. */ 115 116 #ifdef USE_TLSRPT 117 #include <tlsrpt_wrapper.h> 118 #endif 119 120 #define TLS_INTERNAL 121 #include <tls.h> 122 123 /* update_error_state - safely stash away error state */ 124 125 static void update_error_state(X509_STORE_CTX *ctx, TLS_SESS_STATE *TLScontext, 126 int depth, const X509 *errorcert, int errorcode) 127 { 128 129 /* 130 * Report the error that is closest to the leaf certificate, any errors 131 * higher up the chain are immaterial until the "inner" errors are fixed. 132 * 133 * We special-case "X509_V_ERR_HOSTNAME_MISMATCH" (at depth 0) in order to 134 * distinguish between untrusted certificates and trusted certificates 135 * with a hostname mismatch. Any other error has a higher priority. 136 */ 137 if (TLScontext->errordepth >= 0) { 138 if ((TLScontext->errordepth <= depth && 139 TLScontext->errorcode != X509_V_ERR_HOSTNAME_MISMATCH) || 140 errorcode == X509_V_ERR_HOSTNAME_MISMATCH) { 141 X509_STORE_CTX_set_error(ctx, TLScontext->errorcode); 142 return; 143 } 144 } 145 146 /* 147 * The certificate pointer is stable during the verification callback, 148 * but may be freed after the callback returns. Since we delay error 149 * reporting till later, we bump the refcount so we can rely on it still 150 * being there until later. 151 */ 152 if (TLScontext->errorcert != 0) 153 X509_free((X509 *) TLScontext->errorcert); 154 if (errorcert != 0) 155 X509_up_ref((X509 *) errorcert); 156 TLScontext->errorcert = errorcert; 157 TLScontext->errorcode = errorcode; 158 TLScontext->errordepth = depth; 159 } 160 161 /* tls_verify_certificate_callback - verify peer certificate info */ 162 163 int tls_verify_certificate_callback(int ok, X509_STORE_CTX *ctx) 164 { 165 char buf[CCERT_BUFSIZ]; 166 const X509 *cert; 167 int err; 168 int depth; 169 SSL *con; 170 TLS_SESS_STATE *TLScontext; 171 EVP_PKEY *rpk = 0; 172 173 /* May be NULL as of OpenSSL 1.0, thanks for the API change! */ 174 cert = X509_STORE_CTX_get_current_cert(ctx); 175 err = X509_STORE_CTX_get_error(ctx); 176 con = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); 177 TLScontext = SSL_get_ex_data(con, TLScontext_index); 178 depth = X509_STORE_CTX_get_error_depth(ctx); 179 #if OPENSSL_VERSION_PREREQ(3,2) 180 if (cert == 0) 181 rpk = X509_STORE_CTX_get0_rpk(ctx); 182 #endif 183 184 /* 185 * Transient failures to load the (DNS or synthetic TLSA) trust settings 186 * must poison certificate verification, since otherwise the default 187 * trust store may bless a certificate that would have failed 188 * verification with the preferred trust anchors (or fingerprints). 189 * 190 * Since we unconditionally continue, or in any case if verification is 191 * about to succeed, there is eventually a final depth 0 callback, at 192 * which point we force an "unspecified" error. The failure to load the 193 * trust settings was logged earlier. 194 */ 195 if (TLScontext->must_fail) { 196 if (depth == 0) { 197 X509_STORE_CTX_set_error(ctx, err = X509_V_ERR_UNSPECIFIED); 198 update_error_state(ctx, TLScontext, depth, cert, err); 199 } 200 return (1); 201 } 202 if (ok == 0) 203 update_error_state(ctx, TLScontext, depth, cert, err); 204 205 if (TLScontext->log_mask & TLS_LOG_VERBOSE) { 206 if (cert) { 207 X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf)); 208 msg_info("%s: depth=%d verify=%d subject=%s", 209 TLScontext->namaddr, depth, ok, printable(buf, '?')); 210 } else if (rpk) { 211 msg_info("%s: verify=%d raw public key", TLScontext->namaddr, ok); 212 } else { 213 msg_info("%s: depth=%d verify=%d", TLScontext->namaddr, depth, ok); 214 } 215 } 216 return (1); 217 } 218 219 /* tls_log_verify_error - Report final verification error status */ 220 221 void tls_log_verify_error(TLS_SESS_STATE *TLScontext, 222 struct TLSRPT_WRAPPER *tlsrpt) 223 { 224 char buf[CCERT_BUFSIZ]; 225 int err = TLScontext->errorcode; 226 const X509 *cert = TLScontext->errorcert; 227 int depth = TLScontext->errordepth; 228 229 #ifdef USE_TLSRPT 230 VSTRING *err_vstr = vstring_alloc(100); 231 232 #define CERT_ERROR_TO_STRING(err) \ 233 translit(vstring_str(vstring_strcpy(err_vstr, \ 234 X509_verify_cert_error_string(err))), \ 235 " ", "_") 236 #endif 237 238 #define PURPOSE ((depth>0) ? "CA": TLScontext->am_server ? "client": "server") 239 240 if (err == X509_V_OK) 241 return; 242 243 /* 244 * If an external policy flagged an error, report that instead. 245 */ 246 if (TLScontext->ffail_type) { 247 msg_info("certificate verification failed for %s: " 248 "external policy failure (%s)", 249 TLScontext->namaddr, TLScontext->ffail_type); 250 #ifdef USE_TLSRPT 251 if (tlsrpt) { 252 tlsrpt_failure_t failure_type; 253 254 if ((failure_type = convert_tlsrpt_policy_failure(TLScontext->ffail_type)) < 0) 255 msg_panic("tls_log_verify_error: unexpected failure_reason: %s", 256 TLScontext->ffail_type); 257 trw_report_failure(tlsrpt, failure_type, 258 /* additional_info= */ (char *) 0, 259 /* failure_reason= */ (char *) 0); 260 } 261 #endif 262 return; 263 } 264 265 /* 266 * Specific causes for verification failure. 267 */ 268 switch (err) { 269 case X509_V_ERR_CERT_UNTRUSTED: 270 271 /* 272 * We expect the error cert to be the leaf, but it is likely 273 * sufficient to omit it from the log, even less user confusion. 274 */ 275 msg_info("certificate verification failed for %s: " 276 "not trusted by local or TLSA policy", TLScontext->namaddr); 277 #ifdef USE_TLSRPT 278 if (tlsrpt) 279 trw_report_failure(tlsrpt, TLSRPT_CERTIFICATE_NOT_TRUSTED, 280 /* additional_info= */ (char *) 0, 281 /* failure_code= */ (char *) 0); 282 #endif 283 break; 284 case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: 285 msg_info("certificate verification failed for %s: " 286 "self-signed certificate", TLScontext->namaddr); 287 #ifdef USE_TLSRPT 288 if (tlsrpt) 289 trw_report_failure(tlsrpt, TLSRPT_VALIDATION_FAILURE, 290 /* additional_info= */ (char *) 0, 291 CERT_ERROR_TO_STRING(err)); 292 #endif 293 break; 294 case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: 295 case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: 296 297 /* 298 * There is no difference between issuing cert not provided and 299 * provided, but not found in CAfile/CApath. Either way, we don't 300 * trust it. 301 */ 302 if (cert) 303 X509_NAME_oneline(X509_get_issuer_name(cert), buf, sizeof(buf)); 304 else 305 strcpy(buf, "<unknown>"); 306 msg_info("certificate verification failed for %s: untrusted issuer %s", 307 TLScontext->namaddr, printable(buf, '?')); 308 #ifdef USE_TLSRPT 309 if (tlsrpt) 310 trw_report_failure(tlsrpt, TLSRPT_VALIDATION_FAILURE, 311 /* additional_info= */ (char *) 0, 312 CERT_ERROR_TO_STRING(err)); 313 #endif 314 break; 315 case X509_V_ERR_CERT_NOT_YET_VALID: 316 case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: 317 msg_info("%s certificate verification failed for %s: certificate not" 318 " yet valid", PURPOSE, TLScontext->namaddr); 319 #ifdef USE_TLSRPT 320 if (tlsrpt) 321 trw_report_failure(tlsrpt, TLSRPT_VALIDATION_FAILURE, 322 /* additional_info= */ (char *) 0, 323 CERT_ERROR_TO_STRING(err)); 324 #endif 325 break; 326 case X509_V_ERR_CERT_HAS_EXPIRED: 327 case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: 328 msg_info("%s certificate verification failed for %s: certificate has" 329 " expired", PURPOSE, TLScontext->namaddr); 330 #ifdef USE_TLSRPT 331 if (tlsrpt) 332 trw_report_failure(tlsrpt, TLSRPT_CERTIFICATE_EXPIRED, 333 /* additional_info= */ (char *) 0, 334 /* failure_code= */ (char *) 0); 335 #endif 336 break; 337 case X509_V_ERR_INVALID_PURPOSE: 338 msg_info("certificate verification failed for %s: not designated for " 339 "use as a %s certificate", TLScontext->namaddr, PURPOSE); 340 #ifdef USE_TLSRPT 341 if (tlsrpt) 342 trw_report_failure(tlsrpt, TLSRPT_VALIDATION_FAILURE, 343 /* additional_info= */ (char *) 0, 344 CERT_ERROR_TO_STRING(err)); 345 #endif 346 break; 347 case X509_V_ERR_CERT_CHAIN_TOO_LONG: 348 msg_info("certificate verification failed for %s: " 349 "certificate chain longer than limit(%d)", 350 TLScontext->namaddr, depth - 1); 351 #ifdef USE_TLSRPT 352 if (tlsrpt) 353 trw_report_failure(tlsrpt, TLSRPT_VALIDATION_FAILURE, 354 /* additional_info= */ (char *) 0, 355 CERT_ERROR_TO_STRING(err)); 356 #endif 357 break; 358 default: 359 msg_info("%s certificate verification failed for %s: num=%d:%s", 360 PURPOSE, TLScontext->namaddr, err, 361 X509_verify_cert_error_string(err)); 362 break; 363 } 364 #ifdef USE_TLSRPT 365 vstring_free(err_vstr); 366 #endif 367 } 368 369 #ifndef DONT_GRIPE 370 #define DONT_GRIPE 0 371 #define DO_GRIPE 1 372 #endif 373 374 /* tls_text_name - extract certificate property value by name */ 375 376 static char *tls_text_name(const X509_NAME *name, int nid, const char *label, 377 const TLS_SESS_STATE *TLScontext, int gripe) 378 { 379 const char *myname = "tls_text_name"; 380 int pos; 381 const X509_NAME_ENTRY *entry; 382 const ASN1_STRING *entry_str; 383 int asn1_type; 384 int utf8_length; 385 unsigned char *utf8_value; 386 int ch; 387 unsigned char *cp; 388 389 if (name == 0 || (pos = X509_NAME_get_index_by_NID((X509_NAME *) name, nid, -1)) < 0) { 390 if (gripe != DONT_GRIPE) { 391 msg_warn("%s: %s: peer certificate has no %s", 392 myname, TLScontext->namaddr, label); 393 tls_print_errors(); 394 } 395 return (0); 396 } 397 #if 0 398 399 /* 400 * If the match is required unambiguous, insist that that no other values 401 * be present. 402 */ 403 if (X509_NAME_get_index_by_NID(name, nid, pos) >= 0) { 404 msg_warn("%s: %s: multiple %ss in peer certificate", 405 myname, TLScontext->namaddr, label); 406 return (0); 407 } 408 #endif 409 410 if ((entry = X509_NAME_get_entry(name, pos)) == 0) { 411 /* This should not happen */ 412 msg_warn("%s: %s: error reading peer certificate %s entry", 413 myname, TLScontext->namaddr, label); 414 tls_print_errors(); 415 return (0); 416 } 417 if ((entry_str = X509_NAME_ENTRY_get_data(entry)) == 0) { 418 /* This should not happen */ 419 msg_warn("%s: %s: error reading peer certificate %s data", 420 myname, TLScontext->namaddr, label); 421 tls_print_errors(); 422 return (0); 423 } 424 425 /* 426 * XXX Convert everything into UTF-8. This is a super-set of ASCII, so we 427 * don't have to bother with separate code paths for ASCII-like content. 428 * If the payload is ASCII then we won't waste lots of CPU cycles 429 * converting it into UTF-8. It's up to OpenSSL to do something 430 * reasonable when converting ASCII formats that contain non-ASCII 431 * content. 432 * 433 * XXX Don't bother optimizing the string length error check. It is not 434 * worth the complexity. 435 */ 436 asn1_type = ASN1_STRING_type(entry_str); 437 if ((utf8_length = ASN1_STRING_to_UTF8(&utf8_value, entry_str)) < 0) { 438 msg_warn("%s: %s: error decoding peer %s of ASN.1 type=%d", 439 myname, TLScontext->namaddr, label, asn1_type); 440 tls_print_errors(); 441 return (0); 442 } 443 444 /* 445 * No returns without cleaning up. A good optimizer will replace multiple 446 * blocks of identical code by jumps to just one such block. 447 */ 448 #define TLS_TEXT_NAME_RETURN(x) do { \ 449 char *__tls_text_name_temp = (x); \ 450 OPENSSL_free(utf8_value); \ 451 return (__tls_text_name_temp); \ 452 } while (0) 453 454 /* 455 * Remove trailing null characters. They would give false alarms with the 456 * length check and with the embedded null check. 457 */ 458 #define TRIM0(s, l) do { while ((l) > 0 && (s)[(l)-1] == 0) --(l); } while (0) 459 460 TRIM0(utf8_value, utf8_length); 461 462 /* 463 * Enforce the length limit, because the caller will copy the result into 464 * a fixed-length buffer. 465 */ 466 if (utf8_length >= CCERT_BUFSIZ) { 467 msg_warn("%s: %s: peer %s too long: %d", 468 myname, TLScontext->namaddr, label, utf8_length); 469 TLS_TEXT_NAME_RETURN(0); 470 } 471 472 /* 473 * Reject embedded nulls in ASCII or UTF-8 names. OpenSSL is responsible 474 * for producing properly-formatted UTF-8. 475 */ 476 if (utf8_length != strlen((char *) utf8_value)) { 477 msg_warn("%s: %s: NULL character in peer %s", 478 myname, TLScontext->namaddr, label); 479 TLS_TEXT_NAME_RETURN(0); 480 } 481 482 /* 483 * Reject non-printable ASCII characters in UTF-8 content. 484 * 485 * Note: the code below does not find control characters in illegal UTF-8 486 * sequences. It's OpenSSL's job to produce valid UTF-8, and reportedly, 487 * it does validation. 488 */ 489 for (cp = utf8_value; (ch = *cp) != 0; cp++) { 490 if (ISASCII(ch) && !ISPRINT(ch)) { 491 msg_warn("%s: %s: non-printable content in peer %s", 492 myname, TLScontext->namaddr, label); 493 TLS_TEXT_NAME_RETURN(0); 494 } 495 } 496 TLS_TEXT_NAME_RETURN(mystrdup((char *) utf8_value)); 497 } 498 499 /* tls_peer_CN - extract peer common name from certificate */ 500 501 char *tls_peer_CN(X509 *peercert, const TLS_SESS_STATE *TLScontext) 502 { 503 char *cn; 504 const char *san; 505 506 /* Absent a commonName, return a validated DNS-ID SAN */ 507 cn = tls_text_name(X509_get_subject_name(peercert), NID_commonName, 508 "subject CN", TLScontext, DONT_GRIPE); 509 if (cn == 0 && (san = SSL_get0_peername(TLScontext->con)) != 0) 510 cn = mystrdup(san); 511 return (cn ? cn : mystrdup("")); 512 } 513 514 /* tls_issuer_CN - extract issuer common name from certificate */ 515 516 char *tls_issuer_CN(X509 *peer, const TLS_SESS_STATE *TLScontext) 517 { 518 const X509_NAME *name; 519 char *cn; 520 521 name = X509_get_issuer_name(peer); 522 523 /* 524 * If no issuer CN field, use Organization instead. CA certs without a CN 525 * are common, so we only complain if the organization is also missing. 526 */ 527 if ((cn = tls_text_name(name, NID_commonName, 528 "issuer CN", TLScontext, DONT_GRIPE)) == 0) 529 cn = tls_text_name(name, NID_organizationName, 530 "issuer Organization", TLScontext, DONT_GRIPE); 531 return (cn ? cn : mystrdup("")); 532 } 533 534 #endif 535