/*++ /* NAME /* tls_verify 3 /* SUMMARY /* peer name and peer certificate verification /* SYNOPSIS /* #define TLS_INTERNAL /* #include <tls.h> /* /* char *tls_peer_CN(peercert) /* X509 *peercert; /* /* char *tls_issuer_CN(peercert) /* X509 *peercert; /* /* int tls_verify_certificate_callback(ok, ctx) /* int ok; /* X509_STORE_CTX *ctx; /* DESCRIPTION /* tls_peer_CN() returns the text CommonName for the peer /* certificate subject, or a null pointer if no CommonName was /* found. The result is allocated with mymalloc() and must be /* freed by the caller. /* /* tls_issuer_CN() returns the text CommonName for the peer /* certificate issuer, or a null pointer if no CommonName was /* found. The result is allocated with mymalloc() and must be /* freed by the caller. /* /* tls_verify_callback() is called several times (directly or /* indirectly) from crypto/x509/x509_vfy.c. It is called as /* a final check, and if it returns "0", the handshake is /* immediately shut down and the connection fails. /* /* Postfix/TLS has two modes, the "opportunistic" mode and /* the "enforce" mode: /* /* In the "opportunistic" mode we never want the connection /* to fail just because there is something wrong with the /* peer's certificate. After all, we would have sent or received /* the mail even if TLS weren't available. Therefore the /* return value is always "1". /* /* The SMTP client or server may require TLS (e.g. to protect /* passwords), while peer certificates are optional. In this /* case we must return "1" even when we are unhappy with the /* peer certificate. Only when peer certificates are required, /* certificate verification failure will result in immediate /* termination (return 0). /* /* The only error condition not handled inside the OpenSSL /* library is the case of a too-long certificate chain. We /* test for this condition only if "ok = 1", that is, if /* verification didn't fail because of some earlier problem. /* /* Arguments: /* .IP ok /* Result of prior verification: non-zero means success. In /* order to reduce the noise level, some tests or error reports /* are disabled when verification failed because of some /* earlier problem. /* .IP ctx /* TLS client or server context. This also specifies the /* TLScontext with enforcement options. /* DIAGNOSTICS /* tls_peer_CN() and tls_issuer_CN() log a warning and return /* a null pointer when 1) the requested information is not /* available in the specified certificate, 2) the result /* exceeds a fixed limit, or 3) the result contains null /* characters. /* LICENSE /* .ad /* .fi /* This software is free. You can do with it whatever you want. /* The original author kindly requests that you acknowledge /* the use of his software. /* AUTHOR(S) /* Originally written by: /* Lutz Jaenicke /* BTU Cottbus /* Allgemeine Elektrotechnik /* Universitaetsplatz 3-4 /* D-03044 Cottbus, Germany /* /* Updated by: /* Wietse Venema /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA /*--*/ /* System library. */ #include <sys_defs.h> #ifdef USE_TLS #include <string.h> /* Utility library. */ #include <msg.h> #include <mymalloc.h> /* TLS library. */ #define TLS_INTERNAL #include <tls.h> /* tls_verify_certificate_callback - verify peer certificate info */ int tls_verify_certificate_callback(int ok, X509_STORE_CTX *ctx) { char buf[1024]; X509 *err_cert; int err; int depth; int verify_depth; SSL *con; TLScontext_t *TLScontext; /* Adapted from OpenSSL apps/s_cb.c */ err_cert = X509_STORE_CTX_get_current_cert(ctx); err = X509_STORE_CTX_get_error(ctx); depth = X509_STORE_CTX_get_error_depth(ctx); con = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); TLScontext = SSL_get_ex_data(con, TLScontext_index); X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf)); if (TLScontext->log_level >= 2) msg_info("certificate verification depth=%d subject=%s", depth, buf); /* * Test for a too long certificate chain, because that error condition is * not handled by the OpenSSL library. */ verify_depth = SSL_get_verify_depth(con); if (ok && (verify_depth >= 0) && (depth > verify_depth)) { ok = 0; err = X509_V_ERR_CERT_CHAIN_TOO_LONG; X509_STORE_CTX_set_error(ctx, err); } if (!ok) { msg_info("certificate verification failed for %s: num=%d:%s", TLScontext->peername, err, X509_verify_cert_error_string(err)); } /* * We delay peername verification until the SSL handshake completes. The * peername verification previously done here is now called directly from * tls_client_start(). This substantially simplifies the cache interface. */ /* * Other causes for verification failure. */ switch (ctx->error) { case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), buf, sizeof(buf)); msg_info("certificate verification failed for %s:" "issuer %s certificate unavailable", TLScontext->peername, buf); break; case X509_V_ERR_CERT_NOT_YET_VALID: case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: msg_info("certificate verification failed for %s:" "certificate not yet valid", TLScontext->peername); break; case X509_V_ERR_CERT_HAS_EXPIRED: case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: msg_info("certificate verification failed for %s:" "certificate has expired", TLScontext->peername); break; } if (TLScontext->log_level >= 2) msg_info("verify return: %d", ok); /* * Never fail in case of opportunistic mode. */ if (TLScontext->enforce_verify_errors) return (ok); else return (1); } #ifndef DONT_GRIPE #define DONT_GRIPE 0 #define DO_GRIPE 1 #endif /* tls_text_name - extract certificate property value by name */ static char *tls_text_name(X509_NAME *name, int nid, char *label, int gripe) { int len; int pos; X509_NAME_ENTRY *entry; ASN1_STRING *entry_str; unsigned char *tmp; char *result; if (name == 0 || (pos = X509_NAME_get_index_by_NID(name, nid, -1)) < 0) { if (gripe != DONT_GRIPE) { msg_warn("peer certificate has no %s", label); tls_print_errors(); } return (0); } #if 0 /* * If the match is required unambiguous, insist that that no * other values be present. */ if (unique == UNIQUE && X509_NAME_get_index_by_NID(name, nid, pos) >= 0) { msg_warn("multiple %ss in peer certificate", label); return (0); } #endif if ((entry = X509_NAME_get_entry(name, pos)) == 0) { /* This should not happen */ msg_warn("error reading peer certificate %s entry", label); tls_print_errors(); return (0); } if ((entry_str = X509_NAME_ENTRY_get_data(entry)) == 0) { /* This should not happen */ msg_warn("error reading peer certificate %s data", label); tls_print_errors(); return (0); } if ((len = ASN1_STRING_to_UTF8(&tmp, entry_str)) < 0) { /* This should not happen */ msg_warn("error decoding peer certificate %s data", label); tls_print_errors(); return (0); } /* * Since the peer CN is used in peer verification, take care to detect * truncation due to excessive length or internal NULs. */ if (len >= CCERT_BUFSIZ) { OPENSSL_free(tmp); msg_warn("peer %s too long: %d", label, (int) len); return (0); } /* * Standard UTF8 does not encode NUL as 0b11000000, that is * a Java "feature". So we need to check for embedded NULs. */ if (strlen((char *) tmp) != len) { msg_warn("internal NUL in peer %s", label); OPENSSL_free(tmp); return (0); } result = mystrdup((char *) tmp); OPENSSL_free(tmp); return (result); } /* tls_peer_CN - extract peer common name from certificate */ char *tls_peer_CN(X509 *peercert) { char *cn; cn = tls_text_name(X509_get_subject_name(peercert), NID_commonName, "subject CN", DO_GRIPE); return (cn); } /* tls_issuer_CN - extract issuer common name from certificate */ char *tls_issuer_CN(X509 *peer) { X509_NAME *name; char *cn; name = X509_get_issuer_name(peer); /* * If no issuer CN field, use Organization instead. CA certs without a CN * are common, so we only complain if the organization is also missing. */ if ((cn = tls_text_name(name, NID_commonName, "issuer CN", DONT_GRIPE)) == 0) cn = tls_text_name(name, NID_organizationName, "issuer Organization", DO_GRIPE); return (cn); } #endif