Home | History | Annotate | Line # | Download | only in tls
      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