static void verify_extract_name(TLS_SESS_STATE *TLScontext, X509 *peercert, const TLS_CLIENT_START_PROPS *props) { int i; int r; int matched = 0; const char *dnsname; const GENERAL_NAME *gn; STACK_OF(GENERAL_NAME) * gens; /* * On exit both peer_CN and issuer_CN should be set. */ TLScontext->issuer_CN = tls_issuer_CN(peercert, TLScontext); /* * Is the certificate trust chain valid and trusted? */ if (SSL_get_verify_result(TLScontext->con) == X509_V_OK) TLScontext->peer_status |= TLS_CERT_FLAG_TRUSTED; if (TLS_CERT_IS_TRUSTED(TLScontext) && props->tls_level >= TLS_LEV_VERIFY) { /* * Verify the dNSName(s) in the peer certificate against the nexthop * and hostname. * * If DNS names are present, we use the first matching (or else simply * the first) DNS name as the subject CN. The CommonName in the * issuer DN is obsolete when SubjectAltName is available. This * yields much less surprising logs, because we log the name we * verified or a name we checked and failed to match. * * XXX: The nexthop and host name may both be the same network address * rather than a DNS name. In this case we really should be looking * for GEN_IPADD entries, not GEN_DNS entries. * * XXX: In ideal world the caller who used the address to build the * connection would tell us that the nexthop is the connection * address, but if that is not practical, we can parse the nexthop * again here. */ gens = X509_get_ext_d2i(peercert, NID_subject_alt_name, 0, 0); if (gens) { r = sk_GENERAL_NAME_num(gens); for (i = 0; i < r && !matched; ++i) { gn = sk_GENERAL_NAME_value(gens, i); if (gn->type != GEN_DNS) continue; /* * Even if we have an invalid DNS name, we still ultimately * ignore the CommonName, because subjectAltName:DNS is * present (though malformed). Replace any previous peer_CN * if empty or we get a match. * * We always set at least an empty peer_CN if the ALTNAME cert * flag is set. If not, we set peer_CN from the cert * CommonName below, so peer_CN is always non-null on return. */ TLScontext->peer_status |= TLS_CERT_FLAG_ALTNAME; dnsname = tls_dns_name(gn, TLScontext); if (dnsname && *dnsname) { matched = match_hostname(dnsname, props); if (TLScontext->peer_CN && (matched || *TLScontext->peer_CN == 0)) { acl_myfree(TLScontext->peer_CN); TLScontext->peer_CN = 0; } } if (TLScontext->peer_CN == 0) TLScontext->peer_CN = acl_mystrdup(dnsname ? dnsname : ""); } /* * (Sam Rushing, Ironport) Free stack *and* member GENERAL_NAME * objects */ sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free); } /* * No subjectAltNames, peer_CN is taken from CommonName. */ if (TLScontext->peer_CN == 0) { TLScontext->peer_CN = tls_peer_CN(peercert, TLScontext); if (*TLScontext->peer_CN) matched = match_hostname(TLScontext->peer_CN, props); } if (matched) TLScontext->peer_status |= TLS_CERT_FLAG_MATCHED; /* * - Matched: Trusted and peername matches - Trusted: Signed by * trusted CA(s), but peername not matched - Untrusted: Can't verify * the trust chain, reason already logged. */ if (TLScontext->log_level >= 2) acl_msg_info("%s: %s subject_CN=%s, issuer_CN=%s", props->namaddr, TLS_CERT_IS_MATCHED(TLScontext) ? "Matched" : TLS_CERT_IS_TRUSTED(TLScontext) ? "Trusted" : "Untrusted", TLScontext->peer_CN, TLScontext->issuer_CN); } else TLScontext->peer_CN = tls_peer_CN(peercert, TLScontext); /* * Give them a clue. Problems with trust chain verification were logged * when the session was first negotiated, before the session was stored * into the cache. We don't want mystery failures, so log the fact the * real problem is to be found in the past. */ if (TLScontext->session_reused && !TLS_CERT_IS_TRUSTED(TLScontext) && TLScontext->log_level >= 1) acl_msg_info("%s: re-using session with untrusted certificate, " "look for details earlier in the log", props->namaddr); }
static void verify_extract_name(TLS_SESS_STATE *TLScontext, X509 *peercert, const TLS_CLIENT_START_PROPS *props) { int i; int r; int matched = 0; int dnsname_match; int verify_peername = 0; int log_certmatch; int verbose; const char *dnsname; const GENERAL_NAME *gn; general_name_stack_t *gens; /* * On exit both peer_CN and issuer_CN should be set. */ TLScontext->issuer_CN = tls_issuer_CN(peercert, TLScontext); /* * Is the certificate trust chain valid and trusted? */ if (SSL_get_verify_result(TLScontext->con) == X509_V_OK) TLScontext->peer_status |= TLS_CERT_FLAG_TRUSTED; /* * With fingerprint or dane we may already be done. Otherwise, verify the * peername if using traditional PKI or DANE with trust-anchors. */ if (!TLS_CERT_IS_MATCHED(TLScontext) && TLS_CERT_IS_TRUSTED(TLScontext) && TLS_MUST_TRUST(props->tls_level)) verify_peername = 1; /* Force cert processing so we can log the data? */ log_certmatch = TLScontext->log_mask & TLS_LOG_CERTMATCH; /* Log cert details when processing? */ verbose = log_certmatch || (TLScontext->log_mask & TLS_LOG_VERBOSE); if (verify_peername || log_certmatch) { /* * Verify the dNSName(s) in the peer certificate against the nexthop * and hostname. * * If DNS names are present, we use the first matching (or else simply * the first) DNS name as the subject CN. The CommonName in the * issuer DN is obsolete when SubjectAltName is available. This * yields much less surprising logs, because we log the name we * verified or a name we checked and failed to match. * * XXX: The nexthop and host name may both be the same network address * rather than a DNS name. In this case we really should be looking * for GEN_IPADD entries, not GEN_DNS entries. * * XXX: In ideal world the caller who used the address to build the * connection would tell us that the nexthop is the connection * address, but if that is not practical, we can parse the nexthop * again here. */ gens = X509_get_ext_d2i(peercert, NID_subject_alt_name, 0, 0); if (gens) { r = sk_GENERAL_NAME_num(gens); for (i = 0; i < r; ++i) { gn = sk_GENERAL_NAME_value(gens, i); if (gn->type != GEN_DNS) continue; /* * Even if we have an invalid DNS name, we still ultimately * ignore the CommonName, because subjectAltName:DNS is * present (though malformed). Replace any previous peer_CN * if empty or we get a match. * * We always set at least an empty peer_CN if the ALTNAME cert * flag is set. If not, we set peer_CN from the cert * CommonName below, so peer_CN is always non-null on return. */ TLScontext->peer_status |= TLS_CERT_FLAG_ALTNAME; dnsname = tls_dns_name(gn, TLScontext); if (dnsname && *dnsname) { if ((dnsname_match = match_servername(dnsname, props)) != 0) matched++; /* Keep the first matched name. */ if (TLScontext->peer_CN && ((dnsname_match && matched == 1) || *TLScontext->peer_CN == 0)) { myfree(TLScontext->peer_CN); TLScontext->peer_CN = 0; } if (verbose) msg_info("%s: %ssubjectAltName: %s", props->namaddr, dnsname_match ? "Matched " : "", dnsname); } if (TLScontext->peer_CN == 0) TLScontext->peer_CN = mystrdup(dnsname ? dnsname : ""); if (matched && !log_certmatch) break; } if (verify_peername && matched) TLScontext->peer_status |= TLS_CERT_FLAG_MATCHED; /* * (Sam Rushing, Ironport) Free stack *and* member GENERAL_NAME * objects */ sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free); } /* * No subjectAltNames, peer_CN is taken from CommonName. */ if (TLScontext->peer_CN == 0) { TLScontext->peer_CN = tls_peer_CN(peercert, TLScontext); if (*TLScontext->peer_CN) matched = match_servername(TLScontext->peer_CN, props); if (verify_peername && matched) TLScontext->peer_status |= TLS_CERT_FLAG_MATCHED; if (verbose) msg_info("%s %sCommonName %s", props->namaddr, matched ? "Matched " : "", TLScontext->peer_CN); } else if (verbose) { char *tmpcn = tls_peer_CN(peercert, TLScontext); /* * Though the CommonName was superceded by a subjectAltName, log * it when certificate match debugging was requested. */ msg_info("%s CommonName %s", TLScontext->namaddr, tmpcn); myfree(tmpcn); } } else TLScontext->peer_CN = tls_peer_CN(peercert, TLScontext); /* * Give them a clue. Problems with trust chain verification are logged * when the session is first negotiated, before the session is stored * into the cache. We don't want mystery failures, so log the fact the * real problem is to be found in the past. */ if (!TLS_CERT_IS_TRUSTED(TLScontext) && (TLScontext->log_mask & TLS_LOG_UNTRUSTED)) { if (TLScontext->session_reused == 0) tls_log_verify_error(TLScontext); else msg_info("%s: re-using session with untrusted certificate, " "look for details earlier in the log", props->namaddr); } }
TLS_SESS_STATE *tls_server_post_accept(TLS_SESS_STATE *TLScontext) { const SSL_CIPHER *cipher; X509 *peer; char buf[CCERT_BUFSIZ]; /* Only loglevel==4 dumps everything */ if (TLScontext->log_level < 4) BIO_set_callback(SSL_get_rbio(TLScontext->con), 0); /* * The caller may want to know if this session was reused or if a new * session was negotiated. */ TLScontext->session_reused = SSL_session_reused(TLScontext->con); if (TLScontext->log_level >= 2 && TLScontext->session_reused) msg_info("%s: Reusing old session", TLScontext->namaddr); /* * Let's see whether a peer certificate is available and what is the * actual information. We want to save it for later use. */ peer = SSL_get_peer_certificate(TLScontext->con); if (peer != NULL) { TLScontext->peer_status |= TLS_CERT_FLAG_PRESENT; if (SSL_get_verify_result(TLScontext->con) == X509_V_OK) TLScontext->peer_status |= TLS_CERT_FLAG_TRUSTED; if (TLScontext->log_level >= 2) { X509_NAME_oneline(X509_get_subject_name(peer), buf, sizeof(buf)); msg_info("subject=%s", buf); X509_NAME_oneline(X509_get_issuer_name(peer), buf, sizeof(buf)); msg_info("issuer=%s", buf); } TLScontext->peer_CN = tls_peer_CN(peer, TLScontext); TLScontext->issuer_CN = tls_issuer_CN(peer, TLScontext); TLScontext->peer_fingerprint = tls_fingerprint(peer, TLScontext->fpt_dgst); if (TLScontext->log_level >= 1) { msg_info("%s: %s: subject_CN=%s, issuer=%s, fingerprint=%s", TLScontext->namaddr, TLS_CERT_IS_TRUSTED(TLScontext) ? "Trusted" : "Untrusted", TLScontext->peer_CN, TLScontext->issuer_CN, TLScontext->peer_fingerprint); } X509_free(peer); } else { TLScontext->peer_CN = mystrdup(""); TLScontext->issuer_CN = mystrdup(""); TLScontext->peer_fingerprint = mystrdup(""); } /* * Finally, collect information about protocol and cipher for logging */ TLScontext->protocol = SSL_get_version(TLScontext->con); cipher = SSL_get_current_cipher(TLScontext->con); TLScontext->cipher_name = SSL_CIPHER_get_name(cipher); TLScontext->cipher_usebits = SSL_CIPHER_get_bits(cipher, &(TLScontext->cipher_algbits)); /* * If the library triggered the SSL handshake, switch to the * tls_timed_read/write() functions and make the TLScontext available to * those functions. Otherwise, leave control over SSL_read/write/etc. * with the application. */ if (TLScontext->stream != 0) tls_stream_start(TLScontext->stream, TLScontext); /* * All the key facts in a single log entry. */ if (TLScontext->log_level >= 1) msg_info("%s TLS connection established from %s: %s with cipher %s " "(%d/%d bits)", !TLS_CERT_IS_PRESENT(TLScontext) ? "Anonymous" : TLS_CERT_IS_TRUSTED(TLScontext) ? "Trusted" : "Untrusted", TLScontext->namaddr, TLScontext->protocol, TLScontext->cipher_name, TLScontext->cipher_usebits, TLScontext->cipher_algbits); tls_int_seed(); return (TLScontext); }