Blame SOURCES/krb5-1.12ish-tls-plugins.patch

4be148
Adapt to headers being included in a different order in sendto_kdc.c.
4be148
Drop portions which drop checkhost.c and checkhost.h.
4be148
4be148
commit 472349d2a47fbc7db82e46ba46411b95c312fc1f
4be148
Author: Greg Hudson <ghudson@mit.edu>
4be148
Date:   Sun Jun 22 10:42:14 2014 -0400
4be148
4be148
    Move KKDCP OpenSSL code to an internal plugin
4be148
    
4be148
    Create an internal pluggable interface "tls" with one in-tree dynamic
4be148
    plugin module named "k5tls".  Move all of the OpenSSL calls to the
4be148
    plugin module, and make the libkrb5 code load and invoke the plugin.
4be148
    This way we do not load or initialize libssl unless an HTTP proxy is
4be148
    used.
4be148
    
4be148
    ticket: 7929
4be148
4be148
diff --git a/src/Makefile.in b/src/Makefile.in
4be148
index 5e2cf4e..92bb60a 100644
4be148
--- a/src/Makefile.in
4be148
+++ b/src/Makefile.in
4be148
@@ -20,6 +20,7 @@ SUBDIRS=util include lib \
4be148
 	@ldap_plugin_dir@ \
4be148
 	plugins/preauth/otp \
4be148
 	plugins/preauth/pkinit \
4be148
+	plugins/tls/k5tls \
4be148
 	kdc kadmin slave clients appl tests \
4be148
 	config-files build-tools man doc @po@
4be148
 WINSUBDIRS=include util lib ccapi windows clients appl
4be148
@@ -62,7 +63,7 @@ INSTALLMKDIRS = $(KRB5ROOT) $(KRB5MANROOT) $(KRB5OTHERMKDIRS) \
4be148
 		$(KRB5_LIBDIR) $(KRB5_INCDIR) \
4be148
 		$(KRB5_DB_MODULE_DIR) $(KRB5_PA_MODULE_DIR) \
4be148
 		$(KRB5_AD_MODULE_DIR) \
4be148
-		$(KRB5_LIBKRB5_MODULE_DIR) \
4be148
+		$(KRB5_LIBKRB5_MODULE_DIR) $(KRB5_TLS_MODULE_DIR) \
4be148
 		@localstatedir@ @localstatedir@/krb5kdc \
4be148
 		$(KRB5_INCSUBDIRS) $(datadir) $(EXAMPLEDIR) \
4be148
 		$(PKGCONFIG_DIR)
4be148
diff --git a/src/config/pre.in b/src/config/pre.in
4be148
index e1d7e4b..fd8ee56 100644
4be148
--- a/src/config/pre.in
4be148
+++ b/src/config/pre.in
4be148
@@ -213,6 +213,7 @@ KRB5_DB_MODULE_DIR = $(MODULE_DIR)/kdb
4be148
 KRB5_PA_MODULE_DIR = $(MODULE_DIR)/preauth
4be148
 KRB5_AD_MODULE_DIR = $(MODULE_DIR)/authdata
4be148
 KRB5_LIBKRB5_MODULE_DIR = $(MODULE_DIR)/libkrb5
4be148
+KRB5_TLS_MODULE_DIR = $(MODULE_DIR)/tls
4be148
 KRB5_LOCALEDIR = @localedir@
4be148
 GSS_MODULE_DIR = @libdir@/gss
4be148
 KRB5_INCSUBDIRS = \
4be148
diff --git a/src/configure.in b/src/configure.in
4be148
index 8aa513e..43509ab 100644
4be148
--- a/src/configure.in
4be148
+++ b/src/configure.in
4be148
@@ -308,6 +308,11 @@ no)
4be148
   ;;
4be148
 esac
4be148
 
4be148
+if test "$PROXY_TLS_IMPL" = no; then
4be148
+   AC_DEFINE(PROXY_TLS_IMPL_NONE,1,
4be148
+             [Define if no HTTP TLS implementation is selected])
4be148
+fi
4be148
+
4be148
 AC_SUBST(PROXY_TLS_IMPL)
4be148
 AC_SUBST(PROXY_TLS_IMPL_CFLAGS)
4be148
 AC_SUBST(PROXY_TLS_IMPL_LIBS)
4be148
@@ -1386,6 +1391,7 @@ dnl	ccapi ccapi/lib ccapi/lib/unix ccapi/server ccapi/server/unix ccapi/test
4be148
 	plugins/authdata/greet
4be148
 	plugins/authdata/greet_client
4be148
 	plugins/authdata/greet_server
4be148
+	plugins/tls/k5tls
4be148
 
4be148
 	clients clients/klist clients/kinit clients/kvno
4be148
 	clients/kdestroy clients/kpasswd clients/ksu clients/kswitch
4be148
diff --git a/src/include/k5-int.h b/src/include/k5-int.h
4be148
index 9f14ee0..38846eb 100644
4be148
--- a/src/include/k5-int.h
4be148
+++ b/src/include/k5-int.h
4be148
@@ -1083,7 +1083,8 @@ struct plugin_interface {
4be148
 #define PLUGIN_INTERFACE_LOCALAUTH   5
4be148
 #define PLUGIN_INTERFACE_HOSTREALM   6
4be148
 #define PLUGIN_INTERFACE_AUDIT       7
4be148
-#define PLUGIN_NUM_INTERFACES        8
4be148
+#define PLUGIN_INTERFACE_TLS         8
4be148
+#define PLUGIN_NUM_INTERFACES        9
4be148
 
4be148
 /* Retrieve the plugin module of type interface_id and name modname,
4be148
  * storing the result into module. */
4be148
@@ -1126,6 +1127,7 @@ typedef struct krb5_preauth_context_st krb5_preauth_context;
4be148
 struct ccselect_module_handle;
4be148
 struct localauth_module_handle;
4be148
 struct hostrealm_module_handle;
4be148
+struct k5_tls_vtable_st;
4be148
 struct _krb5_context {
4be148
     krb5_magic      magic;
4be148
     krb5_enctype    *in_tkt_etypes;
4be148
@@ -1169,6 +1171,9 @@ struct _krb5_context {
4be148
     /* hostrealm module stuff */
4be148
     struct hostrealm_module_handle **hostrealm_handles;
4be148
 
4be148
+    /* TLS module vtable (if loaded) */
4be148
+    struct k5_tls_vtable_st *tls;
4be148
+
4be148
     /* error detail info */
4be148
     struct errinfo err;
4be148
 
4be148
diff --git a/src/include/k5-tls.h b/src/include/k5-tls.h
4be148
new file mode 100644
4be148
index 0000000..0661c05
4be148
--- /dev/null
4be148
+++ b/src/include/k5-tls.h
4be148
@@ -0,0 +1,104 @@
4be148
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
4be148
+/* include/k5-tls.h - internal pluggable interface for TLS */
4be148
+/*
4be148
+ * Copyright (C) 2014 by the Massachusetts Institute of Technology.
4be148
+ * 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
4be148
+ * are met:
4be148
+ *
4be148
+ * * Redistributions of source code must retain the above copyright
4be148
+ *   notice, this list of conditions and the following disclaimer.
4be148
+ *
4be148
+ * * 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
4be148
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
4be148
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
4be148
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
4be148
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
4be148
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
4be148
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
4be148
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
4be148
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
4be148
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
4be148
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
4be148
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
4be148
+ */
4be148
+
4be148
+/*
4be148
+ * This internal pluggable interface allows libkrb5 to load an in-tree module
4be148
+ * providing TLS support at runtime.  It is currently tailored for the needs of
4be148
+ * the OpenSSL module as used for HTTP proxy support.  As an internal
4be148
+ * interface, it can be changed to fit different implementations and consumers
4be148
+ * without regard for backward compatibility.
4be148
+ */
4be148
+
4be148
+#ifndef K5_TLS_H
4be148
+#define K5_TLS_H
4be148
+
4be148
+#include "k5-int.h"
4be148
+
4be148
+/* An abstract type for localauth module data. */
4be148
+typedef struct k5_tls_handle_st *k5_tls_handle;
4be148
+
4be148
+typedef enum {
4be148
+    DATA_READ, DONE, WANT_READ, WANT_WRITE, ERROR_TLS
4be148
+} k5_tls_status;
4be148
+
4be148
+/*
4be148
+ * Create a handle for fd, where the server certificate must match servername
4be148
+ * and be trusted according to anchors.  anchors is a null-terminated list
4be148
+ * using the DIR:/FILE:/ENV: syntax borrowed from PKINIT.  If anchors is null,
4be148
+ * use the system default trust anchors.
4be148
+ */
4be148
+typedef krb5_error_code
4be148
+(*k5_tls_setup_fn)(krb5_context context, SOCKET fd, const char *servername,
4be148
+                   char **anchors, k5_tls_handle *handle_out);
4be148
+
4be148
+/*
4be148
+ * Write len bytes of data using TLS.  Return DONE if writing is complete,
4be148
+ * WANT_READ or WANT_WRITE if the underlying socket must be readable or
4be148
+ * writable to continue, and ERROR_TLS if the TLS channel or underlying socket
4be148
+ * experienced an error.  After WANT_READ or WANT_WRITE, the operation will be
4be148
+ * retried with the same arguments even if some data has already been written.
4be148
+ * (OpenSSL makes this contract easy to fulfill.  For other implementations we
4be148
+ * might want to change it.)
4be148
+ */
4be148
+typedef k5_tls_status
4be148
+(*k5_tls_write_fn)(krb5_context context, k5_tls_handle handle,
4be148
+                   const void *data, size_t len);
4be148
+
4be148
+/*
4be148
+ * Read up to data_size bytes of data using TLS.  Return DATA_READ and set
4be148
+ * *len_out if any data is read.  Return DONE if there is no more data to be
4be148
+ * read on the connection, WANT_READ or WANT_WRITE if the underlying socket
4be148
+ * must be readable or writable to continue, and ERROR_TLS if the TLS channel
4be148
+ * or underlying socket experienced an error.
4be148
+ *
4be148
+ * After DATA_READ, there may still be pending buffered data to read.  The
4be148
+ * caller must call this method again with additional buffer space before
4be148
+ * selecting for reading on the underlying socket.
4be148
+ */
4be148
+typedef k5_tls_status
4be148
+(*k5_tls_read_fn)(krb5_context context, k5_tls_handle handle, void *data,
4be148
+                  size_t data_size, size_t *len_out);
4be148
+
4be148
+/* Release a handle.  Do not pass a null pointer. */
4be148
+typedef void
4be148
+(*k5_tls_free_handle_fn)(krb5_context context, k5_tls_handle handle);
4be148
+
4be148
+/* All functions are mandatory unless they are all null, in which case the
4be148
+ * caller should assume that TLS is unsupported. */
4be148
+typedef struct k5_tls_vtable_st {
4be148
+    k5_tls_setup_fn setup;
4be148
+    k5_tls_write_fn write;
4be148
+    k5_tls_read_fn read;
4be148
+    k5_tls_free_handle_fn free_handle;
4be148
+} *k5_tls_vtable;
4be148
+
4be148
+#endif /* K5_TLS_H */
4be148
diff --git a/src/include/k5-trace.h b/src/include/k5-trace.h
4be148
index 9e75b29..a0aa85a 100644
4be148
--- a/src/include/k5-trace.h
4be148
+++ b/src/include/k5-trace.h
4be148
@@ -324,23 +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
-                                                       namelen, name,   \
4be148
-                                                       err, errs)       \
4be148
-    TRACE(c, "HTTPS certificate error at {int} ({lenstr}): "            \
4be148
-          "{int} ({str})", depth, namelen, name, err, errs)
4be148
-#define TRACE_SENDTO_KDC_HTTPS_ERROR_CONNECT(c, raddr)                  \
4be148
+#define TRACE_SENDTO_KDC_HTTPS_ERROR_CONNECT(c, raddr)          \
4be148
     TRACE(c, "HTTPS error connecting to {raddr}", raddr)
4be148
-#define TRACE_SENDTO_KDC_HTTPS_ERROR_RECV(c, raddr, err)                \
4be148
-    TRACE(c, "HTTPS error receiving from {raddr}: {errno}", raddr, err)
4be148
-#define TRACE_SENDTO_KDC_HTTPS_ERROR_SEND(c, raddr)                     \
4be148
+#define TRACE_SENDTO_KDC_HTTPS_ERROR_RECV(c, raddr)             \
4be148
+    TRACE(c, "HTTPS error receiving from {raddr}", raddr)
4be148
+#define TRACE_SENDTO_KDC_HTTPS_ERROR_SEND(c, raddr)     \
4be148
     TRACE(c, "HTTPS error sending to {raddr}", raddr)
4be148
 #define TRACE_SENDTO_KDC_HTTPS_SEND(c, raddr)                           \
4be148
     TRACE(c, "Sending HTTPS request to {raddr}", raddr)
4be148
@@ -383,6 +371,19 @@ void krb5int_trace(krb5_context context, const char *fmt, ...);
4be148
     TRACE(c, "TGS reply didn't decode with subkey; trying session key " \
4be148
           "({keyblock)}", keyblock)
4be148
 
4be148
+#define TRACE_TLS_ERROR(c, errs)                \
4be148
+    TRACE(c, "TLS error: {str}", errs)
4be148
+#define TRACE_TLS_NO_REMOTE_CERTIFICATE(c)              \
4be148
+    TRACE(c, "TLS server certificate not received")
4be148
+#define TRACE_TLS_CERT_ERROR(c, depth, namelen, name, err, errs)        \
4be148
+    TRACE(c, "TLS certificate error at {int} ({lenstr}): {int} ({str})", \
4be148
+          depth, namelen, name, err, errs)
4be148
+#define TRACE_TLS_SERVER_NAME_MISMATCH(c, hostname)                     \
4be148
+    TRACE(c, "TLS certificate name mismatch: server certificate is "    \
4be148
+          "not for \"{str}\"", hostname)
4be148
+#define TRACE_TLS_SERVER_NAME_MATCH(c, hostname)                        \
4be148
+    TRACE(c, "TLS certificate name matched \"{str}\"", hostname)
4be148
+
4be148
 #define TRACE_TKT_CREDS(c, creds, cache)                            \
4be148
     TRACE(c, "Getting credentials {creds} using ccache {ccache}",   \
4be148
           creds, cache)
4be148
diff --git a/src/lib/krb5/Makefile.in b/src/lib/krb5/Makefile.in
4be148
index 472c008..d9cddc1 100644
4be148
--- a/src/lib/krb5/Makefile.in
4be148
+++ b/src/lib/krb5/Makefile.in
4be148
@@ -56,8 +56,7 @@ RELDIR=krb5
4be148
 SHLIB_EXPDEPS = \
4be148
 	$(TOPLIBD)/libk5crypto$(SHLIBEXT) \
4be148
 	$(COM_ERR_DEPLIB) $(SUPPORT_DEPLIB)
4be148
-SHLIB_EXPLIBS=-lk5crypto -lcom_err $(PROXY_TLS_IMPL_LIBS) $(SUPPORT_LIB) \
4be148
-	@GEN_LIB@ $(LIBS)
4be148
+SHLIB_EXPLIBS=-lk5crypto -lcom_err $(SUPPORT_LIB) @GEN_LIB@ $(LIBS)
4be148
 
4be148
 all-unix:: all-liblinks
4be148
 
4be148
diff --git a/src/lib/krb5/krb/copy_ctx.c b/src/lib/krb5/krb/copy_ctx.c
4be148
index 4237023..322c288 100644
4be148
--- a/src/lib/krb5/krb/copy_ctx.c
4be148
+++ b/src/lib/krb5/krb/copy_ctx.c
4be148
@@ -81,6 +81,7 @@ krb5_copy_context(krb5_context ctx, krb5_context *nctx_out)
4be148
     nctx->ccselect_handles = NULL;
4be148
     nctx->localauth_handles = NULL;
4be148
     nctx->hostrealm_handles = NULL;
4be148
+    nctx->tls = NULL;
4be148
     nctx->kdblog_context = NULL;
4be148
     nctx->trace_callback = NULL;
4be148
     nctx->trace_callback_data = NULL;
4be148
diff --git a/src/lib/krb5/krb/init_ctx.c b/src/lib/krb5/krb/init_ctx.c
4be148
index 6801bb1..6548f36 100644
4be148
--- a/src/lib/krb5/krb/init_ctx.c
4be148
+++ b/src/lib/krb5/krb/init_ctx.c
4be148
@@ -319,6 +319,7 @@ krb5_free_context(krb5_context ctx)
4be148
     k5_localauth_free_context(ctx);
4be148
     k5_plugin_free_context(ctx);
4be148
     free(ctx->plugin_base_dir);
4be148
+    free(ctx->tls);
4be148
 
4be148
     ctx->magic = 0;
4be148
     free(ctx);
4be148
diff --git a/src/lib/krb5/krb/plugin.c b/src/lib/krb5/krb/plugin.c
4be148
index 8b62c7b..7375f51 100644
4be148
--- a/src/lib/krb5/krb/plugin.c
4be148
+++ b/src/lib/krb5/krb/plugin.c
4be148
@@ -55,7 +55,8 @@ const char *interface_names[] = {
4be148
     "ccselect",
4be148
     "localauth",
4be148
     "hostrealm",
4be148
-    "audit"
4be148
+    "audit",
4be148
+    "tls"
4be148
 };
4be148
 
4be148
 /* Return the context's interface structure for id, or NULL if invalid. */
4be148
diff --git a/src/lib/krb5/krb5_libinit.c b/src/lib/krb5/krb5_libinit.c
4be148
index b72bc58..eb40124 100644
4be148
--- a/src/lib/krb5/krb5_libinit.c
4be148
+++ b/src/lib/krb5/krb5_libinit.c
4be148
@@ -55,8 +55,6 @@ int krb5int_lib_init(void)
4be148
     if (err)
4be148
         return err;
4be148
 
4be148
-    k5_sendto_kdc_initialize();
4be148
-
4be148
     return 0;
4be148
 }
4be148
 
4be148
diff --git a/src/lib/krb5/os/Makefile.in b/src/lib/krb5/os/Makefile.in
4be148
index fa8a093..ea68990 100644
4be148
--- a/src/lib/krb5/os/Makefile.in
4be148
+++ b/src/lib/krb5/os/Makefile.in
4be148
@@ -2,7 +2,7 @@ mydir=lib$(S)krb5$(S)os
4be148
 BUILDTOP=$(REL)..$(S)..$(S)..
4be148
 DEFINES=-DLIBDIR=\"$(KRB5_LIBDIR)\" -DBINDIR=\"$(CLIENT_BINDIR)\" \
4be148
 	-DSBINDIR=\"$(ADMIN_BINDIR)\"
4be148
-LOCALINCLUDES= $(PROXY_TLS_IMPL_CFLAGS) -I$(top_srcdir)/util/profile
4be148
+LOCALINCLUDES= -I$(top_srcdir)/util/profile
4be148
 
4be148
 ##DOS##BUILDTOP = ..\..\..
4be148
 ##DOS##PREFIXDIR=os
4be148
@@ -13,7 +13,6 @@ 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
diff --git a/src/lib/krb5/os/deps b/src/lib/krb5/os/deps
4be148
index d56ff30..211354c 100644
4be148
--- a/src/lib/krb5/os/deps
4be148
+++ b/src/lib/krb5/os/deps
4be148
@@ -49,17 +49,6 @@ 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
@@ -429,7 +418,7 @@ 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 checkhost.h os-proto.h \
4be148
+  $(top_srcdir)/include/socket-utils.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
diff --git a/src/lib/krb5/os/locate_kdc.c b/src/lib/krb5/os/locate_kdc.c
4be148
index 1f2039c..160a2d0 100644
4be148
--- a/src/lib/krb5/os/locate_kdc.c
4be148
+++ b/src/lib/krb5/os/locate_kdc.c
4be148
@@ -177,7 +177,6 @@ oom:
4be148
     return ENOMEM;
4be148
 }
4be148
 
4be148
-#ifdef PROXY_TLS_IMPL_OPENSSL
4be148
 static void
4be148
 parse_uri_if_https(char *host_or_uri, k5_transport *transport, char **host,
4be148
                    char **uri_path)
4be148
@@ -195,13 +194,6 @@ parse_uri_if_https(char *host_or_uri, k5_transport *transport, char **host,
4be148
         }
4be148
     }
4be148
 }
4be148
-#else
4be148
-static void
4be148
-parse_uri_if_https(char *host_or_uri, k5_transport *transport, char **host,
4be148
-                   char **uri)
4be148
-{
4be148
-}
4be148
-#endif
4be148
 
4be148
 /* Return true if server is identical to an entry in list. */
4be148
 static krb5_boolean
4be148
diff --git a/src/lib/krb5/os/os-proto.h b/src/lib/krb5/os/os-proto.h
4be148
index 34bf028..69ee376 100644
4be148
--- a/src/lib/krb5/os/os-proto.h
4be148
+++ b/src/lib/krb5/os/os-proto.h
4be148
@@ -187,6 +187,5 @@ krb5_error_code localauth_k5login_initvt(krb5_context context, int maj_ver,
4be148
                                          krb5_plugin_vtable vtable);
4be148
 krb5_error_code localauth_an2ln_initvt(krb5_context context, int maj_ver,
4be148
                                        int min_ver, krb5_plugin_vtable vtable);
4be148
-void k5_sendto_kdc_initialize(void);
4be148
 
4be148
 #endif /* KRB5_LIBOS_INT_PROTO__ */
4be148
diff --git a/src/lib/krb5/os/sendto_kdc.c b/src/lib/krb5/os/sendto_kdc.c
4be148
index a572831..2242240 100644
4be148
--- a/src/lib/krb5/os/sendto_kdc.c
4be148
+++ b/src/lib/krb5/os/sendto_kdc.c
4be148
@@ -54,6 +54,7 @@
4be148
  * as necessary. */
4be148
 
4be148
 #include "fake-addrinfo.h"
4be148
+#include "k5-tls.h"
4be148
 #include "k5-int.h"
4be148
 
4be148
 #include "os-proto.h"
4be148
@@ -74,15 +75,6 @@
4be148
 #endif
4be148
 #endif
4be148
 
4be148
-#ifdef PROXY_TLS_IMPL_OPENSSL
4be148
-#include <openssl/err.h>
4be148
-#include <openssl/ssl.h>
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
 #define DEFAULT_UDP_PREF_LIMIT   1465
4be148
 #define HARD_UDP_LIMIT          32700 /* could probably do 64K-epsilon ? */
4be148
@@ -147,29 +139,30 @@ struct conn_state {
4be148
         const char *uri_path;
4be148
         const char *servername;
4be148
         char *https_request;
4be148
-#ifdef PROXY_TLS_IMPL_OPENSSL
4be148
-        SSL *ssl;
4be148
-#endif
4be148
+        k5_tls_handle tls;
4be148
     } http;
4be148
 };
4be148
 
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
-k5_sendto_kdc_initialize(void)
4be148
+/* Set up context->tls.  On allocation failure, return ENOMEM.  On plugin load
4be148
+ * failure, set context->tls to point to a nulled vtable and return 0. */
4be148
+static krb5_error_code
4be148
+init_tls_vtable(krb5_context context)
4be148
 {
4be148
-#ifdef PROXY_TLS_IMPL_OPENSSL
4be148
-    SSL_library_init();
4be148
-    SSL_load_error_strings();
4be148
-    OpenSSL_add_all_algorithms();
4be148
+    krb5_plugin_initvt_fn initfn;
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
+    if (context->tls != NULL)
4be148
+        return 0;
4be148
+
4be148
+    context->tls = calloc(1, sizeof(*context->tls));
4be148
+    if (context->tls == NULL)
4be148
+        return ENOMEM;
4be148
+
4be148
+    /* Attempt to load the module; just let it stay nulled out on failure. */
4be148
+    k5_plugin_register_dyn(context, PLUGIN_INTERFACE_TLS, "k5tls", "tls");
4be148
+    if (k5_plugin_load(context, PLUGIN_INTERFACE_TLS, "k5tls", &initfn) == 0)
4be148
+        (*initfn)(context, 0, 0, (krb5_plugin_vtable)context->tls);
4be148
+
4be148
+    return 0;
4be148
 }
4be148
 
4be148
 /* Get current time in milliseconds. */
4be148
@@ -184,21 +177,15 @@ get_curtime_ms(time_ms *time_out)
4be148
     return 0;
4be148
 }
4be148
 
4be148
-#ifdef PROXY_TLS_IMPL_OPENSSL
4be148
 static void
4be148
-free_http_ssl_data(struct conn_state *state)
4be148
+free_http_tls_data(krb5_context context, struct conn_state *state)
4be148
 {
4be148
-    SSL_free(state->http.ssl);
4be148
-    state->http.ssl = NULL;
4be148
+    if (state->http.tls != NULL)
4be148
+        context->tls->free_handle(context, state->http.tls);
4be148
+    state->http.tls = NULL;
4be148
     free(state->http.https_request);
4be148
     state->http.https_request = NULL;
4be148
 }
4be148
-#else
4be148
-static void
4be148
-free_http_ssl_data(struct conn_state *state)
4be148
-{
4be148
-}
4be148
-#endif
4be148
 
4be148
 #ifdef USE_POLL
4be148
 
4be148
@@ -532,7 +519,6 @@ static fd_handler_fn service_udp_read;
4be148
 static fd_handler_fn service_https_write;
4be148
 static fd_handler_fn service_https_read;
4be148
 
4be148
-#ifdef PROXY_TLS_IMPL_OPENSSL
4be148
 static krb5_error_code
4be148
 make_proxy_request(struct conn_state *state, const krb5_data *realm,
4be148
                    const krb5_data *message, char **req_out, size_t *len_out)
4be148
@@ -585,14 +571,6 @@ cleanup:
4be148
     krb5_free_data(NULL, encoded_pm);
4be148
     return ret;
4be148
 }
4be148
-#else
4be148
-static krb5_error_code
4be148
-make_proxy_request(struct conn_state *state, const krb5_data *realm,
4be148
-                   const krb5_data *message, char **req_out, size_t *len_out)
4be148
-{
4be148
-    abort();
4be148
-}
4be148
-#endif
4be148
 
4be148
 /* Set up the actual message we will send across the underlying transport to
4be148
  * communicate the payload message, using one or both of state->out.sgbuf. */
4be148
@@ -963,7 +941,7 @@ static void
4be148
 kill_conn(krb5_context context, struct conn_state *conn,
4be148
           struct select_state *selstate)
4be148
 {
4be148
-    free_http_ssl_data(conn);
4be148
+    free_http_tls_data(context, conn);
4be148
 
4be148
     if (socktype_for_transport(conn->addr.transport) == SOCK_STREAM)
4be148
         TRACE_SENDTO_KDC_TCP_DISCONNECT(context, &conn->addr);
4be148
@@ -1145,249 +1123,44 @@ service_udp_read(krb5_context context, const krb5_data *realm,
4be148
     return TRUE;
4be148
 }
4be148
 
4be148
-#ifdef PROXY_TLS_IMPL_OPENSSL
4be148
-/* Output any error strings that OpenSSL's accumulated as tracing messages. */
4be148
-static void
4be148
-flush_ssl_errors(krb5_context context)
4be148
-{
4be148
-    unsigned long err;
4be148
-    char buf[128];
4be148
-
4be148
-    while ((err = ERR_get_error()) != 0) {
4be148
-        ERR_error_string_n(err, buf, sizeof(buf));
4be148
-        TRACE_SENDTO_KDC_HTTPS_ERROR(context, buf);
4be148
-    }
4be148
-}
4be148
-
4be148
-static krb5_error_code
4be148
-load_http_anchor_file(X509_STORE *store, const char *path)
4be148
-{
4be148
-    FILE *fp;
4be148
-    STACK_OF(X509_INFO) *sk = NULL;
4be148
-    X509_INFO *xi;
4be148
-    int i;
4be148
-
4be148
-    fp = fopen(path, "r");
4be148
-    if (fp == NULL)
4be148
-        return errno;
4be148
-    sk = PEM_X509_INFO_read(fp, NULL, NULL, NULL);
4be148
-    fclose(fp);
4be148
-    if (sk == NULL)
4be148
-        return ENOENT;
4be148
-    for (i = 0; i < sk_X509_INFO_num(sk); i++) {
4be148
-        xi = sk_X509_INFO_value(sk, i);
4be148
-        if (xi->x509 != NULL)
4be148
-            X509_STORE_add_cert(store, xi->x509);
4be148
-    }
4be148
-    sk_X509_INFO_pop_free(sk, X509_INFO_free);
4be148
-    return 0;
4be148
-}
4be148
-
4be148
-static krb5_error_code
4be148
-load_http_anchor_dir(X509_STORE *store, const char *path)
4be148
-{
4be148
-    DIR *d = NULL;
4be148
-    struct dirent *dentry = NULL;
4be148
-    char filename[1024];
4be148
-    krb5_boolean found_any = FALSE;
4be148
-
4be148
-    d = opendir(path);
4be148
-    if (d == NULL)
4be148
-        return ENOENT;
4be148
-    while ((dentry = readdir(d)) != NULL) {
4be148
-        if (dentry->d_name[0] != '.') {
4be148
-            snprintf(filename, sizeof(filename), "%s/%s",
4be148
-                     path, dentry->d_name);
4be148
-            if (load_http_anchor_file(store, filename) == 0)
4be148
-                found_any = TRUE;
4be148
-        }
4be148
-    }
4be148
-    closedir(d);
4be148
-    return found_any ? 0 : ENOENT;
4be148
-}
4be148
-
4be148
-static krb5_error_code
4be148
-load_http_anchor(SSL_CTX *ctx, const char *location)
4be148
+/* Set up conn->http.tls.  Return true on success. */
4be148
+static krb5_boolean
4be148
+setup_tls(krb5_context context, const krb5_data *realm,
4be148
+          struct conn_state *conn, struct select_state *selstate)
4be148
 {
4be148
-    X509_STORE *store;
4be148
-    const char *envloc;
4be148
-
4be148
-    store = SSL_CTX_get_cert_store(ctx);
4be148
-    if (strncmp(location, "FILE:", 5) == 0) {
4be148
-        return load_http_anchor_file(store, location + 5);
4be148
-    } else if (strncmp(location, "DIR:", 4) == 0) {
4be148
-        return load_http_anchor_dir(store, location + 4);
4be148
-    } else if (strncmp(location, "ENV:", 4) == 0) {
4be148
-        envloc = getenv(location + 4);
4be148
-        if (envloc == NULL)
4be148
-            return ENOENT;
4be148
-        return load_http_anchor(ctx, envloc);
4be148
-    }
4be148
-    return EINVAL;
4be148
-}
4be148
+    krb5_error_code ret;
4be148
+    krb5_boolean ok = FALSE;
4be148
+    char **anchors = NULL, *realmstr = NULL;
4be148
+    const char *names[4];
4be148
 
4be148
-static krb5_error_code
4be148
-load_http_verify_anchors(krb5_context context, const krb5_data *realm,
4be148
-                         SSL_CTX *sctx)
4be148
-{
4be148
-    const char *anchors[4];
4be148
-    char **values = NULL, *realmz;
4be148
-    unsigned int i;
4be148
-    krb5_error_code err;
4be148
+    if (init_tls_vtable(context) != 0 || context->tls->setup == NULL)
4be148
+        return FALSE;
4be148
 
4be148
-    realmz = k5memdup0(realm->data, realm->length, &err;;
4be148
-    if (realmz == NULL)
4be148
+    realmstr = k5memdup0(realm->data, realm->length, &ret;;
4be148
+    if (realmstr == NULL)
4be148
         goto cleanup;
4be148
 
4be148
     /* Load the configured anchors. */
4be148
-    anchors[0] = KRB5_CONF_REALMS;
4be148
-    anchors[1] = realmz;
4be148
-    anchors[2] = KRB5_CONF_HTTP_ANCHORS;
4be148
-    anchors[3] = NULL;
4be148
-    if (profile_get_values(context->profile, anchors, &values) == 0) {
4be148
-        for (i = 0; values[i] != NULL; i++) {
4be148
-            err = load_http_anchor(sctx, values[i]);
4be148
-            if (err != 0)
4be148
-                break;
4be148
-        }
4be148
-        profile_free_list(values);
4be148
-    } else {
4be148
-        /* Use the library defaults. */
4be148
-        if (SSL_CTX_set_default_verify_paths(sctx) != 1)
4be148
-            err = ENOENT;
4be148
-    }
4be148
-
4be148
-cleanup:
4be148
-    free(realmz);
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
+    names[0] = KRB5_CONF_REALMS;
4be148
+    names[1] = realmstr;
4be148
+    names[2] = KRB5_CONF_HTTP_ANCHORS;
4be148
+    names[3] = NULL;
4be148
+    ret = profile_get_values(context->profile, names, &anchors);
4be148
+    if (ret != 0 && ret != PROF_NO_RELATION)
4be148
+        goto cleanup;
4be148
 
4be148
-static int
4be148
-ssl_verify_callback(int preverify_ok, X509_STORE_CTX *store_ctx)
4be148
-{
4be148
-    X509 *x;
4be148
-    SSL *ssl;
4be148
-    BIO *bio;
4be148
-    krb5_context context;
4be148
-    int err, depth;
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
-        TRACE_SENDTO_KDC_HTTPS_NO_REMOTE_CERTIFICATE(context);
4be148
-        return 0;
4be148
-    }
4be148
-    /* Figure out where we are. */
4be148
-    depth = X509_STORE_CTX_get_error_depth(store_ctx);
4be148
-    if (depth < 0)
4be148
-        return 0;
4be148
-    /* If there's an error at this level that we're not ignoring, fail. */
4be148
-    err = X509_STORE_CTX_get_error(store_ctx);
4be148
-    if (err != X509_V_OK) {
4be148
-        bio = BIO_new(BIO_s_mem());
4be148
-        if (bio != NULL) {
4be148
-            X509_NAME_print_ex(bio, x->cert_info->subject, 0, 0);
4be148
-            count = BIO_get_mem_data(bio, &cert);
4be148
-            errstr = X509_verify_cert_error_string(err);
4be148
-            TRACE_SENDTO_KDC_HTTPS_PROXY_CERTIFICATE_ERROR(context, depth,
4be148
-                                                           count, cert, err,
4be148
-                                                           errstr);
4be148
-            BIO_free(bio);
4be148
-        }
4be148
-        return 0;
4be148
-    }
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
+    if (context->tls->setup(context, conn->fd, conn->http.servername, anchors,
4be148
+                            &conn->http.tls) != 0) {
4be148
+        TRACE_SENDTO_KDC_HTTPS_ERROR_CONNECT(context, &conn->addr);
4be148
+        goto cleanup;
4be148
     }
4be148
-    /* The name didn't match. */
4be148
-    return 0;
4be148
-}
4be148
 
4be148
-/*
4be148
- * Set up structures that we use to manage the SSL handling for this connection
4be148
- * and apply any non-default settings.  Kill the connection and return false if
4be148
- * anything goes wrong while we're doing that; return true otherwise.
4be148
- */
4be148
-static krb5_boolean
4be148
-setup_ssl(krb5_context context, const krb5_data *realm,
4be148
-          struct conn_state *conn, struct select_state *selstate)
4be148
-{
4be148
-    int e;
4be148
-    long options;
4be148
-    SSL_CTX *ctx = NULL;
4be148
-    SSL *ssl = NULL;
4be148
+    ok = TRUE;
4be148
 
4be148
-    if (ssl_ex_context_id == -1 || ssl_ex_conn_id == -1)
4be148
-        goto kill_conn;
4be148
-
4be148
-    /* Do general SSL library setup. */
4be148
-    ctx = SSL_CTX_new(SSLv23_client_method());
4be148
-    if (ctx == NULL)
4be148
-        goto kill_conn;
4be148
-    options = SSL_CTX_get_options(ctx);
4be148
-    SSL_CTX_set_options(ctx, options | SSL_OP_NO_SSLv2);
4be148
-
4be148
-    SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, ssl_verify_callback);
4be148
-    X509_STORE_set_flags(SSL_CTX_get_cert_store(ctx), 0);
4be148
-    e = load_http_verify_anchors(context, realm, ctx);
4be148
-    if (e != 0)
4be148
-        goto kill_conn;
4be148
-
4be148
-    ssl = SSL_new(ctx);
4be148
-    if (ssl == NULL)
4be148
-        goto kill_conn;
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
-        goto kill_conn;
4be148
-    SSL_set_connect_state(ssl);
4be148
-
4be148
-    SSL_CTX_free(ctx);
4be148
-    conn->http.ssl = ssl;
4be148
-
4be148
-    return TRUE;
4be148
-
4be148
-kill_conn:
4be148
-    TRACE_SENDTO_KDC_HTTPS_ERROR_CONNECT(context, &conn->addr);
4be148
-    flush_ssl_errors(context);
4be148
-    SSL_free(ssl);
4be148
-    SSL_CTX_free(ctx);
4be148
-    kill_conn(context, conn, selstate);
4be148
-    return FALSE;
4be148
+cleanup:
4be148
+    free(realmstr);
4be148
+    profile_free_list(anchors);
4be148
+    return ok;
4be148
 }
4be148
 
4be148
 /* Set conn->state to READING when done; otherwise, call a cm_set_. */
4be148
@@ -1395,50 +1168,41 @@ static krb5_boolean
4be148
 service_https_write(krb5_context context, const krb5_data *realm,
4be148
                     struct conn_state *conn, struct select_state *selstate)
4be148
 {
4be148
-    ssize_t nwritten;
4be148
-    int e;
4be148
+    k5_tls_status st;
4be148
 
4be148
     /* If this is our first time in here, set up the SSL context. */
4be148
-    if (conn->http.ssl == NULL && !setup_ssl(context, realm, conn, selstate))
4be148
+    if (conn->http.tls == NULL && !setup_tls(context, realm, conn, selstate)) {
4be148
+        kill_conn(context, conn, selstate);
4be148
         return FALSE;
4be148
+    }
4be148
 
4be148
     /* Try to transmit our request to the server. */
4be148
-    nwritten = SSL_write(conn->http.ssl, SG_BUF(conn->out.sgp),
4be148
-                         SG_LEN(conn->out.sgbuf));
4be148
-    if (nwritten <= 0) {
4be148
-        e = SSL_get_error(conn->http.ssl, nwritten);
4be148
-        if (e == SSL_ERROR_WANT_READ) {
4be148
-            cm_read(selstate, conn->fd);
4be148
-            return FALSE;
4be148
-        } else if (e == SSL_ERROR_WANT_WRITE) {
4be148
-            cm_write(selstate, conn->fd);
4be148
-            return FALSE;
4be148
-        }
4be148
+    st = context->tls->write(context, conn->http.tls, SG_BUF(conn->out.sgp),
4be148
+                             SG_LEN(conn->out.sgbuf));
4be148
+    if (st == DONE) {
4be148
+        TRACE_SENDTO_KDC_HTTPS_SEND(context, &conn->addr);
4be148
+        cm_read(selstate, conn->fd);
4be148
+        conn->state = READING;
4be148
+    } else if (st == WANT_READ) {
4be148
+        cm_read(selstate, conn->fd);
4be148
+    } else if (st == WANT_WRITE) {
4be148
+        cm_write(selstate, conn->fd);
4be148
+    } else if (st == ERROR_TLS) {
4be148
         TRACE_SENDTO_KDC_HTTPS_ERROR_SEND(context, &conn->addr);
4be148
-        flush_ssl_errors(context);
4be148
         kill_conn(context, conn, selstate);
4be148
-        return FALSE;
4be148
     }
4be148
 
4be148
-    /* Done writing, switch to reading. */
4be148
-    TRACE_SENDTO_KDC_HTTPS_SEND(context, &conn->addr);
4be148
-    cm_read(selstate, conn->fd);
4be148
-    conn->state = READING;
4be148
     return FALSE;
4be148
 }
4be148
 
4be148
-/*
4be148
- * Return true on readable data, call a cm_read/write function and return
4be148
- * false if the SSL layer needs it, kill the connection otherwise.
4be148
- */
4be148
+/* Return true on finished data.  Call a cm_read/write function and return
4be148
+ * false if the TLS layer needs it.  Kill the connection on error. */
4be148
 static krb5_boolean
4be148
 https_read_bytes(krb5_context context, struct conn_state *conn,
4be148
                  struct select_state *selstate)
4be148
 {
4be148
-    size_t bufsize;
4be148
-    ssize_t nread;
4be148
-    krb5_boolean readbytes = FALSE;
4be148
-    int e = 0;
4be148
+    size_t bufsize, nread;
4be148
+    k5_tls_status st;
4be148
     char *tmp;
4be148
     struct incoming_message *in = &conn->in;
4be148
 
4be148
@@ -1458,31 +1222,26 @@ https_read_bytes(krb5_context context, struct conn_state *conn,
4be148
             in->bufsize = bufsize;
4be148
         }
4be148
 
4be148
-        nread = SSL_read(conn->http.ssl, &in->buf[in->pos],
4be148
-                         in->bufsize - in->pos - 1);
4be148
-        if (nread <= 0)
4be148
+        st = context->tls->read(context, conn->http.tls, &in->buf[in->pos],
4be148
+                                in->bufsize - in->pos - 1, &nread);
4be148
+        if (st != DATA_READ)
4be148
             break;
4be148
+
4be148
         in->pos += nread;
4be148
         in->buf[in->pos] = '\0';
4be148
-        readbytes = TRUE;
4be148
     }
4be148
 
4be148
-    e = SSL_get_error(conn->http.ssl, nread);
4be148
-    if (e == SSL_ERROR_WANT_READ) {
4be148
+    if (st == DONE)
4be148
+        return TRUE;
4be148
+
4be148
+    if (st == WANT_READ) {
4be148
         cm_read(selstate, conn->fd);
4be148
-        return FALSE;
4be148
-    } else if (e == SSL_ERROR_WANT_WRITE) {
4be148
+    } else if (st == WANT_WRITE) {
4be148
         cm_write(selstate, conn->fd);
4be148
-        return FALSE;
4be148
-    } else if ((e == SSL_ERROR_ZERO_RETURN) ||
4be148
-               (e == SSL_ERROR_SYSCALL && nread == 0 && readbytes)) {
4be148
-        return TRUE;
4be148
+    } else if (st == ERROR_TLS) {
4be148
+        TRACE_SENDTO_KDC_HTTPS_ERROR_RECV(context, &conn->addr);
4be148
+        kill_conn(context, conn, selstate);
4be148
     }
4be148
-
4be148
-    e = readbytes ? SOCKET_ERRNO : ECONNRESET;
4be148
-    TRACE_SENDTO_KDC_HTTPS_ERROR_RECV(context, &conn->addr, e);
4be148
-    flush_ssl_errors(context);
4be148
-    kill_conn(context, conn, selstate);
4be148
     return FALSE;
4be148
 }
4be148
 
4be148
@@ -1531,20 +1290,6 @@ kill_conn:
4be148
     kill_conn(context, conn, selstate);
4be148
     return FALSE;
4be148
 }
4be148
-#else
4be148
-static krb5_boolean
4be148
-service_https_write(krb5_context context, const krb5_data *realm,
4be148
-                    struct conn_state *conn, struct select_state *selstate)
4be148
-{
4be148
-    abort();
4be148
-}
4be148
-static krb5_boolean
4be148
-service_https_read(krb5_context context, const krb5_data *realm,
4be148
-                   struct conn_state *conn, struct select_state *selstate)
4be148
-{
4be148
-    abort();
4be148
-}
4be148
-#endif
4be148
 
4be148
 /* Return the maximum of endtime and the endtime fields of all currently active
4be148
  * TCP connections. */
4be148
@@ -1765,7 +1510,7 @@ cleanup:
4be148
             if (socktype_for_transport(state->addr.transport) == SOCK_STREAM)
4be148
                 TRACE_SENDTO_KDC_TCP_DISCONNECT(context, &state->addr);
4be148
             closesocket(state->fd);
4be148
-            free_http_ssl_data(state);
4be148
+            free_http_tls_data(context, state);
4be148
         }
4be148
         if (state->state == READING && state->in.buf != udpbuf)
4be148
             free(state->in.buf);
4be148
diff --git a/src/plugins/tls/k5tls/Makefile.in b/src/plugins/tls/k5tls/Makefile.in
4be148
new file mode 100644
4be148
index 0000000..4d58df0
4be148
--- /dev/null
4be148
+++ b/src/plugins/tls/k5tls/Makefile.in
4be148
@@ -0,0 +1,22 @@
4be148
+mydir=plugins$(S)tls$(S)k5tls
4be148
+BUILDTOP=$(REL)..$(S)..$(S)..
4be148
+MODULE_INSTALL_DIR = $(KRB5_TLS_MODULE_DIR)
4be148
+LOCALINCLUDES= $(PROXY_TLS_IMPL_CFLAGS)
4be148
+
4be148
+LIBBASE=k5tls
4be148
+LIBMAJOR=0
4be148
+LIBMINOR=0
4be148
+RELDIR=../plugins/tls/k5tls
4be148
+SHLIB_EXPDEPS= $(KRB5_DEPLIB) $(SUPPORT_DEPLIB)
4be148
+SHLIB_EXPLIBS= $(KRB5_LIB) $(SUPPORT_LIB) $(PROXY_TLS_IMPL_LIBS)
4be148
+
4be148
+STLIBOBJS=openssl.o notls.o
4be148
+
4be148
+SRCS=$(srcdir)/openssl.c $(srcdir)/notls.c
4be148
+
4be148
+all-unix:: all-liblinks
4be148
+install-unix:: install-libs
4be148
+clean-unix:: clean-libs clean-libobjs
4be148
+
4be148
+@libnover_frag@
4be148
+@libobj_frag@
4be148
diff --git a/src/plugins/tls/k5tls/deps b/src/plugins/tls/k5tls/deps
4be148
new file mode 100644
4be148
index 0000000..a6088a7
4be148
--- /dev/null
4be148
+++ b/src/plugins/tls/k5tls/deps
4be148
@@ -0,0 +1,25 @@
4be148
+#
4be148
+# Generated makefile dependencies follow.
4be148
+#
4be148
+openssl.so openssl.po $(OUTPRE)openssl.$(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
+  $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
4be148
+  $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
4be148
+  $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
4be148
+  $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-tls.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 openssl.c
4be148
+notls.so notls.po $(OUTPRE)notls.$(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
+  $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
4be148
+  $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
4be148
+  $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
4be148
+  $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-tls.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 notls.c
4be148
diff --git a/src/plugins/tls/k5tls/k5tls.exports b/src/plugins/tls/k5tls/k5tls.exports
4be148
new file mode 100644
4be148
index 0000000..d67d928
4be148
--- /dev/null
4be148
+++ b/src/plugins/tls/k5tls/k5tls.exports
4be148
@@ -0,0 +1 @@
4be148
+tls_k5tls_initvt
4be148
diff --git a/src/plugins/tls/k5tls/notls.c b/src/plugins/tls/k5tls/notls.c
4be148
new file mode 100644
4be148
index 0000000..7be0a4a
4be148
--- /dev/null
4be148
+++ b/src/plugins/tls/k5tls/notls.c
4be148
@@ -0,0 +1,53 @@
4be148
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
4be148
+/* plus/tls/k5tls/none.c - Stub TLS module implementation */
4be148
+/*
4be148
+ * Copyright (C) 2014 by the Massachusetts Institute of Technology.
4be148
+ * 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
4be148
+ * are met:
4be148
+ *
4be148
+ * * Redistributions of source code must retain the above copyright
4be148
+ *   notice, this list of conditions and the following disclaimer.
4be148
+ *
4be148
+ * * 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
4be148
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
4be148
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
4be148
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
4be148
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
4be148
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
4be148
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
4be148
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
4be148
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
4be148
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
4be148
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
4be148
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
4be148
+ */
4be148
+
4be148
+/* This dummy module is used if no TLS implemented is selected. */
4be148
+
4be148
+#include "k5-int.h"
4be148
+#include "k5-utf8.h"
4be148
+#include "k5-tls.h"
4be148
+
4be148
+#ifdef PROXY_TLS_IMPL_NONE
4be148
+
4be148
+krb5_error_code
4be148
+tls_k5tls_initvt(krb5_context context, int maj_ver, int min_ver,
4be148
+                 krb5_plugin_vtable vtable);
4be148
+
4be148
+krb5_error_code
4be148
+tls_k5tls_initvt(krb5_context context, int maj_ver, int min_ver,
4be148
+                 krb5_plugin_vtable vtable)
4be148
+{
4be148
+    /* Leave all vtable functions nulled. */
4be148
+    return 0;
4be148
+}
4be148
+
4be148
+#endif /* PROXY_TLS_IMPL_NONE */
4be148
diff --git a/src/plugins/tls/k5tls/openssl.c b/src/plugins/tls/k5tls/openssl.c
4be148
new file mode 100644
4be148
index 0000000..0691a34
4be148
--- /dev/null
4be148
+++ b/src/plugins/tls/k5tls/openssl.c
4be148
@@ -0,0 +1,570 @@
4be148
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
4be148
+/* plugins/tls/k5tls/openssl.c - OpenSSL TLS module implementation */
4be148
+/*
4be148
+ * Copyright 2013,2014 Red Hat, Inc.
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
+#include "k5-tls.h"
4be148
+
4be148
+#ifdef PROXY_TLS_IMPL_OPENSSL
4be148
+#include <openssl/err.h>
4be148
+#include <openssl/ssl.h>
4be148
+#include <openssl/x509.h>
4be148
+#include <openssl/x509v3.h>
4be148
+#include <dirent.h>
4be148
+
4be148
+struct k5_tls_handle_st {
4be148
+    SSL *ssl;
4be148
+    char *servername;
4be148
+};
4be148
+
4be148
+static int ex_context_id = -1;
4be148
+static int ex_handle_id = -1;
4be148
+
4be148
+MAKE_INIT_FUNCTION(init_openssl);
4be148
+
4be148
+int
4be148
+init_openssl()
4be148
+{
4be148
+    SSL_library_init();
4be148
+    SSL_load_error_strings();
4be148
+    OpenSSL_add_all_algorithms();
4be148
+    ex_context_id = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
4be148
+    ex_handle_id = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
4be148
+    return 0;
4be148
+}
4be148
+
4be148
+static void
4be148
+flush_errors(krb5_context context)
4be148
+{
4be148
+    unsigned long err;
4be148
+    char buf[128];
4be148
+
4be148
+    while ((err = ERR_get_error()) != 0) {
4be148
+        ERR_error_string_n(err, buf, sizeof(buf));
4be148
+        TRACE_TLS_ERROR(context, buf);
4be148
+    }
4be148
+}
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 int
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
+/* Return true if text matches any of the addresses we can recover from x. */
4be148
+static krb5_boolean
4be148
+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
+    int name_length;
4be148
+    struct in_addr sin;
4be148
+    struct in6_addr sin6;
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
+    if (inet_pton(AF_INET, text, &sin)) {
4be148
+        M_ASN1_OCTET_STRING_set(ip, &sin, sizeof(sin));
4be148
+    } else if (inet_pton(AF_INET6, text, &sin6)) {
4be148
+        M_ASN1_OCTET_STRING_set(ip, &sin6, sizeof(sin6));
4be148
+    } else {
4be148
+        ASN1_OCTET_STRING_free(ip);
4be148
+        return FALSE;
4be148
+    }
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 (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) == (size_t)name_length &&
4be148
+               strncmp(text, buf, name_length) == 0;
4be148
+    }
4be148
+
4be148
+    /* We didn't find a match. */
4be148
+    return FALSE;
4be148
+}
4be148
+
4be148
+/* Return true if expected matches any of the names we can recover from x. */
4be148
+static krb5_boolean
4be148
+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
+
4be148
+static krb5_boolean
4be148
+check_cert_name_or_ip(X509 *x, const char *expected_name)
4be148
+{
4be148
+    struct in_addr in;
4be148
+    struct in6_addr in6;
4be148
+
4be148
+    if (inet_pton(AF_INET, expected_name, &in) != 0 ||
4be148
+        inet_pton(AF_INET6, expected_name, &in6) != 0) {
4be148
+        return check_cert_address(x, expected_name);
4be148
+    } else {
4be148
+        return check_cert_servername(x, expected_name);
4be148
+    }
4be148
+}
4be148
+
4be148
+static int
4be148
+verify_callback(int preverify_ok, X509_STORE_CTX *store_ctx)
4be148
+{
4be148
+    X509 *x;
4be148
+    SSL *ssl;
4be148
+    BIO *bio;
4be148
+    krb5_context context;
4be148
+    int err, depth;
4be148
+    k5_tls_handle handle;
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, ex_context_id);
4be148
+    handle = SSL_get_ex_data(ssl, ex_handle_id);
4be148
+    assert(context != NULL && handle != NULL);
4be148
+    /* We do have the peer's cert, right? */
4be148
+    x = X509_STORE_CTX_get_current_cert(store_ctx);
4be148
+    if (x == NULL) {
4be148
+        TRACE_TLS_NO_REMOTE_CERTIFICATE(context);
4be148
+        return 0;
4be148
+    }
4be148
+    /* Figure out where we are. */
4be148
+    depth = X509_STORE_CTX_get_error_depth(store_ctx);
4be148
+    if (depth < 0)
4be148
+        return 0;
4be148
+    /* If there's an error at this level that we're not ignoring, fail. */
4be148
+    err = X509_STORE_CTX_get_error(store_ctx);
4be148
+    if (err != X509_V_OK) {
4be148
+        bio = BIO_new(BIO_s_mem());
4be148
+        if (bio != NULL) {
4be148
+            X509_NAME_print_ex(bio, x->cert_info->subject, 0, 0);
4be148
+            count = BIO_get_mem_data(bio, &cert);
4be148
+            errstr = X509_verify_cert_error_string(err);
4be148
+            TRACE_TLS_CERT_ERROR(context, depth, count, cert, err, errstr);
4be148
+            BIO_free(bio);
4be148
+        }
4be148
+        return 0;
4be148
+    }
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 = handle->servername;
4be148
+    if (check_cert_name_or_ip(x, expected_name)) {
4be148
+        TRACE_TLS_SERVER_NAME_MATCH(context, expected_name);
4be148
+        return 1;
4be148
+    } else {
4be148
+        TRACE_TLS_SERVER_NAME_MISMATCH(context, expected_name);
4be148
+    }
4be148
+    /* The name didn't match. */
4be148
+    return 0;
4be148
+}
4be148
+
4be148
+static krb5_error_code
4be148
+load_anchor_file(X509_STORE *store, const char *path)
4be148
+{
4be148
+    FILE *fp;
4be148
+    STACK_OF(X509_INFO) *sk = NULL;
4be148
+    X509_INFO *xi;
4be148
+    int i;
4be148
+
4be148
+    fp = fopen(path, "r");
4be148
+    if (fp == NULL)
4be148
+        return errno;
4be148
+    sk = PEM_X509_INFO_read(fp, NULL, NULL, NULL);
4be148
+    fclose(fp);
4be148
+    if (sk == NULL)
4be148
+        return ENOENT;
4be148
+    for (i = 0; i < sk_X509_INFO_num(sk); i++) {
4be148
+        xi = sk_X509_INFO_value(sk, i);
4be148
+        if (xi->x509 != NULL)
4be148
+            X509_STORE_add_cert(store, xi->x509);
4be148
+    }
4be148
+    sk_X509_INFO_pop_free(sk, X509_INFO_free);
4be148
+    return 0;
4be148
+}
4be148
+
4be148
+static krb5_error_code
4be148
+load_anchor_dir(X509_STORE *store, const char *path)
4be148
+{
4be148
+    DIR *d = NULL;
4be148
+    struct dirent *dentry = NULL;
4be148
+    char filename[1024];
4be148
+    krb5_boolean found_any = FALSE;
4be148
+
4be148
+    d = opendir(path);
4be148
+    if (d == NULL)
4be148
+        return ENOENT;
4be148
+    while ((dentry = readdir(d)) != NULL) {
4be148
+        if (dentry->d_name[0] != '.') {
4be148
+            snprintf(filename, sizeof(filename), "%s/%s",
4be148
+                     path, dentry->d_name);
4be148
+            if (load_anchor_file(store, filename) == 0)
4be148
+                found_any = TRUE;
4be148
+        }
4be148
+    }
4be148
+    closedir(d);
4be148
+    return found_any ? 0 : ENOENT;
4be148
+}
4be148
+
4be148
+static krb5_error_code
4be148
+load_anchor(SSL_CTX *ctx, const char *location)
4be148
+{
4be148
+    X509_STORE *store;
4be148
+    const char *envloc;
4be148
+
4be148
+    store = SSL_CTX_get_cert_store(ctx);
4be148
+    if (strncmp(location, "FILE:", 5) == 0) {
4be148
+        return load_anchor_file(store, location + 5);
4be148
+    } else if (strncmp(location, "DIR:", 4) == 0) {
4be148
+        return load_anchor_dir(store, location + 4);
4be148
+    } else if (strncmp(location, "ENV:", 4) == 0) {
4be148
+        envloc = getenv(location + 4);
4be148
+        if (envloc == NULL)
4be148
+            return ENOENT;
4be148
+        return load_anchor(ctx, envloc);
4be148
+    }
4be148
+    return EINVAL;
4be148
+}
4be148
+
4be148
+static krb5_error_code
4be148
+load_anchors(krb5_context context, char **anchors, SSL_CTX *sctx)
4be148
+{
4be148
+    unsigned int i;
4be148
+    krb5_error_code ret;
4be148
+
4be148
+    if (anchors != NULL) {
4be148
+        for (i = 0; anchors[i] != NULL; i++) {
4be148
+            ret = load_anchor(sctx, anchors[i]);
4be148
+            if (ret)
4be148
+                return ret;
4be148
+        }
4be148
+    } else {
4be148
+        /* Use the library defaults. */
4be148
+        if (SSL_CTX_set_default_verify_paths(sctx) != 1)
4be148
+            return ENOENT;
4be148
+    }
4be148
+
4be148
+    return 0;
4be148
+}
4be148
+
4be148
+static krb5_error_code
4be148
+setup(krb5_context context, SOCKET fd, const char *servername,
4be148
+      char **anchors, k5_tls_handle *handle_out)
4be148
+{
4be148
+    int e;
4be148
+    long options;
4be148
+    SSL_CTX *ctx = NULL;
4be148
+    SSL *ssl = NULL;
4be148
+    k5_tls_handle handle = NULL;
4be148
+
4be148
+    *handle_out = NULL;
4be148
+
4be148
+    (void)CALL_INIT_FUNCTION(init_openssl);
4be148
+    if (ex_context_id == -1 || ex_handle_id == -1)
4be148
+        return KRB5_PLUGIN_OP_NOTSUPP;
4be148
+
4be148
+    /* Do general SSL library setup. */
4be148
+    ctx = SSL_CTX_new(SSLv23_client_method());
4be148
+    if (ctx == NULL)
4be148
+        goto error;
4be148
+    options = SSL_CTX_get_options(ctx);
4be148
+    SSL_CTX_set_options(ctx, options | SSL_OP_NO_SSLv2);
4be148
+
4be148
+    SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_callback);
4be148
+    X509_STORE_set_flags(SSL_CTX_get_cert_store(ctx), 0);
4be148
+    e = load_anchors(context, anchors, ctx);
4be148
+    if (e != 0)
4be148
+        goto error;
4be148
+
4be148
+    ssl = SSL_new(ctx);
4be148
+    if (ssl == NULL)
4be148
+        goto error;
4be148
+
4be148
+    if (!SSL_set_fd(ssl, fd))
4be148
+        goto error;
4be148
+    SSL_set_connect_state(ssl);
4be148
+
4be148
+    /* Create a handle and allow verify_callback to access it. */
4be148
+    handle = malloc(sizeof(*handle));
4be148
+    if (handle == NULL || !SSL_set_ex_data(ssl, ex_handle_id, handle))
4be148
+        goto error;
4be148
+
4be148
+    handle->ssl = ssl;
4be148
+    handle->servername = strdup(servername);
4be148
+    if (handle->servername == NULL)
4be148
+        goto error;
4be148
+    *handle_out = handle;
4be148
+    SSL_CTX_free(ctx);
4be148
+    return 0;
4be148
+
4be148
+error:
4be148
+    flush_errors(context);
4be148
+    free(handle);
4be148
+    SSL_free(ssl);
4be148
+    SSL_CTX_free(ctx);
4be148
+    return KRB5_PLUGIN_OP_NOTSUPP;
4be148
+}
4be148
+
4be148
+static k5_tls_status
4be148
+write_tls(krb5_context context, k5_tls_handle handle, const void *data,
4be148
+          size_t len)
4be148
+{
4be148
+    int nwritten, e;
4be148
+
4be148
+    /* Try to transmit our request; allow verify_callback to access context. */
4be148
+    if (!SSL_set_ex_data(handle->ssl, ex_context_id, context))
4be148
+        return ERROR_TLS;
4be148
+    nwritten = SSL_write(handle->ssl, data, len);
4be148
+    (void)SSL_set_ex_data(handle->ssl, ex_context_id, NULL);
4be148
+    if (nwritten > 0)
4be148
+        return DONE;
4be148
+
4be148
+    e = SSL_get_error(handle->ssl, nwritten);
4be148
+    if (e == SSL_ERROR_WANT_READ)
4be148
+        return WANT_READ;
4be148
+    else if (e == SSL_ERROR_WANT_WRITE)
4be148
+        return WANT_WRITE;
4be148
+    flush_errors(context);
4be148
+    return ERROR_TLS;
4be148
+}
4be148
+
4be148
+static k5_tls_status
4be148
+read_tls(krb5_context context, k5_tls_handle handle, void *data,
4be148
+         size_t data_size, size_t *len_out)
4be148
+{
4be148
+    ssize_t nread;
4be148
+    int e;
4be148
+
4be148
+    *len_out = 0;
4be148
+
4be148
+    /* Try to read response data; allow verify_callback to access context. */
4be148
+    if (!SSL_set_ex_data(handle->ssl, ex_context_id, context))
4be148
+        return ERROR_TLS;
4be148
+    nread = SSL_read(handle->ssl, data, data_size);
4be148
+    (void)SSL_set_ex_data(handle->ssl, ex_context_id, NULL);
4be148
+    if (nread > 0) {
4be148
+        *len_out = nread;
4be148
+        return DATA_READ;
4be148
+    }
4be148
+
4be148
+    e = SSL_get_error(handle->ssl, nread);
4be148
+    if (e == SSL_ERROR_WANT_READ)
4be148
+        return WANT_READ;
4be148
+    else if (e == SSL_ERROR_WANT_WRITE)
4be148
+        return WANT_WRITE;
4be148
+
4be148
+    if (e == SSL_ERROR_ZERO_RETURN || (e == SSL_ERROR_SYSCALL && nread == 0))
4be148
+        return DONE;
4be148
+
4be148
+    flush_errors(context);
4be148
+    return ERROR_TLS;
4be148
+}
4be148
+
4be148
+static void
4be148
+free_handle(krb5_context context, k5_tls_handle handle)
4be148
+{
4be148
+    SSL_free(handle->ssl);
4be148
+    free(handle->servername);
4be148
+    free(handle);
4be148
+}
4be148
+
4be148
+krb5_error_code
4be148
+tls_k5tls_initvt(krb5_context context, int maj_ver, int min_ver,
4be148
+                 krb5_plugin_vtable vtable);
4be148
+
4be148
+krb5_error_code
4be148
+tls_k5tls_initvt(krb5_context context, int maj_ver, int min_ver,
4be148
+                 krb5_plugin_vtable vtable)
4be148
+{
4be148
+    k5_tls_vtable vt;
4be148
+
4be148
+    vt = (k5_tls_vtable)vtable;
4be148
+    vt->setup = setup;
4be148
+    vt->write = write_tls;
4be148
+    vt->read = read_tls;
4be148
+    vt->free_handle = free_handle;
4be148
+    return 0;
4be148
+}
4be148
+
4be148
+#endif /* PROXY_TLS_IMPL_OPENSSL */