|
|
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 |
|