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