Blob Blame History Raw
From f7825e81b1ebf533c1dba9f84ae9ad36073a89cf Mon Sep 17 00:00:00 2001
From: Nalin Dahyabhai <nalin@dahyabhai.net>
Date: Thu, 17 Apr 2014 17:19:03 -0400
Subject: [PATCH 09/13] Check names in the server's cert when using KKDCP

When we connect to a KDC using an HTTPS proxy, check that the naming
information in the certificate matches the name or address which we
extracted from the server URL in the configuration.

ticket: 7929
---
 src/include/k5-trace.h       |   5 +
 src/lib/krb5/os/Makefile.in  |   3 +
 src/lib/krb5/os/checkhost.c  | 251 +++++++++++++++++++++++++++++++++++++++++++
 src/lib/krb5/os/checkhost.h  |  39 +++++++
 src/lib/krb5/os/deps         |  14 ++-
 src/lib/krb5/os/sendto_kdc.c |  53 +++++++--
 6 files changed, 355 insertions(+), 10 deletions(-)
 create mode 100644 src/lib/krb5/os/checkhost.c
 create mode 100644 src/lib/krb5/os/checkhost.h

diff --git a/src/include/k5-trace.h b/src/include/k5-trace.h
index 046bc95..9e75b29 100644
--- a/src/include/k5-trace.h
+++ b/src/include/k5-trace.h
@@ -324,6 +324,11 @@ void krb5int_trace(krb5_context context, const char *fmt, ...);
     TRACE(c, "Resolving hostname {str}", hostname)
 #define TRACE_SENDTO_KDC_RESPONSE(c, len, raddr)                        \
     TRACE(c, "Received answer ({int} bytes) from {raddr}", len, raddr)
+#define TRACE_SENDTO_KDC_HTTPS_SERVER_NAME_MISMATCH(c, hostname)        \
+    TRACE(c, "HTTPS certificate name mismatch: server certificate is "  \
+          "not for \"{str}\"", hostname)
+#define TRACE_SENDTO_KDC_HTTPS_SERVER_NAME_MATCH(c, hostname)           \
+    TRACE(c, "HTTPS certificate name matched \"{str}\"", hostname)
 #define TRACE_SENDTO_KDC_HTTPS_NO_REMOTE_CERTIFICATE(c)                 \
     TRACE(c, "HTTPS server certificate not received")
 #define TRACE_SENDTO_KDC_HTTPS_PROXY_CERTIFICATE_ERROR(c, depth,        \
diff --git a/src/lib/krb5/os/Makefile.in b/src/lib/krb5/os/Makefile.in
index fb4001a..fa8a093 100644
--- a/src/lib/krb5/os/Makefile.in
+++ b/src/lib/krb5/os/Makefile.in
@@ -13,6 +13,7 @@ STLIBOBJS= \
 	c_ustime.o	\
 	ccdefname.o	\
 	changepw.o	\
+	checkhost.o	\
 	dnsglue.o	\
 	dnssrv.o	\
 	expand_path.o	\
@@ -59,6 +60,7 @@ OBJS= \
 	$(OUTPRE)c_ustime.$(OBJEXT)	\
 	$(OUTPRE)ccdefname.$(OBJEXT)	\
 	$(OUTPRE)changepw.$(OBJEXT)	\
+	$(OUTPRE)checkhost.$(OBJEXT)	\
 	$(OUTPRE)dnsglue.$(OBJEXT)	\
 	$(OUTPRE)dnssrv.$(OBJEXT)	\
 	$(OUTPRE)expand_path.$(OBJEXT)	\
@@ -105,6 +107,7 @@ SRCS= \
 	$(srcdir)/c_ustime.c	\
 	$(srcdir)/ccdefname.c	\
 	$(srcdir)/changepw.c	\
+	$(srcdir)/checkhost.c	\
 	$(srcdir)/dnsglue.c	\
 	$(srcdir)/dnssrv.c	\
 	$(srcdir)/expand_path.c	\
diff --git a/src/lib/krb5/os/checkhost.c b/src/lib/krb5/os/checkhost.c
new file mode 100644
index 0000000..a91615d
--- /dev/null
+++ b/src/lib/krb5/os/checkhost.c
@@ -0,0 +1,251 @@
+/*
+ * Copyright 2014 Red Hat, Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *    1. Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *
+ *    2. Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "k5-int.h"
+#include "k5-utf8.h"
+
+#ifdef PROXY_TLS_IMPL_OPENSSL
+#include <openssl/ssl.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+#include "checkhost.h"
+
+/* Return the passed-in character, lower-cased if it's an ASCII character. */
+static inline char
+ascii_tolower(char p)
+{
+    if (KRB5_UPPER(p))
+        return p + ('a' - 'A');
+    return p;
+}
+
+/*
+ * Check a single label.  If allow_wildcard is true, and the presented name
+ * includes a wildcard, return true and note that we matched a wildcard.
+ * Otherwise, for both the presented and expected values, do a case-insensitive
+ * comparison of ASCII characters, and a case-sensitive comparison of
+ * everything else.
+ */
+static krb5_boolean
+label_match(const char *presented, size_t plen, const char *expected,
+            size_t elen, krb5_boolean allow_wildcard, krb5_boolean *wildcard)
+{
+    unsigned int i;
+
+    if (allow_wildcard && plen == 1 && presented[0] == '*') {
+        *wildcard = TRUE;
+        return TRUE;
+    }
+
+    if (plen != elen)
+        return FALSE;
+
+    for (i = 0; i < elen; i++) {
+        if (ascii_tolower(presented[i]) != ascii_tolower(expected[i]))
+            return FALSE;
+    }
+    return TRUE;
+}
+
+/* Break up the two names and check them, label by label. */
+static krb5_boolean
+domain_match(const char *presented, size_t plen, const char *expected)
+{
+    const char *p, *q, *r, *s;
+    int n_label;
+    krb5_boolean used_wildcard = FALSE;
+
+    n_label = 0;
+    p = presented;
+    r = expected;
+    while (p < presented + plen && *r != '\0') {
+        q = memchr(p, '.', plen - (p - presented));
+        if (q == NULL)
+            q = presented + plen;
+        s = r + strcspn(r, ".");
+        if (!label_match(p, q - p, r, s - r, n_label == 0, &used_wildcard))
+            return FALSE;
+        p = q < presented + plen ? q + 1 : q;
+        r = *s ? s + 1 : s;
+        n_label++;
+    }
+    if (used_wildcard && n_label <= 2)
+        return FALSE;
+    if (p == presented + plen && *r == '\0')
+        return TRUE;
+    return FALSE;
+}
+
+/* Fetch the list of subjectAltNames from a certificate. */
+static GENERAL_NAMES *
+get_cert_sans(X509 *x)
+{
+    int ext;
+    X509_EXTENSION *san_ext;
+
+    ext = X509_get_ext_by_NID(x, NID_subject_alt_name, -1);
+    if (ext < 0)
+        return NULL;
+    san_ext = X509_get_ext(x, ext);
+    if (san_ext == NULL)
+        return NULL;
+    return X509V3_EXT_d2i(san_ext);
+}
+
+/* Fetch a CN value from the subjct name field, returning its length, or -1 if
+ * there is no subject name or it contains no CN value. */
+static ssize_t
+get_cert_cn(X509 *x, char *buf, size_t bufsize)
+{
+    X509_NAME *name;
+
+    name = X509_get_subject_name(x);
+    if (name == NULL)
+        return -1;
+    return X509_NAME_get_text_by_NID(name, NID_commonName, buf, bufsize);
+}
+
+/*
+ * Return true if the passed-in expected IP address matches any of the names we
+ * can recover from the server certificate, false otherwise.
+ */
+krb5_boolean
+k5_check_cert_address(X509 *x, const char *text)
+{
+    char buf[1024];
+    GENERAL_NAMES *sans;
+    GENERAL_NAME *san = NULL;
+    ASN1_OCTET_STRING *ip;
+    krb5_boolean found_ip_san = FALSE, matched = FALSE;
+    int n_sans, i;
+    size_t name_length;
+    union {
+        struct in_addr in;
+        struct in6_addr in6;
+    } name;
+
+    /* Parse the IP address into an octet string. */
+    ip = M_ASN1_OCTET_STRING_new();
+    if (ip == NULL)
+        return FALSE;
+
+    if (inet_aton(text, &name.in) == 1)
+        name_length = sizeof(name.in);
+    else if (inet_pton(AF_INET6, text, &name.in6) == 1)
+        name_length = sizeof(name.in6);
+    else
+        name_length = 0;
+
+    if (name_length == 0) {
+        ASN1_OCTET_STRING_free(ip);
+        return FALSE;
+    }
+    M_ASN1_OCTET_STRING_set(ip, &name, name_length);
+
+    /* Check for matches in ipaddress subjectAltName values. */
+    sans = get_cert_sans(x);
+    if (sans != NULL) {
+        n_sans = sk_GENERAL_NAME_num(sans);
+        for (i = 0; i < n_sans; i++) {
+            san = sk_GENERAL_NAME_value(sans, i);
+            if (san->type != GEN_IPADD)
+                continue;
+            found_ip_san = TRUE;
+            matched = ASN1_OCTET_STRING_cmp(ip, san->d.iPAddress) == 0;
+            if (matched)
+                break;
+        }
+        sk_GENERAL_NAME_pop_free(sans, GENERAL_NAME_free);
+    }
+    ASN1_OCTET_STRING_free(ip);
+
+    if (matched)
+        return TRUE;
+    if (found_ip_san)
+        return matched;
+
+    /* Check for a match against the CN value in the peer's subject name. */
+    name_length = get_cert_cn(x, buf, sizeof(buf));
+    if (name_length >= 0) {
+        /* Do a string compare to check if it's an acceptable value. */
+        return strlen(text) == name_length &&
+               strncmp(text, buf, name_length) == 0;
+    }
+
+    /* We didn't find a match. */
+    return FALSE;
+}
+
+/*
+ * Return true if the passed-in expected name matches any of the names we can
+ * recover from a server certificate, false otherwise.
+ */
+krb5_boolean
+k5_check_cert_servername(X509 *x, const char *expected)
+{
+    char buf[1024];
+    GENERAL_NAMES *sans;
+    GENERAL_NAME *san = NULL;
+    unsigned char *dnsname;
+    krb5_boolean found_dns_san = FALSE, matched = FALSE;
+    int name_length, n_sans, i;
+
+    /* Check for matches in dnsname subjectAltName values. */
+    sans = get_cert_sans(x);
+    if (sans != NULL) {
+        n_sans = sk_GENERAL_NAME_num(sans);
+        for (i = 0; i < n_sans; i++) {
+            san = sk_GENERAL_NAME_value(sans, i);
+            if (san->type != GEN_DNS)
+                continue;
+            found_dns_san = TRUE;
+            dnsname = NULL;
+            name_length = ASN1_STRING_to_UTF8(&dnsname, san->d.dNSName);
+            if (dnsname == NULL)
+                continue;
+            matched = domain_match((char *)dnsname, name_length, expected);
+            OPENSSL_free(dnsname);
+            if (matched)
+                break;
+        }
+        sk_GENERAL_NAME_pop_free(sans, GENERAL_NAME_free);
+    }
+
+    if (matched)
+        return TRUE;
+    if (found_dns_san)
+        return matched;
+
+    /* Check for a match against the CN value in the peer's subject name. */
+    name_length = get_cert_cn(x, buf, sizeof(buf));
+    if (name_length >= 0)
+        return domain_match(buf, name_length, expected);
+
+    /* We didn't find a match. */
+    return FALSE;
+}
+#endif
diff --git a/src/lib/krb5/os/checkhost.h b/src/lib/krb5/os/checkhost.h
new file mode 100644
index 0000000..b9d751e
--- /dev/null
+++ b/src/lib/krb5/os/checkhost.h
@@ -0,0 +1,39 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 2014 Red Hat, Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *    1. Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *
+ *    2. Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Certificate subjectAltName check prototypes.
+ */
+
+#ifndef KRB5_LIBOS_CHECKHOST_PROTO__
+#define KRB5_LIBOS_CHECKHOST_PROTO__
+
+krb5_boolean k5_check_cert_servername(X509 *x, const char *expected);
+krb5_boolean k5_check_cert_address(X509 *x, const char *expected);
+
+#endif /* KRB5_LIBOS_CHECKHOST_PROTO__ */
diff --git a/src/lib/krb5/os/deps b/src/lib/krb5/os/deps
index 3dd6d46..d56ff30 100644
--- a/src/lib/krb5/os/deps
+++ b/src/lib/krb5/os/deps
@@ -49,6 +49,17 @@ changepw.so changepw.po $(OUTPRE)changepw.$(OBJEXT): \
   $(top_srcdir)/include/krb5/locate_plugin.h $(top_srcdir)/include/krb5/plugin.h \
   $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
   changepw.c os-proto.h
+checkhost.so checkhost.po $(OUTPRE)checkhost.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
+  $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
+  $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
+  $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
+  $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
+  $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
+  $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/k5-utf8.h \
+  $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
+  $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
+  $(top_srcdir)/include/socket-utils.h checkhost.c checkhost.h
 dnsglue.so dnsglue.po $(OUTPRE)dnsglue.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
   $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
   $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
@@ -418,7 +429,8 @@ sendto_kdc.so sendto_kdc.po $(OUTPRE)sendto_kdc.$(OBJEXT): \
   $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \
   $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/locate_plugin.h \
   $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
-  $(top_srcdir)/include/socket-utils.h os-proto.h sendto_kdc.c
+  $(top_srcdir)/include/socket-utils.h checkhost.h os-proto.h \
+  sendto_kdc.c
 sn2princ.so sn2princ.po $(OUTPRE)sn2princ.$(OBJEXT): \
   $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
   $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
diff --git a/src/lib/krb5/os/sendto_kdc.c b/src/lib/krb5/os/sendto_kdc.c
index 4bd8698..f083c0f 100644
--- a/src/lib/krb5/os/sendto_kdc.c
+++ b/src/lib/krb5/os/sendto_kdc.c
@@ -80,6 +80,7 @@
 #include <openssl/x509.h>
 #include <openssl/x509v3.h>
 #include <dirent.h>
+#include "checkhost.h"
 #endif
 
 #define MAX_PASS                    3
@@ -148,6 +149,7 @@ struct conn_state {
     krb5_boolean defer;
     struct {
         const char *uri_path;
+        const char *servername;
         char *https_request;
 #ifdef PROXY_TLS_IMPL_OPENSSL
         SSL *ssl;
@@ -158,6 +160,7 @@ struct conn_state {
 #ifdef PROXY_TLS_IMPL_OPENSSL
 /* Extra-data identifier, used to pass context into the verify callback. */
 static int ssl_ex_context_id = -1;
+static int ssl_ex_conn_id = -1;
 #endif
 
 void
@@ -169,6 +172,7 @@ k5_sendto_kdc_initialize(void)
     OpenSSL_add_all_algorithms();
 
     ssl_ex_context_id = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
+    ssl_ex_conn_id = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
 #endif
 }
 
@@ -635,7 +639,8 @@ set_transport_message(struct conn_state *state, const krb5_data *realm,
 static krb5_error_code
 add_connection(struct conn_state **conns, k5_transport transport,
                krb5_boolean defer, struct addrinfo *ai, size_t server_index,
-               const krb5_data *realm, const char *uri_path, char **udpbufp)
+               const krb5_data *realm, const char *hostname,
+               const char *uri_path, char **udpbufp)
 {
     struct conn_state *state, **tailptr;
 
@@ -661,6 +666,7 @@ add_connection(struct conn_state **conns, k5_transport transport,
         state->service_write = service_https_write;
         state->service_read = service_https_read;
         state->http.uri_path = uri_path;
+        state->http.servername = hostname;
     } else {
         state->service_connect = NULL;
         state->service_write = NULL;
@@ -760,8 +766,8 @@ resolve_server(krb5_context context, const krb5_data *realm,
         ai.ai_addrlen = entry->addrlen;
         ai.ai_addr = (struct sockaddr *)&entry->addr;
         defer = (entry->transport != transport);
-        return add_connection(conns, entry->transport, defer, &ai, ind,
-                              realm, entry->uri_path, udpbufp);
+        return add_connection(conns, entry->transport, defer, &ai, ind, realm,
+                              NULL, entry->uri_path, udpbufp);
     }
 
     /* If the entry has a specified transport, use it. */
@@ -787,7 +793,7 @@ resolve_server(krb5_context context, const krb5_data *realm,
     retval = 0;
     for (a = addrs; a != 0 && retval == 0; a = a->ai_next) {
         retval = add_connection(conns, transport, FALSE, a, ind, realm,
-                                entry->uri_path, udpbufp);
+                                entry->hostname, entry->uri_path, udpbufp);
     }
 
     /* For TCP_OR_UDP entries, add each address again with the non-preferred
@@ -797,7 +803,7 @@ resolve_server(krb5_context context, const krb5_data *realm,
         for (a = addrs; a != 0 && retval == 0; a = a->ai_next) {
             a->ai_socktype = socktype_for_transport(transport);
             retval = add_connection(conns, transport, TRUE, a, ind, realm,
-                                    entry->uri_path, udpbufp);
+                                    entry->hostname, entry->uri_path, udpbufp);
         }
     }
     freeaddrinfo(addrs);
@@ -1260,6 +1266,20 @@ cleanup:
     return err;
 }
 
+static krb5_boolean
+ssl_check_name_or_ip(X509 *x, const char *expected_name)
+{
+    struct in_addr in;
+    struct in6_addr in6;
+
+    if (inet_aton(expected_name, &in) != 0 ||
+        inet_pton(AF_INET6, expected_name, &in6) != 0) {
+        return k5_check_cert_address(x, expected_name);
+    } else {
+        return k5_check_cert_servername(x, expected_name);
+    }
+}
+
 static int
 ssl_verify_callback(int preverify_ok, X509_STORE_CTX *store_ctx)
 {
@@ -1268,12 +1288,14 @@ ssl_verify_callback(int preverify_ok, X509_STORE_CTX *store_ctx)
     BIO *bio;
     krb5_context context;
     int err, depth;
-    const char *cert = NULL, *errstr;
+    struct conn_state *conn = NULL;
+    const char *cert = NULL, *errstr, *expected_name;
     size_t count;
 
     ssl = X509_STORE_CTX_get_ex_data(store_ctx,
                                      SSL_get_ex_data_X509_STORE_CTX_idx());
     context = SSL_get_ex_data(ssl, ssl_ex_context_id);
+    conn = SSL_get_ex_data(ssl, ssl_ex_conn_id);
     /* We do have the peer's cert, right? */
     x = X509_STORE_CTX_get_current_cert(store_ctx);
     if (x == NULL) {
@@ -1299,8 +1321,19 @@ ssl_verify_callback(int preverify_ok, X509_STORE_CTX *store_ctx)
         }
         return 0;
     }
-    /* All done. */
-    return 1;
+    /* If we're not looking at the peer, we're done and everything's ok. */
+    if (depth != 0)
+        return 1;
+    /* Check if the name we expect to find is in the certificate. */
+    expected_name = conn->http.servername;
+    if (ssl_check_name_or_ip(x, expected_name)) {
+        TRACE_SENDTO_KDC_HTTPS_SERVER_NAME_MATCH(context, expected_name);
+        return 1;
+    } else {
+        TRACE_SENDTO_KDC_HTTPS_SERVER_NAME_MISMATCH(context, expected_name);
+    }
+    /* The name didn't match. */
+    return 0;
 }
 
 /*
@@ -1317,7 +1350,7 @@ setup_ssl(krb5_context context, const krb5_data *realm,
     SSL_CTX *ctx = NULL;
     SSL *ssl = NULL;
 
-    if (ssl_ex_context_id == -1)
+    if (ssl_ex_context_id == -1 || ssl_ex_conn_id == -1)
         goto kill_conn;
 
     /* Do general SSL library setup. */
@@ -1339,6 +1372,8 @@ setup_ssl(krb5_context context, const krb5_data *realm,
 
     if (!SSL_set_ex_data(ssl, ssl_ex_context_id, context))
         goto kill_conn;
+    if (!SSL_set_ex_data(ssl, ssl_ex_conn_id, conn))
+        goto kill_conn;
 
     /* Tell the SSL library about the socket. */
     if (!SSL_set_fd(ssl, conn->fd))
-- 
2.1.0