Blame SOURCES/0009-Check-names-in-the-server-s-cert-when-using-KKDCP.patch

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