summaryrefslogtreecommitdiffstats
path: root/0007-HTTPS-transport-Microsoft-KKDCPP-implementation.patch
diff options
context:
space:
mode:
authorNalin Dahyabhai <nalin@redhat.com>2014-09-05 17:51:35 -0400
committerNalin Dahyabhai <nalin@redhat.com>2014-09-05 18:18:58 -0400
commit888bc144da94c9bf8d2c35ab38868e748c059de3 (patch)
tree75f2892ca8e4b049bbdce82ffd200cf66dea81bc /0007-HTTPS-transport-Microsoft-KKDCPP-implementation.patch
parentf69697ba82697909efed00f34c51901f881e1989 (diff)
downloadkrb5-888bc144da94c9bf8d2c35ab38868e748c059de3.tar.gz
krb5-888bc144da94c9bf8d2c35ab38868e748c059de3.tar.xz
krb5-888bc144da94c9bf8d2c35ab38868e748c059de3.zip
Add HTTPS patches from masterkrb5-1.12.2-6.fc21
Pull in a stack of patches to add support for accessing servers via HTTPS proxies, such as python-kdcproxy or the KDC Proxy Service on a properly-outfitted Windows box. Pull in the patch to move the logic out of libkrb5 proper and into a loadable plugin to avoid linking our local applications against our libkrb5 against libssl against the installed copy of libgssapi_krb5 and our local libkrb5support. Adjust a couple of other patches to apply correctly after them.
Diffstat (limited to '0007-HTTPS-transport-Microsoft-KKDCPP-implementation.patch')
-rw-r--r--0007-HTTPS-transport-Microsoft-KKDCPP-implementation.patch858
1 files changed, 858 insertions, 0 deletions
diff --git a/0007-HTTPS-transport-Microsoft-KKDCPP-implementation.patch b/0007-HTTPS-transport-Microsoft-KKDCPP-implementation.patch
new file mode 100644
index 0000000..cd21dac
--- /dev/null
+++ b/0007-HTTPS-transport-Microsoft-KKDCPP-implementation.patch
@@ -0,0 +1,858 @@
+From d950809ff49e3e7603594186d77135a09ab6b1b2 Mon Sep 17 00:00:00 2001
+From: Nalin Dahyabhai <nalin@dahyabhai.net>
+Date: Thu, 24 Apr 2014 16:30:56 -0400
+Subject: [PATCH 07/13] HTTPS transport (Microsoft KKDCPP implementation)
+
+Add an 'HTTPS' transport type which connects to an [MS-KKDCP] proxy
+server using HTTPS to communicate with a KDC. The KDC's name should
+take the form of an HTTPS URL (e.g. "https://proxybox/KdcProxy").
+
+An HTTPS connection's encryption layer can be reading and writing when
+the application layer is expecting to write and read, so the HTTPS
+callbacks have to handle being called multiple times.
+
+[nalin@redhat.com: use cleanup labels, make sure we always send the
+ realm name, keep a copy of the URI on-hand, move most of the
+ conditionally-compiled sections into their own conditionally-built
+ functions, break out HTTPS request formatting into a helper function,
+ handle the MS-KKDCP length bytes, update comments to mention specific
+ versions of the MS-KKDCP spec, differentiate TCP and HTTP trace
+ messages, trace unparseable responses]
+
+ticket: 7929
+---
+ src/include/k5-trace.h | 13 ++
+ src/lib/krb5/os/locate_kdc.c | 63 ++++++-
+ src/lib/krb5/os/os-proto.h | 2 +
+ src/lib/krb5/os/sendto_kdc.c | 417 ++++++++++++++++++++++++++++++++++++++---
+ src/lib/krb5/os/t_locate_kdc.c | 2 +
+ src/lib/krb5/os/trace.c | 2 +
+ 6 files changed, 471 insertions(+), 28 deletions(-)
+
+diff --git a/src/include/k5-trace.h b/src/include/k5-trace.h
+index dfd34f6..f0d79f1 100644
+--- a/src/include/k5-trace.h
++++ b/src/include/k5-trace.h
+@@ -312,6 +312,9 @@ void krb5int_trace(krb5_context context, const char *fmt, ...);
+ TRACE(c, "AP-REQ ticket: {princ} -> {princ}, session key {keyblock}", \
+ client, server, keyblock)
+
++#define TRACE_SENDTO_KDC_ERROR_SET_MESSAGE(c, raddr, err) \
++ TRACE(c, "Error preparing message to send to {raddr}: {errno}", \
++ raddr, err)
+ #define TRACE_SENDTO_KDC(c, len, rlm, master, tcp) \
+ TRACE(c, "Sending request ({int} bytes) to {data}{str}{str}", len, \
+ rlm, (master) ? " (master)" : "", (tcp) ? " (tcp only)" : "")
+@@ -321,6 +324,16 @@ 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_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) \
++ TRACE(c, "HTTPS error sending to {raddr}", raddr)
++#define TRACE_SENDTO_KDC_HTTPS_SEND(c, raddr) \
++ TRACE(c, "Sending HTTPS request to {raddr}", raddr)
++#define TRACE_SENDTO_KDC_HTTPS_ERROR(c, errs) \
++ TRACE(c, "HTTPS error: {str}", errs)
+ #define TRACE_SENDTO_KDC_TCP_CONNECT(c, raddr) \
+ TRACE(c, "Initiating TCP connection to {raddr}", raddr)
+ #define TRACE_SENDTO_KDC_TCP_DISCONNECT(c, raddr) \
+diff --git a/src/lib/krb5/os/locate_kdc.c b/src/lib/krb5/os/locate_kdc.c
+index 4c8aead..1136809 100644
+--- a/src/lib/krb5/os/locate_kdc.c
++++ b/src/lib/krb5/os/locate_kdc.c
+@@ -91,8 +91,10 @@ k5_free_serverlist (struct serverlist *list)
+ {
+ size_t i;
+
+- for (i = 0; i < list->nservers; i++)
++ for (i = 0; i < list->nservers; i++) {
+ free(list->servers[i].hostname);
++ free(list->servers[i].uri_path);
++ }
+ free(list->servers);
+ list->servers = NULL;
+ list->nservers = 0;
+@@ -140,6 +142,7 @@ add_addr_to_list(struct serverlist *list, k5_transport transport, int family,
+ entry->transport = transport;
+ entry->family = family;
+ entry->hostname = NULL;
++ entry->uri_path = NULL;
+ entry->addrlen = addrlen;
+ memcpy(&entry->addr, addr, addrlen);
+ list->nservers++;
+@@ -149,7 +152,7 @@ add_addr_to_list(struct serverlist *list, k5_transport transport, int family,
+ /* Add a hostname entry to list. */
+ static int
+ add_host_to_list(struct serverlist *list, const char *hostname, int port,
+- k5_transport transport, int family)
++ k5_transport transport, int family, char *uri_path)
+ {
+ struct server_entry *entry;
+
+@@ -160,11 +163,46 @@ add_host_to_list(struct serverlist *list, const char *hostname, int port,
+ entry->family = family;
+ entry->hostname = strdup(hostname);
+ if (entry->hostname == NULL)
+- return ENOMEM;
++ goto oom;
++ if (uri_path != NULL) {
++ entry->uri_path = strdup(uri_path);
++ if (entry->uri_path == NULL)
++ goto oom;
++ }
+ entry->port = port;
+ list->nservers++;
+ return 0;
++oom:
++ free(entry->hostname);
++ entry->hostname = NULL;
++ 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)
++{
++ char *cp;
++
++ if (strncmp(host_or_uri, "https://", 8) == 0) {
++ *transport = HTTPS;
++ *host = host_or_uri + 8;
++
++ cp = strchr(*host, '/');
++ if (cp != NULL) {
++ *cp = '\0';
++ *uri_path = cp + 1;
++ }
++ }
++}
++#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
+@@ -222,9 +260,14 @@ locate_srv_conf_1(krb5_context context, const krb5_data *realm,
+
+ for (i=0; hostlist[i]; i++) {
+ int p1, p2;
++ k5_transport this_transport = transport;
++ char *uri_path = NULL;
+
+ host = hostlist[i];
+ Tprintf ("entry %d is '%s'\n", i, host);
++
++ parse_uri_if_https(host, &this_transport, &host, &uri_path);
++
+ /* Find port number, and strip off any excess characters. */
+ if (*host == '[' && (cp = strchr(host, ']')))
+ cp = cp + 1;
+@@ -244,6 +287,9 @@ locate_srv_conf_1(krb5_context context, const krb5_data *realm,
+ return EINVAL;
+ p1 = htons (l);
+ p2 = 0;
++ } else if (this_transport == HTTPS) {
++ p1 = htons(443);
++ p2 = 0;
+ } else {
+ p1 = udpport;
+ p2 = sec_udpport;
+@@ -255,12 +301,15 @@ locate_srv_conf_1(krb5_context context, const krb5_data *realm,
+ *cp = '\0';
+ }
+
+- code = add_host_to_list(serverlist, host, p1, transport, AF_UNSPEC);
++ code = add_host_to_list(serverlist, host, p1, this_transport,
++ AF_UNSPEC, uri_path);
+ /* Second port is for IPv4 UDP only, and should possibly go away as
+ * it was originally a krb4 compatibility measure. */
+ if (code == 0 && p2 != 0 &&
+- (transport == TCP_OR_UDP || transport == UDP))
+- code = add_host_to_list(serverlist, host, p2, UDP, AF_INET);
++ (this_transport == TCP_OR_UDP || this_transport == UDP)) {
++ code = add_host_to_list(serverlist, host, p2, UDP, AF_INET,
++ uri_path);
++ }
+ if (code)
+ goto cleanup;
+ }
+@@ -313,7 +362,7 @@ locate_srv_dns_1(const krb5_data *realm, const char *service,
+ for (entry = head; entry != NULL; entry = entry->next) {
+ transport = (strcmp(protocol, "_tcp") == 0) ? TCP : UDP;
+ code = add_host_to_list(serverlist, entry->host, htons(entry->port),
+- transport, AF_UNSPEC);
++ transport, AF_UNSPEC, NULL);
+ if (code)
+ goto cleanup;
+ }
+diff --git a/src/lib/krb5/os/os-proto.h b/src/lib/krb5/os/os-proto.h
+index e60ccd0..34bf028 100644
+--- a/src/lib/krb5/os/os-proto.h
++++ b/src/lib/krb5/os/os-proto.h
+@@ -42,6 +42,7 @@ typedef enum {
+ TCP_OR_UDP = 0,
+ TCP,
+ UDP,
++ HTTPS,
+ } k5_transport;
+
+ typedef enum {
+@@ -55,6 +56,7 @@ struct server_entry {
+ char *hostname; /* NULL -> use addrlen/addr instead */
+ int port; /* Used only if hostname set */
+ k5_transport transport; /* May be 0 for UDP/TCP if hostname set */
++ char *uri_path; /* Used only if transport is HTTPS */
+ int family; /* May be 0 (aka AF_UNSPEC) if hostname set */
+ size_t addrlen;
+ struct sockaddr_storage addr;
+diff --git a/src/lib/krb5/os/sendto_kdc.c b/src/lib/krb5/os/sendto_kdc.c
+index 28f1c4d..a4727c4 100644
+--- a/src/lib/krb5/os/sendto_kdc.c
++++ b/src/lib/krb5/os/sendto_kdc.c
+@@ -23,6 +23,32 @@
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
++/*
++ * MS-KKDCP 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.
++ */
+
+ /* Send packet to KDC for realm; wait for response, retransmitting
+ * as necessary. */
+@@ -49,6 +75,7 @@
+ #endif
+
+ #ifdef PROXY_TLS_IMPL_OPENSSL
++#include <openssl/err.h>
+ #include <openssl/ssl.h>
+ #endif
+
+@@ -116,6 +143,13 @@ struct conn_state {
+ struct conn_state *next;
+ time_ms endtime;
+ krb5_boolean defer;
++ struct {
++ const char *uri_path;
++ char *https_request;
++#ifdef PROXY_TLS_IMPL_OPENSSL
++ SSL *ssl;
++#endif
++ } http;
+ };
+
+ void
+@@ -140,6 +174,22 @@ get_curtime_ms(time_ms *time_out)
+ return 0;
+ }
+
++#ifdef PROXY_TLS_IMPL_OPENSSL
++static void
++free_http_ssl_data(struct conn_state *state)
++{
++ SSL_free(state->http.ssl);
++ state->http.ssl = 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
+
+ /* Find a pollfd in selstate by fd, or abort if we can't find it. */
+@@ -321,6 +371,7 @@ socktype_for_transport(k5_transport transport)
+ case UDP:
+ return SOCK_DGRAM;
+ case TCP:
++ case HTTPS:
+ return SOCK_STREAM;
+ default:
+ return 0;
+@@ -468,33 +519,113 @@ static fd_handler_fn service_tcp_connect;
+ static fd_handler_fn service_tcp_write;
+ static fd_handler_fn service_tcp_read;
+ 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)
++{
++ krb5_kkdcp_message pm;
++ krb5_data *encoded_pm = NULL;
++ struct k5buf buf;
++ const char *uri_path;
++ krb5_error_code ret;
++
++ *req_out = NULL;
++ *len_out = 0;
++
++ /*
++ * Stuff the message length in at the front of the kerb_message field
++ * before encoding. The proxied messages are actually the payload we'd
++ * be sending and receiving if we were using plain TCP.
++ */
++ memset(&pm, 0, sizeof(pm));
++ ret = alloc_data(&pm.kerb_message, message->length + 4);
++ if (ret != 0)
++ goto cleanup;
++ store_32_be(message->length, pm.kerb_message.data);
++ memcpy(pm.kerb_message.data + 4, message->data, message->length);
++ pm.target_domain = *realm;
++ ret = encode_krb5_kkdcp_message(&pm, &encoded_pm);
++ if (ret != 0)
++ goto cleanup;
++
++ /* Build the request to transmit: the headers + the proxy message. */
++ k5_buf_init_dynamic(&buf);
++ uri_path = (state->http.uri_path != NULL) ? state->http.uri_path : "";
++ k5_buf_add_fmt(&buf, "POST /%s HTTP/1.0\r\n", uri_path);
++ k5_buf_add(&buf, "Cache-Control: no-cache\r\n");
++ k5_buf_add(&buf, "Pragma: no-cache\r\n");
++ k5_buf_add(&buf, "User-Agent: kerberos/1.0\r\n");
++ k5_buf_add(&buf, "Content-type: application/kerberos\r\n");
++ k5_buf_add_fmt(&buf, "Content-Length: %d\r\n\r\n", encoded_pm->length);
++ k5_buf_add_len(&buf, encoded_pm->data, encoded_pm->length);
++ if (k5_buf_data(&buf) == NULL) {
++ ret = ENOMEM;
++ goto cleanup;
++ }
++
++ *req_out = k5_buf_data(&buf);
++ *len_out = k5_buf_len(&buf);
++
++cleanup:
++ krb5_free_data_contents(NULL, &pm.kerb_message);
++ 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. */
+-static void
+-set_transport_message(struct conn_state *state, const krb5_data *message)
++static krb5_error_code
++set_transport_message(struct conn_state *state, const krb5_data *realm,
++ const krb5_data *message)
+ {
+ struct outgoing_message *out = &state->out;
++ char *req = NULL;
++ size_t reqlen;
++ krb5_error_code ret;
+
+ if (message == NULL || message->length == 0)
+- return;
++ return 0;
+
+ if (state->addr.transport == TCP) {
+ store_32_be(message->length, out->msg_len_buf);
+ SG_SET(&out->sgbuf[0], out->msg_len_buf, 4);
+ SG_SET(&out->sgbuf[1], message->data, message->length);
+ out->sg_count = 2;
++ return 0;
++ } else if (state->addr.transport == HTTPS) {
++ ret = make_proxy_request(state, realm, message, &req, &reqlen);
++ if (ret != 0)
++ return ret;
++ SG_SET(&state->out.sgbuf[0], req, reqlen);
++ SG_SET(&state->out.sgbuf[1], 0, 0);
++ state->out.sg_count = 1;
++ free(state->http.https_request);
++ state->http.https_request = req;
++ return 0;
+ } else {
+ SG_SET(&out->sgbuf[0], message->data, message->length);
+ SG_SET(&out->sgbuf[1], NULL, 0);
+ out->sg_count = 1;
++ return 0;
+ }
+ }
+
+ static krb5_error_code
+ add_connection(struct conn_state **conns, k5_transport transport,
+ krb5_boolean defer, struct addrinfo *ai, size_t server_index,
+- char **udpbufp)
++ const krb5_data *realm, const char *uri_path, char **udpbufp)
+ {
+ struct conn_state *state, **tailptr;
+
+@@ -515,6 +646,11 @@ add_connection(struct conn_state **conns, k5_transport transport,
+ state->service_connect = service_tcp_connect;
+ state->service_write = service_tcp_write;
+ state->service_read = service_tcp_read;
++ } else if (transport == HTTPS) {
++ state->service_connect = service_tcp_connect;
++ state->service_write = service_https_write;
++ state->service_read = service_https_read;
++ state->http.uri_path = uri_path;
+ } else {
+ state->service_connect = NULL;
+ state->service_write = NULL;
+@@ -589,10 +725,10 @@ translate_ai_error (int err)
+ * connections.
+ */
+ static krb5_error_code
+-resolve_server(krb5_context context, const struct serverlist *servers,
+- size_t ind, k5_transport_strategy strategy,
+- const krb5_data *message, char **udpbufp,
+- struct conn_state **conns)
++resolve_server(krb5_context context, const krb5_data *realm,
++ const struct serverlist *servers, size_t ind,
++ k5_transport_strategy strategy, const krb5_data *message,
++ char **udpbufp, struct conn_state **conns)
+ {
+ krb5_error_code retval;
+ struct server_entry *entry = &servers->servers[ind];
+@@ -615,7 +751,7 @@ resolve_server(krb5_context context, const struct serverlist *servers,
+ ai.ai_addr = (struct sockaddr *)&entry->addr;
+ defer = (entry->transport != transport);
+ return add_connection(conns, entry->transport, defer, &ai, ind,
+- udpbufp);
++ realm, entry->uri_path, udpbufp);
+ }
+
+ /* If the entry has a specified transport, use it. */
+@@ -639,8 +775,10 @@ resolve_server(krb5_context context, const struct serverlist *servers,
+
+ /* Add each address with the specified or preferred transport. */
+ retval = 0;
+- for (a = addrs; a != 0 && retval == 0; a = a->ai_next)
+- retval = add_connection(conns, transport, FALSE, a, ind, udpbufp);
++ for (a = addrs; a != 0 && retval == 0; a = a->ai_next) {
++ retval = add_connection(conns, transport, FALSE, a, ind, realm,
++ entry->uri_path, udpbufp);
++ }
+
+ /* For TCP_OR_UDP entries, add each address again with the non-preferred
+ * transport, unless we are avoiding UDP. Flag these as deferred. */
+@@ -648,7 +786,8 @@ resolve_server(krb5_context context, const struct serverlist *servers,
+ transport = (strategy == UDP_FIRST) ? TCP : UDP;
+ for (a = addrs; a != 0 && retval == 0; a = a->ai_next) {
+ a->ai_socktype = socktype_for_transport(transport);
+- retval = add_connection(conns, transport, TRUE, a, ind, udpbufp);
++ retval = add_connection(conns, transport, TRUE, a, ind, realm,
++ entry->uri_path, udpbufp);
+ }
+ }
+ freeaddrinfo(addrs);
+@@ -658,6 +797,7 @@ resolve_server(krb5_context context, const struct serverlist *servers,
+ static int
+ start_connection(krb5_context context, struct conn_state *state,
+ const krb5_data *message, struct select_state *selstate,
++ const krb5_data *realm,
+ struct sendto_callback_info *callback_info)
+ {
+ int fd, e, type;
+@@ -718,7 +858,15 @@ start_connection(krb5_context context, struct conn_state *state,
+
+ message = &state->callback_buffer;
+ }
+- set_transport_message(state, message);
++
++ e = set_transport_message(state, realm, message);
++ if (e != 0) {
++ TRACE_SENDTO_KDC_ERROR_SET_MESSAGE(context, &state->addr, e);
++ (void) closesocket(state->fd);
++ state->fd = INVALID_SOCKET;
++ state->state = FAILED;
++ return -4;
++ }
+
+ if (state->addr.transport == UDP) {
+ /* Send it now. */
+@@ -733,7 +881,7 @@ start_connection(krb5_context context, struct conn_state *state,
+ (void) closesocket(state->fd);
+ state->fd = INVALID_SOCKET;
+ state->state = FAILED;
+- return -4;
++ return -5;
+ } else {
+ state->state = READING;
+ }
+@@ -760,6 +908,7 @@ start_connection(krb5_context context, struct conn_state *state,
+ static int
+ maybe_send(krb5_context context, struct conn_state *conn,
+ const krb5_data *message, struct select_state *selstate,
++ const krb5_data *realm,
+ struct sendto_callback_info *callback_info)
+ {
+ sg_buf *sg;
+@@ -767,7 +916,7 @@ maybe_send(krb5_context context, struct conn_state *conn,
+
+ if (conn->state == INITIALIZING) {
+ return start_connection(context, conn, message, selstate,
+- callback_info);
++ realm, callback_info);
+ }
+
+ /* Did we already shut down this channel? */
+@@ -802,6 +951,8 @@ static void
+ kill_conn(krb5_context context, struct conn_state *conn,
+ struct select_state *selstate)
+ {
++ free_http_ssl_data(conn);
++
+ if (socktype_for_transport(conn->addr.transport) == SOCK_STREAM)
+ TRACE_SENDTO_KDC_TCP_DISCONNECT(context, &conn->addr);
+ cm_remove_fd(selstate, conn->fd);
+@@ -876,7 +1027,7 @@ service_tcp_connect(krb5_context context, const krb5_data *realm,
+ if (get_curtime_ms(&conn->endtime) == 0)
+ conn->endtime += 10000;
+
+- return service_tcp_write(context, realm, conn, selstate);
++ return conn->service_write(context, realm, conn, selstate);
+ }
+
+ /* Sets conn->state to READING when done. */
+@@ -982,6 +1133,223 @@ 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);
++ }
++}
++
++/*
++ * 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)
++{
++ long options;
++ SSL_CTX *ctx = NULL;
++ SSL *ssl = NULL;
++
++ /* 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, NULL);
++ if (!SSL_CTX_set_default_verify_paths(ctx))
++ goto kill_conn;
++
++ ssl = SSL_new(ctx);
++ if (ssl == NULL)
++ 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;
++}
++
++/* Set conn->state to READING when done; otherwise, call a cm_set_. */
++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;
++
++ /* If this is our first time in here, set up the SSL context. */
++ if (conn->http.ssl == NULL && !setup_ssl(context, realm, 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;
++ }
++ 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.
++ */
++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;
++ char *tmp;
++ struct incoming_message *in = &conn->in;
++
++ for (;;) {
++ if (in->buf == NULL || in->bufsize - in->pos < 1024) {
++ bufsize = in->bufsize ? in->bufsize * 2 : 8192;
++ if (bufsize > 1024 * 1024) {
++ kill_conn(context, conn, selstate);
++ return FALSE;
++ }
++ tmp = realloc(in->buf, bufsize);
++ if (tmp == NULL) {
++ kill_conn(context, conn, selstate);
++ return FALSE;
++ }
++ in->buf = tmp;
++ in->bufsize = bufsize;
++ }
++
++ nread = SSL_read(conn->http.ssl, &in->buf[in->pos],
++ in->bufsize - in->pos - 1);
++ if (nread <= 0)
++ 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) {
++ cm_read(selstate, conn->fd);
++ return FALSE;
++ } else if (e == SSL_ERROR_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;
++ }
++
++ 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;
++}
++
++/* Return true on readable, valid KKDCPP data. */
++static krb5_boolean
++service_https_read(krb5_context context, const krb5_data *realm,
++ struct conn_state *conn, struct select_state *selstate)
++{
++ krb5_kkdcp_message *pm = NULL;
++ krb5_data buf;
++ const char *rep;
++ struct incoming_message *in = &conn->in;
++
++ /* Read data through the encryption layer. */
++ if (!https_read_bytes(context, conn, selstate))
++ return FALSE;
++
++ /* Find the beginning of the response body. */
++ rep = strstr(in->buf, "\r\n\r\n");
++ if (rep == NULL)
++ goto kill_conn;
++ rep += 4;
++
++ /* Decode the response. */
++ buf = make_data((char *)rep, in->pos - (rep - in->buf));
++ if (decode_krb5_kkdcp_message(&buf, &pm) != 0)
++ goto kill_conn;
++
++ /* Check and discard the message length at the front of the kerb_message
++ * field after decoding. If it's wrong or missing, something broke. */
++ if (pm->kerb_message.length < 4 ||
++ load_32_be(pm->kerb_message.data) != pm->kerb_message.length - 4) {
++ goto kill_conn;
++ }
++
++ /* Replace all of the content that we read back with just the message. */
++ memcpy(in->buf, pm->kerb_message.data + 4, pm->kerb_message.length - 4);
++ in->pos = pm->kerb_message.length - 4;
++ k5_free_kkdcp_message(context, pm);
++
++ return TRUE;
++
++kill_conn:
++ TRACE_SENDTO_KDC_HTTPS_ERROR(context, in->buf);
++ k5_free_kkdcp_message(context, pm);
++ 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. */
+ static time_ms
+@@ -1123,7 +1491,7 @@ k5_sendto(krb5_context context, const krb5_data *message,
+ for (s = 0; s < servers->nservers && !done; s++) {
+ /* Find the current tail pointer. */
+ for (tailptr = &conns; *tailptr != NULL; tailptr = &(*tailptr)->next);
+- retval = resolve_server(context, servers, s, strategy, message,
++ retval = resolve_server(context, realm, servers, s, strategy, message,
+ &udpbuf, &conns);
+ if (retval)
+ goto cleanup;
+@@ -1132,7 +1500,8 @@ k5_sendto(krb5_context context, const krb5_data *message,
+ * non-preferred RFC 4120 transport. */
+ if (state->defer)
+ continue;
+- if (maybe_send(context, state, message, sel_state, callback_info))
++ if (maybe_send(context, state, message, sel_state, realm,
++ callback_info))
+ continue;
+ done = service_fds(context, sel_state, 1000, conns, seltemp,
+ realm, msg_handler, msg_handler_data, &winner);
+@@ -1144,7 +1513,8 @@ k5_sendto(krb5_context context, const krb5_data *message,
+ for (state = conns; state != NULL && !done; state = state->next) {
+ if (!state->defer)
+ continue;
+- if (maybe_send(context, state, message, sel_state, callback_info))
++ if (maybe_send(context, state, message, sel_state, realm,
++ callback_info))
+ continue;
+ done = service_fds(context, sel_state, 1000, conns, seltemp,
+ realm, msg_handler, msg_handler_data, &winner);
+@@ -1160,7 +1530,8 @@ k5_sendto(krb5_context context, const krb5_data *message,
+ delay = 4000;
+ for (pass = 1; pass < MAX_PASS && !done; pass++) {
+ for (state = conns; state != NULL && !done; state = state->next) {
+- if (maybe_send(context, state, message, sel_state, callback_info))
++ if (maybe_send(context, state, message, sel_state, realm,
++ callback_info))
+ continue;
+ done = service_fds(context, sel_state, 1000, conns, seltemp,
+ realm, msg_handler, msg_handler_data, &winner);
+@@ -1194,8 +1565,12 @@ k5_sendto(krb5_context context, const krb5_data *message,
+ cleanup:
+ for (state = conns; state != NULL; state = next) {
+ next = state->next;
+- if (state->fd != INVALID_SOCKET)
++ if (state->fd != INVALID_SOCKET) {
++ 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);
++ }
+ if (state->state == READING && state->in.buf != udpbuf)
+ free(state->in.buf);
+ if (callback_info) {
+diff --git a/src/lib/krb5/os/t_locate_kdc.c b/src/lib/krb5/os/t_locate_kdc.c
+index 300aa71..dd609fd 100644
+--- a/src/lib/krb5/os/t_locate_kdc.c
++++ b/src/lib/krb5/os/t_locate_kdc.c
+@@ -39,6 +39,8 @@ ttypename (k5_transport ttype)
+ return "tcp";
+ case UDP:
+ return "udp";
++ case HTTPS:
++ return "https";
+ default:
+ snprintf(buf, sizeof(buf), "?%d", ttype);
+ return buf;
+diff --git a/src/lib/krb5/os/trace.c b/src/lib/krb5/os/trace.c
+index 8319a86..105a2cd 100644
+--- a/src/lib/krb5/os/trace.c
++++ b/src/lib/krb5/os/trace.c
+@@ -201,6 +201,8 @@ trace_format(krb5_context context, const char *fmt, va_list ap)
+ k5_buf_add(&buf, "dgram");
+ else if (ra->transport == TCP)
+ k5_buf_add(&buf, "stream");
++ else if (ra->transport == HTTPS)
++ k5_buf_add(&buf, "https");
+ else
+ k5_buf_add_fmt(&buf, "transport%d", ra->transport);
+
+--
+2.1.0
+