Exemple #1
0
/* based on verify_extract_name from tls_client.c in postfix */
static gboolean irssi_ssl_verify_hostname(X509 *cert, const char *hostname)
{
	int gen_index, gen_count;
	gboolean matched = FALSE, has_dns_name = FALSE;
	const char *cert_dns_name;
	char *cert_subject_cn;
	const GENERAL_NAME *gn;
	STACK_OF(GENERAL_NAME) * gens;
	GString *alt_names;

	alt_names = g_string_new("");

	/* Verify the dNSName(s) in the peer certificate against the hostname. */
	gens = X509_get_ext_d2i(cert, NID_subject_alt_name, 0, 0);
	if (gens) {
		gen_count = sk_GENERAL_NAME_num(gens);
		for (gen_index = 0; gen_index < gen_count && !matched; ++gen_index) {
			gn = sk_GENERAL_NAME_value(gens, gen_index);
			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). */
			has_dns_name = TRUE;
			cert_dns_name = tls_dns_name(gn);
			if (cert_dns_name && *cert_dns_name) {
				g_string_append_printf(alt_names, " '%s'", cert_dns_name);
				matched = match_hostname(cert_dns_name, hostname);
			}
    	}

	    /* Free stack *and* member GENERAL_NAME objects */
	    sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free);
	}

	if (has_dns_name) {
		if (! matched) {
			/* The CommonName in the issuer DN is obsolete when SubjectAltName is available. */
			g_warning("None of the Subject Alt Names%s in the certificate match hostname '%s'", alt_names->str, hostname);
		}
		g_string_free(alt_names, TRUE);
		return matched;
	} else { /* No subjectAltNames, look at CommonName */
		g_string_free(alt_names, TRUE);
		cert_subject_cn = tls_text_name(X509_get_subject_name(cert), NID_commonName);
	    if (cert_subject_cn && *cert_subject_cn) {
	    	matched = match_hostname(cert_subject_cn, hostname);
	    	if (! matched) {
				g_warning("SSL certificate common name '%s' doesn't match host name '%s'", cert_subject_cn, hostname);
	    	}
	    } else {
	    	g_warning("No subjectAltNames and no valid common name in certificate");
	    }
	    free(cert_subject_cn);
	}

	return matched;
}
Exemple #2
0
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);
    }
}
Exemple #3
0
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);
}