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