summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--0000-Refactor-cm-functions-in-sendto_kdc.c.patch436
-rw-r--r--0001-Simplify-sendto_kdc.c.patch421
-rw-r--r--0002-Add-helper-to-determine-if-a-KDC-is-the-master.patch233
-rw-r--r--0003-Use-k5_transport-_strategy-enums-for-k5_sendto.patch913
-rw-r--r--0004-Build-support-for-TLS-used-by-HTTPS-proxy-support.patch187
-rw-r--r--0005-Add-ASN.1-codec-for-KKDCP-s-KDC-PROXY-MESSAGE.patch352
-rw-r--r--0006-Dispatch-style-protocol-switching-for-transport.patch506
-rw-r--r--0007-HTTPS-transport-Microsoft-KKDCPP-implementation.patch858
-rw-r--r--0008-Load-custom-anchors-when-using-KKDCP.patch312
-rw-r--r--0009-Check-names-in-the-server-s-cert-when-using-KKDCP.patch562
-rw-r--r--0010-Add-some-longer-form-docs-for-HTTPS.patch86
-rw-r--r--0011-Have-k5test.py-provide-runenv-to-python-tests.patch64
-rw-r--r--0012-Add-a-simple-KDC-proxy-test-server.patch526
-rw-r--r--0013-Add-tests-for-MS-KKDCP-client-support.patch259
-rw-r--r--krb5-1.12-system-exts.patch2
-rw-r--r--krb5-1.12ish-kpasswd_tcp.patch (renamed from krb5-1.10-kpasswd_tcp.patch)10
-rw-r--r--krb5-1.12ish-tls-plugins.patch1680
-rw-r--r--krb5-master-move-otp-sockets.patch2
-rw-r--r--krb5.spec40
19 files changed, 7441 insertions, 8 deletions
diff --git a/0000-Refactor-cm-functions-in-sendto_kdc.c.patch b/0000-Refactor-cm-functions-in-sendto_kdc.c.patch
new file mode 100644
index 0000000..ff332b1
--- /dev/null
+++ b/0000-Refactor-cm-functions-in-sendto_kdc.c.patch
@@ -0,0 +1,436 @@
+Tweaked a bit to apply to 1.12:
+* krb5_int64 hadn't been replaced by int64_t yet.
+
+commit 346883c48f1b9e09b1af2cf73e3b96ee8f934072
+Author: Greg Hudson <ghudson@mit.edu>
+Date: Wed Mar 26 13:21:45 2014 -0400
+
+ Refactor cm functions in sendto_kdc.c
+
+ Move get_curtime_ms and the cm functions near the top of the file
+ right after structure definitions. Except for cm_select_or_poll,
+ define each cm function separately for poll and for select, since the
+ implementations don't share much in common. Instead of
+ cm_unset_write, define cm_read and cm_write functions to put an fd in
+ read-only or write-only state. Remove the ssflags argument from
+ cm_add_fd and just expect the caller to make a subsequent call to
+ cm_read or cm_write. Always select for exceptions when using select.
+ (Polling for exceptions is implicit with poll).
+
+ With these changes, we no longer select/poll for reading on a TCP
+ connection until we are done writing to it. So in service_tcp_fd,
+ remove the check for unexpected read events.
+
+diff --git a/src/lib/krb5/os/sendto_kdc.c b/src/lib/krb5/os/sendto_kdc.c
+index e60a375..e773a0a 100644
+--- a/src/lib/krb5/os/sendto_kdc.c
++++ b/src/lib/krb5/os/sendto_kdc.c
+@@ -59,8 +59,7 @@
+
+ typedef krb5_int64 time_ms;
+
+-/* Since fd_set is large on some platforms (8K on AIX 5.2), this probably
+- * shouldn't be allocated in automatic storage. */
++/* This can be pretty large, so should not be stack-allocated. */
+ struct select_state {
+ #ifdef USE_POLL
+ struct pollfd fds[MAX_POLLFDS];
+@@ -107,6 +106,183 @@ struct conn_state {
+ time_ms endtime;
+ };
+
++/* Get current time in milliseconds. */
++static krb5_error_code
++get_curtime_ms(time_ms *time_out)
++{
++ struct timeval tv;
++
++ if (gettimeofday(&tv, 0))
++ return errno;
++ *time_out = (time_ms)tv.tv_sec * 1000 + tv.tv_usec / 1000;
++ return 0;
++}
++
++#ifdef USE_POLL
++
++/* Find a pollfd in selstate by fd, or abort if we can't find it. */
++static inline struct pollfd *
++find_pollfd(struct select_state *selstate, int fd)
++{
++ int i;
++
++ for (i = 0; i < selstate->nfds; i++) {
++ if (selstate->fds[i].fd == fd)
++ return &selstate->fds[i];
++ }
++ abort();
++}
++
++static void
++cm_init_selstate(struct select_state *selstate)
++{
++ selstate->nfds = 0;
++}
++
++static krb5_boolean
++cm_add_fd(struct select_state *selstate, int fd)
++{
++ if (selstate->nfds >= MAX_POLLFDS)
++ return FALSE;
++ selstate->fds[selstate->nfds].fd = fd;
++ selstate->fds[selstate->nfds].events = 0;
++ selstate->nfds++;
++ return TRUE;
++}
++
++static void
++cm_remove_fd(struct select_state *selstate, int fd)
++{
++ struct pollfd *pfd = find_pollfd(selstate, fd);
++
++ *pfd = selstate->fds[selstate->nfds - 1];
++ selstate->nfds--;
++}
++
++/* Poll for reading (and not writing) on fd the next time we poll. */
++static void
++cm_read(struct select_state *selstate, int fd)
++{
++ find_pollfd(selstate, fd)->events = POLLIN;
++}
++
++/* Poll for writing (and not reading) on fd the next time we poll. */
++static void
++cm_write(struct select_state *selstate, int fd)
++{
++ find_pollfd(selstate, fd)->events = POLLOUT;
++}
++
++/* Get the output events for fd in the form of ssflags. */
++static unsigned int
++cm_get_ssflags(struct select_state *selstate, int fd)
++{
++ struct pollfd *pfd = find_pollfd(selstate, fd);
++
++ return ((pfd->revents & POLLIN) ? SSF_READ : 0) |
++ ((pfd->revents & POLLOUT) ? SSF_WRITE : 0) |
++ ((pfd->revents & POLLERR) ? SSF_EXCEPTION : 0);
++}
++
++#else /* not USE_POLL */
++
++static void
++cm_init_selstate(struct select_state *selstate)
++{
++ selstate->nfds = 0;
++ selstate->max = 0;
++ FD_ZERO(&selstate->rfds);
++ FD_ZERO(&selstate->wfds);
++ FD_ZERO(&selstate->xfds);
++}
++
++static krb5_boolean
++cm_add_fd(struct select_state *selstate, int fd)
++{
++#ifndef _WIN32 /* On Windows FD_SETSIZE is a count, not a max value. */
++ if (fd >= FD_SETSIZE)
++ return FALSE;
++#endif
++ FD_SET(fd, &selstate->xfds);
++ if (selstate->max <= fd)
++ selstate->max = fd + 1;
++ selstate->nfds++;
++ return TRUE;
++}
++
++static void
++cm_remove_fd(struct select_state *selstate, int fd)
++{
++ FD_CLR(fd, &selstate->rfds);
++ FD_CLR(fd, &selstate->wfds);
++ FD_CLR(fd, &selstate->xfds);
++ if (selstate->max == fd + 1) {
++ while (selstate->max > 0 &&
++ !FD_ISSET(selstate->max - 1, &selstate->rfds) &&
++ !FD_ISSET(selstate->max - 1, &selstate->wfds) &&
++ !FD_ISSET(selstate->max - 1, &selstate->xfds))
++ selstate->max--;
++ }
++ selstate->nfds--;
++}
++
++/* Select for reading (and not writing) on fd the next time we select. */
++static void
++cm_read(struct select_state *selstate, int fd)
++{
++ FD_SET(fd, &selstate->rfds);
++ FD_CLR(fd, &selstate->wfds);
++}
++
++/* Select for writing (and not reading) on fd the next time we select. */
++static void
++cm_write(struct select_state *selstate, int fd)
++{
++ FD_CLR(fd, &selstate->rfds);
++ FD_SET(fd, &selstate->wfds);
++}
++
++/* Get the events for fd from selstate after a select. */
++static unsigned int
++cm_get_ssflags(struct select_state *selstate, int fd)
++{
++ return (FD_ISSET(fd, &selstate->rfds) ? SSF_READ : 0) |
++ (FD_ISSET(fd, &selstate->wfds) ? SSF_WRITE : 0) |
++ (FD_ISSET(fd, &selstate->xfds) ? SSF_EXCEPTION : 0);
++}
++
++#endif /* not USE_POLL */
++
++static krb5_error_code
++cm_select_or_poll(const struct select_state *in, time_ms endtime,
++ struct select_state *out, int *sret)
++{
++#ifndef USE_POLL
++ struct timeval tv;
++#endif
++ krb5_error_code retval;
++ time_ms curtime, interval;
++
++ retval = get_curtime_ms(&curtime);
++ if (retval != 0)
++ return retval;
++ interval = (curtime < endtime) ? endtime - curtime : 0;
++
++ /* We don't need a separate copy of the selstate for poll, but use one for
++ * consistency with how we use select. */
++ *out = *in;
++
++#ifdef USE_POLL
++ *sret = poll(out->fds, out->nfds, interval);
++#else
++ tv.tv_sec = interval / 1000;
++ tv.tv_usec = interval % 1000 * 1000;
++ *sret = select(out->max, &out->rfds, &out->wfds, &out->xfds, &tv);
++#endif
++
++ return (*sret < 0) ? SOCKET_ERRNO : 0;
++}
++
+ static int
+ in_addrlist(struct server_entry *entry, struct serverlist *list)
+ {
+@@ -251,18 +427,6 @@ cleanup:
+ return retval;
+ }
+
+-/* Get current time in milliseconds. */
+-static krb5_error_code
+-get_curtime_ms(time_ms *time_out)
+-{
+- struct timeval tv;
+-
+- if (gettimeofday(&tv, 0))
+- return errno;
+- *time_out = (time_ms)tv.tv_sec * 1000 + tv.tv_usec / 1000;
+- return 0;
+-}
+-
+ /*
+ * Notes:
+ *
+@@ -283,144 +447,6 @@ get_curtime_ms(time_ms *time_out)
+ * connections already in progress
+ */
+
+-static void
+-cm_init_selstate(struct select_state *selstate)
+-{
+- selstate->nfds = 0;
+-#ifndef USE_POLL
+- selstate->max = 0;
+- FD_ZERO(&selstate->rfds);
+- FD_ZERO(&selstate->wfds);
+- FD_ZERO(&selstate->xfds);
+-#endif
+-}
+-
+-static krb5_boolean
+-cm_add_fd(struct select_state *selstate, int fd, unsigned int ssflags)
+-{
+-#ifdef USE_POLL
+- if (selstate->nfds >= MAX_POLLFDS)
+- return FALSE;
+- selstate->fds[selstate->nfds].fd = fd;
+- selstate->fds[selstate->nfds].events = 0;
+- if (ssflags & SSF_READ)
+- selstate->fds[selstate->nfds].events |= POLLIN;
+- if (ssflags & SSF_WRITE)
+- selstate->fds[selstate->nfds].events |= POLLOUT;
+-#else
+-#ifndef _WIN32 /* On Windows FD_SETSIZE is a count, not a max value. */
+- if (fd >= FD_SETSIZE)
+- return FALSE;
+-#endif
+- if (ssflags & SSF_READ)
+- FD_SET(fd, &selstate->rfds);
+- if (ssflags & SSF_WRITE)
+- FD_SET(fd, &selstate->wfds);
+- if (ssflags & SSF_EXCEPTION)
+- FD_SET(fd, &selstate->xfds);
+- if (selstate->max <= fd)
+- selstate->max = fd + 1;
+-#endif
+- selstate->nfds++;
+- return TRUE;
+-}
+-
+-static void
+-cm_remove_fd(struct select_state *selstate, int fd)
+-{
+-#ifdef USE_POLL
+- int i;
+-
+- /* Find the FD in the array and move the last entry to its place. */
+- assert(selstate->nfds > 0);
+- for (i = 0; i < selstate->nfds && selstate->fds[i].fd != fd; i++);
+- assert(i < selstate->nfds);
+- selstate->fds[i] = selstate->fds[selstate->nfds - 1];
+-#else
+- FD_CLR(fd, &selstate->rfds);
+- FD_CLR(fd, &selstate->wfds);
+- FD_CLR(fd, &selstate->xfds);
+- if (selstate->max == 1 + fd) {
+- while (selstate->max > 0
+- && ! FD_ISSET(selstate->max-1, &selstate->rfds)
+- && ! FD_ISSET(selstate->max-1, &selstate->wfds)
+- && ! FD_ISSET(selstate->max-1, &selstate->xfds))
+- selstate->max--;
+- }
+-#endif
+- selstate->nfds--;
+-}
+-
+-static void
+-cm_unset_write(struct select_state *selstate, int fd)
+-{
+-#ifdef USE_POLL
+- int i;
+-
+- for (i = 0; i < selstate->nfds && selstate->fds[i].fd != fd; i++);
+- assert(i < selstate->nfds);
+- selstate->fds[i].events &= ~POLLOUT;
+-#else
+- FD_CLR(fd, &selstate->wfds);
+-#endif
+-}
+-
+-static krb5_error_code
+-cm_select_or_poll(const struct select_state *in, time_ms endtime,
+- struct select_state *out, int *sret)
+-{
+-#ifndef USE_POLL
+- struct timeval tv;
+-#endif
+- krb5_error_code retval;
+- time_ms curtime, interval;
+-
+- retval = get_curtime_ms(&curtime);
+- if (retval != 0)
+- return retval;
+- interval = (curtime < endtime) ? endtime - curtime : 0;
+-
+- /* We don't need a separate copy of the selstate for poll, but use one for
+- * consistency with how we use select. */
+- *out = *in;
+-
+-#ifdef USE_POLL
+- *sret = poll(out->fds, out->nfds, interval);
+-#else
+- tv.tv_sec = interval / 1000;
+- tv.tv_usec = interval % 1000 * 1000;
+- *sret = select(out->max, &out->rfds, &out->wfds, &out->xfds, &tv);
+-#endif
+-
+- return (*sret < 0) ? SOCKET_ERRNO : 0;
+-}
+-
+-static unsigned int
+-cm_get_ssflags(struct select_state *selstate, int fd)
+-{
+- unsigned int ssflags = 0;
+-#ifdef USE_POLL
+- int i;
+-
+- for (i = 0; i < selstate->nfds && selstate->fds[i].fd != fd; i++);
+- assert(i < selstate->nfds);
+- if (selstate->fds[i].revents & POLLIN)
+- ssflags |= SSF_READ;
+- if (selstate->fds[i].revents & POLLOUT)
+- ssflags |= SSF_WRITE;
+- if (selstate->fds[i].revents & POLLERR)
+- ssflags |= SSF_EXCEPTION;
+-#else
+- if (FD_ISSET(fd, &selstate->rfds))
+- ssflags |= SSF_READ;
+- if (FD_ISSET(fd, &selstate->wfds))
+- ssflags |= SSF_WRITE;
+- if (FD_ISSET(fd, &selstate->xfds))
+- ssflags |= SSF_EXCEPTION;
+-#endif
+- return ssflags;
+-}
+-
+ static int service_tcp_fd(krb5_context context, struct conn_state *conn,
+ struct select_state *selstate, int ssflags);
+ static int service_udp_fd(krb5_context context, struct conn_state *conn,
+@@ -600,7 +626,6 @@ start_connection(krb5_context context, struct conn_state *state,
+ struct sendto_callback_info *callback_info)
+ {
+ int fd, e;
+- unsigned int ssflags;
+ static const int one = 1;
+ static const struct linger lopt = { 0, 0 };
+
+@@ -676,15 +701,17 @@ start_connection(krb5_context context, struct conn_state *state,
+ state->state = READING;
+ }
+ }
+- ssflags = SSF_READ | SSF_EXCEPTION;
+- if (state->state == CONNECTING || state->state == WRITING)
+- ssflags |= SSF_WRITE;
+- if (!cm_add_fd(selstate, state->fd, ssflags)) {
++
++ if (!cm_add_fd(selstate, state->fd)) {
+ (void) closesocket(state->fd);
+ state->fd = INVALID_SOCKET;
+ state->state = FAILED;
+ return -1;
+ }
++ if (state->state == CONNECTING || state->state == WRITING)
++ cm_write(selstate, state->fd);
++ else
++ cm_read(selstate, state->fd);
+
+ return 0;
+ }
+@@ -768,9 +795,8 @@ service_tcp_fd(krb5_context context, struct conn_state *conn,
+ ssize_t nwritten, nread;
+ SOCKET_WRITEV_TEMP tmp;
+
+- /* Check for a socket exception or readable data before we expect it. */
+- if (ssflags & SSF_EXCEPTION ||
+- ((ssflags & SSF_READ) && conn->state != READING))
++ /* Check for a socket exception. */
++ if (ssflags & SSF_EXCEPTION)
+ goto kill_conn;
+
+ switch (conn->state) {
+@@ -810,7 +836,7 @@ service_tcp_fd(krb5_context context, struct conn_state *conn,
+ }
+ if (conn->x.out.sg_count == 0) {
+ /* Done writing, switch to reading. */
+- cm_unset_write(selstate, conn->fd);
++ cm_read(selstate, conn->fd);
+ conn->state = READING;
+ conn->x.in.bufsizebytes_read = 0;
+ conn->x.in.bufsize = 0;
diff --git a/0001-Simplify-sendto_kdc.c.patch b/0001-Simplify-sendto_kdc.c.patch
new file mode 100644
index 0000000..51c512d
--- /dev/null
+++ b/0001-Simplify-sendto_kdc.c.patch
@@ -0,0 +1,421 @@
+From 42b3c2ed11c1e62c1691f868a6796983f93c3beb Mon Sep 17 00:00:00 2001
+From: Greg Hudson <ghudson@mit.edu>
+Date: Wed, 9 Apr 2014 13:19:03 -0400
+Subject: [PATCH 01/13] Simplify sendto_kdc.c
+
+* Get rid of the "x" member of conn_state, which used to be a union
+ but hasn't been since r14742.
+* Define a structure type for the "out" member of conn_state.
+* Rename incoming_krb5_message to incoming_message for brevity.
+* Make the "pos" member of incoming_message an offset instead of a
+ pointer, simplifying several present and future computations.
+* Use "in" and "out" aliases to the conn_state in and out members
+ where it improves brevity.
+* Rename set_conn_state_msg_length to set_transport_message and give
+ it a descriptive comment.
+* Call set_transport_message from start_connection only, instead of
+ once in add_connection and perhaps again in start_connection. To
+ make this possible, pass the original message argument to maybe_send
+ and start_connection.
+* Use make_data and empty_data helpers where appropriate.
+---
+ src/lib/krb5/os/sendto_kdc.c | 159 +++++++++++++++++++++----------------------
+ 1 file changed, 79 insertions(+), 80 deletions(-)
+
+diff --git a/src/lib/krb5/os/sendto_kdc.c b/src/lib/krb5/os/sendto_kdc.c
+index 67e2a60..5f781d3 100644
+--- a/src/lib/krb5/os/sendto_kdc.c
++++ b/src/lib/krb5/os/sendto_kdc.c
+@@ -76,30 +76,30 @@ static const char *const state_strings[] = {
+
+ /* connection states */
+ enum conn_states { INITIALIZING, CONNECTING, WRITING, READING, FAILED };
+-struct incoming_krb5_message {
++struct incoming_message {
+ size_t bufsizebytes_read;
+ size_t bufsize;
++ size_t pos;
+ char *buf;
+- char *pos;
+ unsigned char bufsizebytes[4];
+ size_t n_left;
+ };
+
++struct outgoing_message {
++ sg_buf sgbuf[2];
++ sg_buf *sgp;
++ int sg_count;
++ unsigned char msg_len_buf[4];
++};
++
+ struct conn_state {
+ SOCKET fd;
+ enum conn_states state;
+ int (*service)(krb5_context context, struct conn_state *,
+ struct select_state *, int);
+ struct remote_address addr;
+- struct {
+- struct {
+- sg_buf sgbuf[2];
+- sg_buf *sgp;
+- int sg_count;
+- unsigned char msg_len_buf[4];
+- } out;
+- struct incoming_krb5_message in;
+- } x;
++ struct incoming_message in;
++ struct outgoing_message out;
+ krb5_data callback_buffer;
+ size_t server_index;
+ struct conn_state *next;
+@@ -461,30 +461,31 @@ static int service_tcp_fd(krb5_context context, struct conn_state *conn,
+ static int service_udp_fd(krb5_context context, struct conn_state *conn,
+ struct select_state *selstate, int ssflags);
+
++/* 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_conn_state_msg_length (struct conn_state *state, const krb5_data *message)
++set_transport_message(struct conn_state *state, const krb5_data *message)
+ {
+- if (!message || message->length == 0)
++ struct outgoing_message *out = &state->out;
++
++ if (message == NULL || message->length == 0)
+ return;
+
+ if (state->addr.type == SOCK_STREAM) {
+- store_32_be(message->length, state->x.out.msg_len_buf);
+- SG_SET(&state->x.out.sgbuf[0], state->x.out.msg_len_buf, 4);
+- SG_SET(&state->x.out.sgbuf[1], message->data, message->length);
+- state->x.out.sg_count = 2;
+-
++ 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;
+ } else {
+-
+- SG_SET(&state->x.out.sgbuf[0], message->data, message->length);
+- SG_SET(&state->x.out.sgbuf[1], 0, 0);
+- state->x.out.sg_count = 1;
+-
++ SG_SET(&out->sgbuf[0], message->data, message->length);
++ SG_SET(&out->sgbuf[1], NULL, 0);
++ out->sg_count = 1;
+ }
+ }
+
+ static krb5_error_code
+ add_connection(struct conn_state **conns, struct addrinfo *ai,
+- size_t server_index, const krb5_data *message, char **udpbufp)
++ size_t server_index, char **udpbufp)
+ {
+ struct conn_state *state, **tailptr;
+
+@@ -492,28 +493,26 @@ add_connection(struct conn_state **conns, struct addrinfo *ai,
+ if (state == NULL)
+ return ENOMEM;
+ state->state = INITIALIZING;
+- state->x.out.sgp = state->x.out.sgbuf;
++ state->out.sgp = state->out.sgbuf;
+ state->addr.type = ai->ai_socktype;
+ state->addr.family = ai->ai_family;
+ state->addr.len = ai->ai_addrlen;
+ memcpy(&state->addr.saddr, ai->ai_addr, ai->ai_addrlen);
+ state->fd = INVALID_SOCKET;
+ state->server_index = server_index;
+- SG_SET(&state->x.out.sgbuf[1], 0, 0);
++ SG_SET(&state->out.sgbuf[1], NULL, 0);
+ if (ai->ai_socktype == SOCK_STREAM) {
+ state->service = service_tcp_fd;
+- set_conn_state_msg_length (state, message);
+ } else {
+ state->service = service_udp_fd;
+- set_conn_state_msg_length (state, message);
+
+ if (*udpbufp == NULL) {
+ *udpbufp = malloc(MAX_DGRAM_SIZE);
+ if (*udpbufp == 0)
+ return ENOMEM;
+ }
+- state->x.in.buf = *udpbufp;
+- state->x.in.bufsize = MAX_DGRAM_SIZE;
++ state->in.buf = *udpbufp;
++ state->in.bufsize = MAX_DGRAM_SIZE;
+ }
+
+ /* Chain the new state onto the tail of the list. */
+@@ -597,7 +596,7 @@ resolve_server(krb5_context context, const struct serverlist *servers,
+ ai.ai_family = entry->family;
+ ai.ai_addrlen = entry->addrlen;
+ ai.ai_addr = (struct sockaddr *)&entry->addr;
+- return add_connection(conns, &ai, ind, message, udpbufp);
++ return add_connection(conns, &ai, ind, udpbufp);
+ }
+
+ memset(&hint, 0, sizeof(hint));
+@@ -617,12 +616,12 @@ resolve_server(krb5_context context, const struct serverlist *servers,
+ /* Add each address with the preferred socktype. */
+ retval = 0;
+ for (a = addrs; a != 0 && retval == 0; a = a->ai_next)
+- retval = add_connection(conns, a, ind, message, udpbufp);
++ retval = add_connection(conns, a, ind, udpbufp);
+ if (retval == 0 && entry->socktype == 0 && socktype2 != 0) {
+ /* Add each address again with the non-preferred socktype. */
+ for (a = addrs; a != 0 && retval == 0; a = a->ai_next) {
+ a->ai_socktype = socktype2;
+- retval = add_connection(conns, a, ind, message, udpbufp);
++ retval = add_connection(conns, a, ind, udpbufp);
+ }
+ }
+ freeaddrinfo(addrs);
+@@ -631,7 +630,7 @@ resolve_server(krb5_context context, const struct serverlist *servers,
+
+ static int
+ start_connection(krb5_context context, struct conn_state *state,
+- struct select_state *selstate,
++ const krb5_data *message, struct select_state *selstate,
+ struct sendto_callback_info *callback_info)
+ {
+ int fd, e;
+@@ -689,13 +688,14 @@ start_connection(krb5_context context, struct conn_state *state,
+ return -3;
+ }
+
+- set_conn_state_msg_length(state, &state->callback_buffer);
++ message = &state->callback_buffer;
+ }
++ set_transport_message(state, message);
+
+ if (state->addr.type == SOCK_DGRAM) {
+ /* Send it now. */
+ ssize_t ret;
+- sg_buf *sg = &state->x.out.sgbuf[0];
++ sg_buf *sg = &state->out.sgbuf[0];
+
+ TRACE_SENDTO_KDC_UDP_SEND_INITIAL(context, &state->addr);
+ ret = send(state->fd, SG_BUF(sg), SG_LEN(sg), 0);
+@@ -731,14 +731,16 @@ start_connection(krb5_context context, struct conn_state *state,
+ next connection. */
+ static int
+ maybe_send(krb5_context context, struct conn_state *conn,
+- struct select_state *selstate,
++ const krb5_data *message, struct select_state *selstate,
+ struct sendto_callback_info *callback_info)
+ {
+ sg_buf *sg;
+ ssize_t ret;
+
+- if (conn->state == INITIALIZING)
+- return start_connection(context, conn, selstate, callback_info);
++ if (conn->state == INITIALIZING) {
++ return start_connection(context, conn, message, selstate,
++ callback_info);
++ }
+
+ /* Did we already shut down this channel? */
+ if (conn->state == FAILED) {
+@@ -752,7 +754,7 @@ maybe_send(krb5_context context, struct conn_state *conn,
+ }
+
+ /* UDP - retransmit after a previous attempt timed out. */
+- sg = &conn->x.out.sgbuf[0];
++ sg = &conn->out.sgbuf[0];
+ TRACE_SENDTO_KDC_UDP_SEND_RETRY(context, &conn->addr);
+ ret = send(conn->fd, SG_BUF(sg), SG_LEN(sg), 0);
+ if (ret < 0 || (size_t) ret != SG_LEN(sg)) {
+@@ -803,6 +805,8 @@ service_tcp_fd(krb5_context context, struct conn_state *conn,
+ int e = 0;
+ ssize_t nwritten, nread;
+ SOCKET_WRITEV_TEMP tmp;
++ struct incoming_message *in = &conn->in;
++ struct outgoing_message *out = &conn->out;
+
+ /* Check for a socket exception. */
+ if (ssflags & SSF_EXCEPTION)
+@@ -825,68 +829,68 @@ service_tcp_fd(krb5_context context, struct conn_state *conn,
+ /* Fall through. */
+ case WRITING:
+ TRACE_SENDTO_KDC_TCP_SEND(context, &conn->addr);
+- nwritten = SOCKET_WRITEV(conn->fd, conn->x.out.sgp,
+- conn->x.out.sg_count, tmp);
++ nwritten = SOCKET_WRITEV(conn->fd, out->sgp, out->sg_count, tmp);
+ if (nwritten < 0) {
+ TRACE_SENDTO_KDC_TCP_ERROR_SEND(context, &conn->addr,
+ SOCKET_ERRNO);
+ goto kill_conn;
+ }
+ while (nwritten) {
+- sg_buf *sgp = conn->x.out.sgp;
++ sg_buf *sgp = out->sgp;
+ if ((size_t) nwritten < SG_LEN(sgp)) {
+ SG_ADVANCE(sgp, (size_t) nwritten);
+ nwritten = 0;
+ } else {
+ nwritten -= SG_LEN(sgp);
+- conn->x.out.sgp++;
+- conn->x.out.sg_count--;
++ out->sgp++;
++ out->sg_count--;
+ }
+ }
+- if (conn->x.out.sg_count == 0) {
++ if (out->sg_count == 0) {
+ /* Done writing, switch to reading. */
+ cm_read(selstate, conn->fd);
+ conn->state = READING;
+- conn->x.in.bufsizebytes_read = 0;
+- conn->x.in.bufsize = 0;
+- conn->x.in.buf = 0;
+- conn->x.in.pos = 0;
+- conn->x.in.n_left = 0;
++ in->bufsizebytes_read = 0;
++ in->bufsize = 0;
++ in->pos = 0;
++ in->buf = NULL;
++ in->n_left = 0;
+ }
+ return 0;
+
+ case READING:
+- if (conn->x.in.bufsizebytes_read == 4) {
++ if (in->bufsizebytes_read == 4) {
+ /* Reading data. */
+- nread = SOCKET_READ(conn->fd, conn->x.in.pos, conn->x.in.n_left);
++ nread = SOCKET_READ(conn->fd, &in->buf[in->pos], in->n_left);
+ if (nread <= 0) {
+ e = nread ? SOCKET_ERRNO : ECONNRESET;
+ TRACE_SENDTO_KDC_TCP_ERROR_RECV(context, &conn->addr, e);
+ goto kill_conn;
+ }
+- conn->x.in.n_left -= nread;
+- conn->x.in.pos += nread;
+- if (conn->x.in.n_left <= 0)
++ in->n_left -= nread;
++ in->pos += nread;
++ if (in->n_left <= 0)
+ return 1;
+ } else {
+ /* Reading length. */
+ nread = SOCKET_READ(conn->fd,
+- conn->x.in.bufsizebytes + conn->x.in.bufsizebytes_read,
+- 4 - conn->x.in.bufsizebytes_read);
++ in->bufsizebytes + in->bufsizebytes_read,
++ 4 - in->bufsizebytes_read);
+ if (nread <= 0) {
+ e = nread ? SOCKET_ERRNO : ECONNRESET;
+ TRACE_SENDTO_KDC_TCP_ERROR_RECV_LEN(context, &conn->addr, e);
+ goto kill_conn;
+ }
+- conn->x.in.bufsizebytes_read += nread;
+- if (conn->x.in.bufsizebytes_read == 4) {
+- unsigned long len = load_32_be (conn->x.in.bufsizebytes);
++ in->bufsizebytes_read += nread;
++ if (in->bufsizebytes_read == 4) {
++ unsigned long len = load_32_be(in->bufsizebytes);
+ /* Arbitrary 1M cap. */
+ if (len > 1 * 1024 * 1024)
+ goto kill_conn;
+- conn->x.in.bufsize = conn->x.in.n_left = len;
+- conn->x.in.buf = conn->x.in.pos = malloc(len);
+- if (conn->x.in.buf == 0)
++ in->bufsize = in->n_left = len;
++ in->pos = 0;
++ in->buf = malloc(len);
++ if (in->buf == NULL)
+ goto kill_conn;
+ }
+ }
+@@ -915,13 +919,13 @@ service_udp_fd(krb5_context context, struct conn_state *conn,
+ if (conn->state != READING)
+ abort();
+
+- nread = recv(conn->fd, conn->x.in.buf, conn->x.in.bufsize, 0);
++ nread = recv(conn->fd, conn->in.buf, conn->in.bufsize, 0);
+ if (nread < 0) {
+ TRACE_SENDTO_KDC_UDP_ERROR_RECV(context, &conn->addr, SOCKET_ERRNO);
+ kill_conn(conn, selstate);
+ return 0;
+ }
+- conn->x.in.pos = conn->x.in.buf + nread;
++ conn->in.pos = nread;
+ return 1;
+ }
+
+@@ -986,10 +990,7 @@ service_fds(krb5_context context, struct select_state *selstate,
+ int stop = 1;
+
+ if (msg_handler != NULL) {
+- krb5_data reply;
+-
+- reply.data = state->x.in.buf;
+- reply.length = state->x.in.pos - state->x.in.buf;
++ krb5_data reply = make_data(state->in.buf, state->in.pos);
+
+ stop = (msg_handler(context, &reply, msg_handler_data) != 0);
+ }
+@@ -1051,8 +1052,7 @@ k5_sendto(krb5_context context, const krb5_data *message,
+ char *udpbuf = NULL;
+ krb5_boolean done = FALSE;
+
+- reply->data = 0;
+- reply->length = 0;
++ *reply = empty_data();
+
+ /* One for use here, listing all our fds in use, and one for
+ * temporary use in service_fds, for the fds of interest. */
+@@ -1077,7 +1077,7 @@ k5_sendto(krb5_context context, const krb5_data *message,
+ /* Contact each new connection whose socktype matches socktype1. */
+ if (state->addr.type != socktype1)
+ continue;
+- if (maybe_send(context, state, sel_state, callback_info))
++ if (maybe_send(context, state, message, sel_state, callback_info))
+ continue;
+ done = service_fds(context, sel_state, 1000, conns, seltemp,
+ msg_handler, msg_handler_data, &winner);
+@@ -1089,7 +1089,7 @@ k5_sendto(krb5_context context, const krb5_data *message,
+ for (state = conns; state != NULL && !done; state = state->next) {
+ if (state->addr.type != socktype2)
+ continue;
+- if (maybe_send(context, state, sel_state, callback_info))
++ if (maybe_send(context, state, message, sel_state, callback_info))
+ continue;
+ done = service_fds(context, sel_state, 1000, conns, seltemp,
+ msg_handler, msg_handler_data, &winner);
+@@ -1105,7 +1105,7 @@ 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, sel_state, callback_info))
++ if (maybe_send(context, state, message, sel_state, callback_info))
+ continue;
+ done = service_fds(context, sel_state, 1000, conns, seltemp,
+ msg_handler, msg_handler_data, &winner);
+@@ -1127,10 +1127,9 @@ k5_sendto(krb5_context context, const krb5_data *message,
+ goto cleanup;
+ }
+ /* Success! */
+- reply->data = winner->x.in.buf;
+- reply->length = winner->x.in.pos - winner->x.in.buf;
++ *reply = make_data(winner->in.buf, winner->in.pos);
+ retval = 0;
+- winner->x.in.buf = NULL;
++ winner->in.buf = NULL;
+ if (server_used != NULL)
+ *server_used = winner->server_index;
+ if (remoteaddr != NULL && remoteaddrlen != 0 && *remoteaddrlen > 0)
+@@ -1142,8 +1141,8 @@ cleanup:
+ next = state->next;
+ if (state->fd != INVALID_SOCKET)
+ closesocket(state->fd);
+- if (state->state == READING && state->x.in.buf != udpbuf)
+- free(state->x.in.buf);
++ if (state->state == READING && state->in.buf != udpbuf)
++ free(state->in.buf);
+ if (callback_info) {
+ callback_info->pfn_cleanup(callback_info->data,
+ &state->callback_buffer);
+--
+2.1.0
+
diff --git a/0002-Add-helper-to-determine-if-a-KDC-is-the-master.patch b/0002-Add-helper-to-determine-if-a-KDC-is-the-master.patch
new file mode 100644
index 0000000..50ae55b
--- /dev/null
+++ b/0002-Add-helper-to-determine-if-a-KDC-is-the-master.patch
@@ -0,0 +1,233 @@
+From f4b1a7e7b80ce68e57912edcd48c39ea62c73e43 Mon Sep 17 00:00:00 2001
+From: Greg Hudson <ghudson@mit.edu>
+Date: Sun, 6 Apr 2014 18:06:14 -0400
+Subject: [PATCH 02/13] Add helper to determine if a KDC is the master
+
+Add a new function k5_kdc_is_master in locate_kdc.c to determine
+whether a KDC matches one of the masters, and use it in
+krb5_sendto_kdc.
+---
+ src/lib/krb5/os/locate_kdc.c | 110 +++++++++++++++++++++++++++++--------------
+ src/lib/krb5/os/os-proto.h | 3 ++
+ src/lib/krb5/os/sendto_kdc.c | 31 +-----------
+ 3 files changed, 80 insertions(+), 64 deletions(-)
+
+diff --git a/src/lib/krb5/os/locate_kdc.c b/src/lib/krb5/os/locate_kdc.c
+index 88d55a8..4479465 100644
+--- a/src/lib/krb5/os/locate_kdc.c
++++ b/src/lib/krb5/os/locate_kdc.c
+@@ -166,6 +166,24 @@ add_host_to_list(struct serverlist *list, const char *hostname, int port,
+ return 0;
+ }
+
++/* Return true if server is identical to an entry in list. */
++static krb5_boolean
++server_list_contains(struct serverlist *list, struct server_entry *server)
++{
++ struct server_entry *ent;
++
++ for (ent = list->servers; ent < list->servers + list->nservers; ent++) {
++ if (server->hostname != NULL && ent->hostname != NULL &&
++ strcmp(server->hostname, ent->hostname) == 0)
++ return TRUE;
++ if (server->hostname == NULL && ent->hostname == NULL &&
++ server->addrlen == ent->addrlen &&
++ memcmp(&server->addr, &ent->addr, server->addrlen) == 0)
++ return TRUE;
++ }
++ return FALSE;
++}
++
+ static krb5_error_code
+ locate_srv_conf_1(krb5_context context, const krb5_data *realm,
+ const char * name, struct serverlist *serverlist,
+@@ -529,6 +547,41 @@ dns_locate_server(krb5_context context, const krb5_data *realm,
+ }
+ #endif /* KRB5_DNS_LOOKUP */
+
++static krb5_error_code
++locate_server(krb5_context context, const krb5_data *realm,
++ struct serverlist *serverlist, enum locate_service_type svc,
++ int socktype)
++{
++ krb5_error_code ret;
++ struct serverlist list = SERVERLIST_INIT;
++
++ *serverlist = list;
++
++ /* Try modules. If a module returns 0 but leaves the list empty, return an
++ * empty list. */
++ ret = module_locate_server(context, realm, &list, svc, socktype);
++ if (ret != KRB5_PLUGIN_NO_HANDLE)
++ goto done;
++
++ /* Try the profile. Fall back to DNS if it returns an empty list. */
++ ret = prof_locate_server(context, realm, &list, svc, socktype);
++ if (ret)
++ goto done;
++
++#ifdef KRB5_DNS_LOOKUP
++ if (list.nservers == 0)
++ ret = dns_locate_server(context, realm, &list, svc, socktype);
++#endif
++
++done:
++ if (ret) {
++ k5_free_serverlist(&list);
++ return ret;
++ }
++ *serverlist = list;
++ return 0;
++}
++
+ /*
+ * Wrapper function for the various backends
+ */
+@@ -538,54 +591,26 @@ k5_locate_server(krb5_context context, const krb5_data *realm,
+ struct serverlist *serverlist, enum locate_service_type svc,
+ int socktype)
+ {
+- krb5_error_code code;
+- struct serverlist al = SERVERLIST_INIT;
+-
+- *serverlist = al;
++ krb5_error_code ret;
+
++ memset(serverlist, 0, sizeof(*serverlist));
+ if (realm == NULL || realm->data == NULL || realm->data[0] == 0) {
+ krb5_set_error_message(context, KRB5_REALM_CANT_RESOLVE,
+ "Cannot find KDC for invalid realm name \"\"");
+ return KRB5_REALM_CANT_RESOLVE;
+ }
+
+- code = module_locate_server(context, realm, &al, svc, socktype);
+- Tprintf("module_locate_server returns %d\n", code);
+- if (code == KRB5_PLUGIN_NO_HANDLE) {
+- /*
+- * We always try the local file before DNS. Note that there
+- * is no way to indicate "service not available" via the
+- * config file.
+- */
+-
+- code = prof_locate_server(context, realm, &al, svc, socktype);
+-
+-#ifdef KRB5_DNS_LOOKUP
+- if (code == 0 && al.nservers == 0)
+- code = dns_locate_server(context, realm, &al, svc, socktype);
+-#endif /* KRB5_DNS_LOOKUP */
++ ret = locate_server(context, realm, serverlist, svc, socktype);
++ if (ret)
++ return ret;
+
+- /* We could put more heuristics here, like looking up a hostname
+- of "kerberos."+REALM, etc. */
+- }
+- if (code == 0)
+- Tprintf ("krb5int_locate_server found %d addresses\n",
+- al.nservers);
+- else
+- Tprintf ("krb5int_locate_server returning error code %d/%s\n",
+- code, error_message(code));
+- if (code != 0) {
+- k5_free_serverlist(&al);
+- return code;
+- }
+- if (al.nservers == 0) { /* No good servers */
+- k5_free_serverlist(&al);
++ if (serverlist->nservers == 0) {
++ k5_free_serverlist(serverlist);
+ krb5_set_error_message(context, KRB5_REALM_UNKNOWN,
+ _("Cannot find KDC for realm \"%.*s\""),
+ realm->length, realm->data);
+ return KRB5_REALM_UNKNOWN;
+ }
+- *serverlist = al;
+ return 0;
+ }
+
+@@ -598,3 +623,18 @@ k5_locate_kdc(krb5_context context, const krb5_data *realm,
+ stype = get_masters ? locate_service_master_kdc : locate_service_kdc;
+ return k5_locate_server(context, realm, serverlist, stype, socktype);
+ }
++
++krb5_boolean
++k5_kdc_is_master(krb5_context context, const krb5_data *realm,
++ struct server_entry *server)
++{
++ struct serverlist list;
++ krb5_boolean found;
++
++ if (locate_server(context, realm, &list, locate_service_master_kdc,
++ server->socktype) != 0)
++ return FALSE;
++ found = server_list_contains(&list, server);
++ k5_free_serverlist(&list);
++ return found;
++}
+diff --git a/src/lib/krb5/os/os-proto.h b/src/lib/krb5/os/os-proto.h
+index c6b730f..9125ba0 100644
+--- a/src/lib/krb5/os/os-proto.h
++++ b/src/lib/krb5/os/os-proto.h
+@@ -76,6 +76,9 @@ krb5_error_code k5_locate_kdc(krb5_context context, const krb5_data *realm,
+ struct serverlist *serverlist, int get_masters,
+ int socktype);
+
++krb5_boolean k5_kdc_is_master(krb5_context context, const krb5_data *realm,
++ struct server_entry *server);
++
+ void k5_free_serverlist(struct serverlist *);
+
+ #ifdef HAVE_NETINET_IN_H
+diff --git a/src/lib/krb5/os/sendto_kdc.c b/src/lib/krb5/os/sendto_kdc.c
+index 5f781d3..e3855a3 100644
+--- a/src/lib/krb5/os/sendto_kdc.c
++++ b/src/lib/krb5/os/sendto_kdc.c
+@@ -293,25 +293,6 @@ cm_select_or_poll(const struct select_state *in, time_ms endtime,
+ }
+
+ static int
+-in_addrlist(struct server_entry *entry, struct serverlist *list)
+-{
+- size_t i;
+- struct server_entry *le;
+-
+- for (i = 0; i < list->nservers; i++) {
+- le = &list->servers[i];
+- if (entry->hostname != NULL && le->hostname != NULL &&
+- strcmp(entry->hostname, le->hostname) == 0)
+- return 1;
+- if (entry->hostname == NULL && le->hostname == NULL &&
+- entry->addrlen == le->addrlen &&
+- memcmp(&entry->addr, &le->addr, entry->addrlen) == 0)
+- return 1;
+- }
+- return 0;
+-}
+-
+-static int
+ check_for_svc_unavailable (krb5_context context,
+ const krb5_data *reply,
+ void *msg_handler_data)
+@@ -418,17 +399,9 @@ krb5_sendto_kdc(krb5_context context, const krb5_data *message,
+ /* Set use_master to 1 if we ended up talking to a master when we didn't
+ * explicitly request to. */
+ if (*use_master == 0) {
+- struct serverlist mservers;
+- struct server_entry *entry = &servers.servers[server_used];
+- retval = k5_locate_kdc(context, realm, &mservers, TRUE,
+- entry->socktype);
+- if (retval == 0) {
+- if (in_addrlist(entry, &mservers))
+- *use_master = 1;
+- k5_free_serverlist(&mservers);
+- }
++ *use_master = k5_kdc_is_master(context, realm,
++ &servers.servers[server_used]);
+ TRACE_SENDTO_KDC_MASTER(context, *use_master);
+- retval = 0;
+ }
+
+ cleanup:
+--
+2.1.0
+
diff --git a/0003-Use-k5_transport-_strategy-enums-for-k5_sendto.patch b/0003-Use-k5_transport-_strategy-enums-for-k5_sendto.patch
new file mode 100644
index 0000000..ba3f91c
--- /dev/null
+++ b/0003-Use-k5_transport-_strategy-enums-for-k5_sendto.patch
@@ -0,0 +1,913 @@
+From 9c6be00daca0b80aed94ec9680724f95e6be92e1 Mon Sep 17 00:00:00 2001
+From: "Robbie Harwood (frozencemetery)" <rharwood@club.cc.cmu.edu>
+Date: Thu, 15 Aug 2013 15:55:52 -0400
+Subject: [PATCH 03/13] Use k5_transport(_strategy) enums for k5_sendto
+
+In k5_sendto and k5_locate_server, replace "socktype" parameters with
+a new enumerator k5_transport, so that we can add new transports which
+are not in the socket type namespace. Control the order in which we
+make connections of different types using a new k5_transport_strategy
+enumerator, to simplify the logic for adding new transports later.
+Control the result of k5_locate_server with a no_udp boolean rather
+than a socket type.
+
+[ghudson@mit.edu: renamed type to k5_transport; k5_locate_server
+ no_udp change; clarified commit message; fix for Solaris getaddrinfo]
+[kaduk@mit.edu: name variables of type k5_transport 'transport']
+[nalin@redhat.com: use transport rather than sock_type in more places,
+ add and use k5_transport_strategy, update the test program]
+
+ticket: 7929
+---
+ src/lib/krb5/os/changepw.c | 31 +++++-----
+ src/lib/krb5/os/hostrealm_domain.c | 2 +-
+ src/lib/krb5/os/locate_kdc.c | 75 ++++++++++++----------
+ src/lib/krb5/os/os-proto.h | 27 +++++---
+ src/lib/krb5/os/sendto_kdc.c | 123 +++++++++++++++++++++++--------------
+ src/lib/krb5/os/t_locate_kdc.c | 24 ++++----
+ src/lib/krb5/os/t_std_conf.c | 2 +-
+ src/lib/krb5/os/t_trace.c | 6 +-
+ src/lib/krb5/os/t_trace.ref | 4 +-
+ src/lib/krb5/os/trace.c | 6 +-
+ 10 files changed, 178 insertions(+), 122 deletions(-)
+
+diff --git a/src/lib/krb5/os/changepw.c b/src/lib/krb5/os/changepw.c
+index 4d8abd9..a1c9885 100644
+--- a/src/lib/krb5/os/changepw.c
++++ b/src/lib/krb5/os/changepw.c
+@@ -59,25 +59,25 @@ struct sendto_callback_context {
+
+ static krb5_error_code
+ locate_kpasswd(krb5_context context, const krb5_data *realm,
+- struct serverlist *serverlist, int socktype)
++ struct serverlist *serverlist, krb5_boolean no_udp)
+ {
+ krb5_error_code code;
+
+ code = k5_locate_server(context, realm, serverlist, locate_service_kpasswd,
+- socktype);
++ no_udp);
+
+ if (code == KRB5_REALM_CANT_RESOLVE || code == KRB5_REALM_UNKNOWN) {
+ code = k5_locate_server(context, realm, serverlist,
+- locate_service_kadmin, SOCK_STREAM);
++ locate_service_kadmin, TRUE);
+ if (!code) {
+- /* Success with admin_server but now we need to change the
+- port number to use DEFAULT_KPASSWD_PORT and the socktype. */
++ /* Success with admin_server but now we need to change the port
++ * number to use DEFAULT_KPASSWD_PORT and the transport. */
+ size_t i;
+ for (i = 0; i < serverlist->nservers; i++) {
+ struct server_entry *s = &serverlist->servers[i];
+ krb5_ui_2 kpasswd_port = htons(DEFAULT_KPASSWD_PORT);
+- if (socktype != SOCK_STREAM)
+- s->socktype = socktype;
++ if (!no_udp && s->transport == TCP)
++ s->transport = TCP_OR_UDP;
+ if (s->hostname != NULL)
+ s->port = kpasswd_port;
+ else if (s->family == AF_INET)
+@@ -214,7 +214,7 @@ change_set_password(krb5_context context,
+ krb5_data *result_string)
+ {
+ krb5_data chpw_rep;
+- krb5_boolean use_tcp = 0;
++ krb5_boolean no_udp = FALSE;
+ GETSOCKNAME_ARG3_TYPE addrlen;
+ krb5_error_code code = 0;
+ char *code_string;
+@@ -247,9 +247,10 @@ change_set_password(krb5_context context,
+ callback_ctx.local_seq_num = callback_ctx.auth_context->local_seq_number;
+
+ do {
+- int socktype = (use_tcp ? SOCK_STREAM : SOCK_DGRAM);
++ k5_transport_strategy strategy = no_udp ? NO_UDP : UDP_FIRST;
++
+ code = locate_kpasswd(callback_ctx.context, &creds->server->realm, &sl,
+- socktype);
++ no_udp);
+ if (code)
+ break;
+
+@@ -260,7 +261,7 @@ change_set_password(krb5_context context,
+ callback_info.pfn_cleanup = kpasswd_sendto_msg_cleanup;
+ krb5_free_data_contents(callback_ctx.context, &chpw_rep);
+
+- code = k5_sendto(callback_ctx.context, NULL, &sl, socktype, 0,
++ code = k5_sendto(callback_ctx.context, NULL, &sl, strategy,
+ &callback_info, &chpw_rep, ss2sa(&remote_addr),
+ &addrlen, NULL, NULL, NULL);
+ if (code) {
+@@ -277,9 +278,9 @@ change_set_password(krb5_context context,
+ result_string);
+
+ if (code) {
+- if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG && !use_tcp) {
++ if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG && !no_udp) {
+ k5_free_serverlist(&sl);
+- use_tcp = 1;
++ no_udp = 1;
+ continue;
+ }
+
+@@ -305,9 +306,9 @@ change_set_password(krb5_context context,
+ strncpy(result_code_string->data, code_string, result_code_string->length);
+ }
+
+- if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG && !use_tcp) {
++ if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG && !no_udp) {
+ k5_free_serverlist(&sl);
+- use_tcp = 1;
++ no_udp = 1;
+ } else {
+ break;
+ }
+diff --git a/src/lib/krb5/os/hostrealm_domain.c b/src/lib/krb5/os/hostrealm_domain.c
+index dc9cc59..2228df0 100644
+--- a/src/lib/krb5/os/hostrealm_domain.c
++++ b/src/lib/krb5/os/hostrealm_domain.c
+@@ -85,7 +85,7 @@ domain_fallback_realm(krb5_context context, krb5_hostrealm_moddata data,
+ suffix = uhost;
+ while (limit-- >= 0 && (dot = strchr(suffix, '.')) != NULL) {
+ drealm = string2data((char *)suffix);
+- if (k5_locate_kdc(context, &drealm, &slist, FALSE, SOCK_DGRAM) == 0) {
++ if (k5_locate_kdc(context, &drealm, &slist, FALSE, FALSE) == 0) {
+ k5_free_serverlist(&slist);
+ ret = k5_make_realmlist(suffix, realms_out);
+ goto cleanup;
+diff --git a/src/lib/krb5/os/locate_kdc.c b/src/lib/krb5/os/locate_kdc.c
+index 4479465..4c8aead 100644
+--- a/src/lib/krb5/os/locate_kdc.c
++++ b/src/lib/krb5/os/locate_kdc.c
+@@ -129,7 +129,7 @@ new_server_entry(struct serverlist *list)
+
+ /* Add an address entry to list. */
+ static int
+-add_addr_to_list(struct serverlist *list, int socktype, int family,
++add_addr_to_list(struct serverlist *list, k5_transport transport, int family,
+ size_t addrlen, struct sockaddr *addr)
+ {
+ struct server_entry *entry;
+@@ -137,7 +137,7 @@ add_addr_to_list(struct serverlist *list, int socktype, int family,
+ entry = new_server_entry(list);
+ if (entry == NULL)
+ return ENOMEM;
+- entry->socktype = socktype;
++ entry->transport = transport;
+ entry->family = family;
+ entry->hostname = NULL;
+ entry->addrlen = addrlen;
+@@ -149,14 +149,14 @@ add_addr_to_list(struct serverlist *list, int socktype, int family,
+ /* Add a hostname entry to list. */
+ static int
+ add_host_to_list(struct serverlist *list, const char *hostname, int port,
+- int socktype, int family)
++ k5_transport transport, int family)
+ {
+ struct server_entry *entry;
+
+ entry = new_server_entry(list);
+ if (entry == NULL)
+ return ENOMEM;
+- entry->socktype = socktype;
++ entry->transport = transport;
+ entry->family = family;
+ entry->hostname = strdup(hostname);
+ if (entry->hostname == NULL)
+@@ -187,7 +187,7 @@ server_list_contains(struct serverlist *list, struct server_entry *server)
+ static krb5_error_code
+ locate_srv_conf_1(krb5_context context, const krb5_data *realm,
+ const char * name, struct serverlist *serverlist,
+- int socktype, int udpport, int sec_udpport)
++ k5_transport transport, int udpport, int sec_udpport)
+ {
+ const char *realm_srv_names[4];
+ char **hostlist, *host, *port, *cp;
+@@ -255,12 +255,12 @@ locate_srv_conf_1(krb5_context context, const krb5_data *realm,
+ *cp = '\0';
+ }
+
+- code = add_host_to_list(serverlist, host, p1, socktype, AF_UNSPEC);
++ code = add_host_to_list(serverlist, host, p1, transport, AF_UNSPEC);
+ /* 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 &&
+- (socktype == 0 || socktype == SOCK_DGRAM))
+- code = add_host_to_list(serverlist, host, p2, SOCK_DGRAM, AF_INET);
++ (transport == TCP_OR_UDP || transport == UDP))
++ code = add_host_to_list(serverlist, host, p2, UDP, AF_INET);
+ if (code)
+ goto cleanup;
+ }
+@@ -278,7 +278,8 @@ krb5_locate_srv_conf(krb5_context context, const krb5_data *realm,
+ {
+ krb5_error_code ret;
+
+- ret = locate_srv_conf_1(context, realm, name, al, 0, udpport, sec_udpport);
++ ret = locate_srv_conf_1(context, realm, name, al, TCP_OR_UDP, udpport,
++ sec_udpport);
+ if (ret)
+ return ret;
+ if (al->nservers == 0) /* Couldn't resolve any KDC names */
+@@ -294,7 +295,7 @@ locate_srv_dns_1(const krb5_data *realm, const char *service,
+ {
+ struct srv_dns_entry *head = NULL, *entry = NULL;
+ krb5_error_code code = 0;
+- int socktype;
++ k5_transport transport;
+
+ code = krb5int_make_srv_query_realm(realm, service, protocol, &head);
+ if (code)
+@@ -310,9 +311,9 @@ locate_srv_dns_1(const krb5_data *realm, const char *service,
+ }
+
+ for (entry = head; entry != NULL; entry = entry->next) {
+- socktype = (strcmp(protocol, "_tcp") == 0) ? SOCK_STREAM : SOCK_DGRAM;
++ transport = (strcmp(protocol, "_tcp") == 0) ? TCP : UDP;
+ code = add_host_to_list(serverlist, entry->host, htons(entry->port),
+- socktype, AF_UNSPEC);
++ transport, AF_UNSPEC);
+ if (code)
+ goto cleanup;
+ }
+@@ -341,6 +342,7 @@ module_callback(void *cbdata, int socktype, struct sockaddr *sa)
+ {
+ struct module_callback_data *d = cbdata;
+ size_t addrlen;
++ k5_transport transport;
+
+ if (socktype != SOCK_STREAM && socktype != SOCK_DGRAM)
+ return 0;
+@@ -350,7 +352,8 @@ module_callback(void *cbdata, int socktype, struct sockaddr *sa)
+ addrlen = sizeof(struct sockaddr_in6);
+ else
+ return 0;
+- if (add_addr_to_list(d->list, socktype, sa->sa_family, addrlen,
++ transport = (socktype == SOCK_STREAM) ? TCP : UDP;
++ if (add_addr_to_list(d->list, transport, sa->sa_family, addrlen,
+ sa) != 0) {
+ /* Assumes only error is ENOMEM. */
+ d->out_of_mem = 1;
+@@ -362,14 +365,14 @@ module_callback(void *cbdata, int socktype, struct sockaddr *sa)
+ static krb5_error_code
+ module_locate_server(krb5_context ctx, const krb5_data *realm,
+ struct serverlist *serverlist,
+- enum locate_service_type svc, int socktype)
++ enum locate_service_type svc, k5_transport transport)
+ {
+ struct krb5plugin_service_locate_result *res = NULL;
+ krb5_error_code code;
+ struct krb5plugin_service_locate_ftable *vtbl = NULL;
+ void **ptrs;
+ char *realmz; /* NUL-terminated realm */
+- int i;
++ int socktype, i;
+ struct module_callback_data cbdata = { 0, };
+ const char *msg;
+
+@@ -413,11 +416,11 @@ module_locate_server(krb5_context ctx, const krb5_data *realm,
+ if (code)
+ continue;
+
+- code = vtbl->lookup(blob, svc, realmz,
+- (socktype != 0) ? socktype : SOCK_DGRAM, AF_UNSPEC,
++ socktype = (transport == TCP) ? SOCK_STREAM : SOCK_DGRAM;
++ code = vtbl->lookup(blob, svc, realmz, socktype, AF_UNSPEC,
+ module_callback, &cbdata);
+ /* Also ask for TCP addresses if we got UDP addresses and want both. */
+- if (code == 0 && socktype == 0) {
++ if (code == 0 && transport == TCP_OR_UDP) {
+ code = vtbl->lookup(blob, svc, realmz, SOCK_STREAM, AF_UNSPEC,
+ module_callback, &cbdata);
+ if (code == KRB5_PLUGIN_NO_HANDLE)
+@@ -459,7 +462,7 @@ module_locate_server(krb5_context ctx, const krb5_data *realm,
+ static krb5_error_code
+ prof_locate_server(krb5_context context, const krb5_data *realm,
+ struct serverlist *serverlist, enum locate_service_type svc,
+- int socktype)
++ k5_transport transport)
+ {
+ const char *profname;
+ int dflport1, dflport2 = 0;
+@@ -495,7 +498,7 @@ prof_locate_server(krb5_context context, const krb5_data *realm,
+ return EBUSY; /* XXX */
+ }
+
+- return locate_srv_conf_1(context, realm, profname, serverlist, socktype,
++ return locate_srv_conf_1(context, realm, profname, serverlist, transport,
+ dflport1, dflport2);
+ }
+
+@@ -503,7 +506,7 @@ prof_locate_server(krb5_context context, const krb5_data *realm,
+ static krb5_error_code
+ dns_locate_server(krb5_context context, const krb5_data *realm,
+ struct serverlist *serverlist, enum locate_service_type svc,
+- int socktype)
++ k5_transport transport)
+ {
+ const char *dnsname;
+ int use_dns = _krb5_use_dns_kdc(context);
+@@ -533,12 +536,12 @@ dns_locate_server(krb5_context context, const krb5_data *realm,
+ }
+
+ code = 0;
+- if (socktype == SOCK_DGRAM || socktype == 0) {
++ if (transport == UDP || transport == TCP_OR_UDP) {
+ code = locate_srv_dns_1(realm, dnsname, "_udp", serverlist);
+ if (code)
+ Tprintf("dns udp lookup returned error %d\n", code);
+ }
+- if ((socktype == SOCK_STREAM || socktype == 0) && code == 0) {
++ if ((transport == TCP || transport == TCP_OR_UDP) && code == 0) {
+ code = locate_srv_dns_1(realm, dnsname, "_tcp", serverlist);
+ if (code)
+ Tprintf("dns tcp lookup returned error %d\n", code);
+@@ -547,10 +550,16 @@ dns_locate_server(krb5_context context, const krb5_data *realm,
+ }
+ #endif /* KRB5_DNS_LOOKUP */
+
++/*
++ * Try all of the server location methods in sequence. transport must be
++ * TCP_OR_UDP, TCP, or UDP. It is applied to hostname entries in the profile
++ * and affects whether we query modules or DNS for UDP or TCP or both, but does
++ * not restrict a method from returning entries of other transports.
++ */
+ static krb5_error_code
+ locate_server(krb5_context context, const krb5_data *realm,
+ struct serverlist *serverlist, enum locate_service_type svc,
+- int socktype)
++ k5_transport transport)
+ {
+ krb5_error_code ret;
+ struct serverlist list = SERVERLIST_INIT;
+@@ -559,18 +568,18 @@ locate_server(krb5_context context, const krb5_data *realm,
+
+ /* Try modules. If a module returns 0 but leaves the list empty, return an
+ * empty list. */
+- ret = module_locate_server(context, realm, &list, svc, socktype);
++ ret = module_locate_server(context, realm, &list, svc, transport);
+ if (ret != KRB5_PLUGIN_NO_HANDLE)
+ goto done;
+
+ /* Try the profile. Fall back to DNS if it returns an empty list. */
+- ret = prof_locate_server(context, realm, &list, svc, socktype);
++ ret = prof_locate_server(context, realm, &list, svc, transport);
+ if (ret)
+ goto done;
+
+ #ifdef KRB5_DNS_LOOKUP
+ if (list.nservers == 0)
+- ret = dns_locate_server(context, realm, &list, svc, socktype);
++ ret = dns_locate_server(context, realm, &list, svc, transport);
+ #endif
+
+ done:
+@@ -589,9 +598,10 @@ done:
+ krb5_error_code
+ k5_locate_server(krb5_context context, const krb5_data *realm,
+ struct serverlist *serverlist, enum locate_service_type svc,
+- int socktype)
++ krb5_boolean no_udp)
+ {
+ krb5_error_code ret;
++ k5_transport transport = no_udp ? TCP : TCP_OR_UDP;
+
+ memset(serverlist, 0, sizeof(*serverlist));
+ if (realm == NULL || realm->data == NULL || realm->data[0] == 0) {
+@@ -600,7 +610,7 @@ k5_locate_server(krb5_context context, const krb5_data *realm,
+ return KRB5_REALM_CANT_RESOLVE;
+ }
+
+- ret = locate_server(context, realm, serverlist, svc, socktype);
++ ret = locate_server(context, realm, serverlist, svc, transport);
+ if (ret)
+ return ret;
+
+@@ -616,12 +626,13 @@ k5_locate_server(krb5_context context, const krb5_data *realm,
+
+ krb5_error_code
+ k5_locate_kdc(krb5_context context, const krb5_data *realm,
+- struct serverlist *serverlist, int get_masters, int socktype)
++ struct serverlist *serverlist, krb5_boolean get_masters,
++ krb5_boolean no_udp)
+ {
+ enum locate_service_type stype;
+
+ stype = get_masters ? locate_service_master_kdc : locate_service_kdc;
+- return k5_locate_server(context, realm, serverlist, stype, socktype);
++ return k5_locate_server(context, realm, serverlist, stype, no_udp);
+ }
+
+ krb5_boolean
+@@ -632,7 +643,7 @@ k5_kdc_is_master(krb5_context context, const krb5_data *realm,
+ krb5_boolean found;
+
+ if (locate_server(context, realm, &list, locate_service_master_kdc,
+- server->socktype) != 0)
++ server->transport) != 0)
+ return FALSE;
+ found = server_list_contains(&list, server);
+ k5_free_serverlist(&list);
+diff --git a/src/lib/krb5/os/os-proto.h b/src/lib/krb5/os/os-proto.h
+index 9125ba0..3196bca 100644
+--- a/src/lib/krb5/os/os-proto.h
++++ b/src/lib/krb5/os/os-proto.h
+@@ -38,11 +38,23 @@
+
+ #include <krb5/locate_plugin.h>
+
++typedef enum {
++ TCP_OR_UDP = 0,
++ TCP,
++ UDP,
++} k5_transport;
++
++typedef enum {
++ UDP_FIRST = 0,
++ UDP_LAST,
++ NO_UDP,
++} k5_transport_strategy;
++
+ /* A single server hostname or address. */
+ struct server_entry {
+ char *hostname; /* NULL -> use addrlen/addr instead */
+ int port; /* Used only if hostname set */
+- int socktype; /* May be 0 for UDP/TCP if hostname set */
++ k5_transport transport; /* May be 0 for UDP/TCP if hostname set */
+ int family; /* May be 0 (aka AF_UNSPEC) if hostname set */
+ size_t addrlen;
+ struct sockaddr_storage addr;
+@@ -56,8 +68,8 @@ struct serverlist {
+ #define SERVERLIST_INIT { NULL, 0 }
+
+ struct remote_address {
++ k5_transport transport;
+ int family;
+- int type;
+ socklen_t len;
+ struct sockaddr_storage saddr;
+ };
+@@ -69,12 +81,13 @@ struct sendto_callback_info {
+ };
+
+ krb5_error_code k5_locate_server(krb5_context, const krb5_data *realm,
+- struct serverlist *,
+- enum locate_service_type svc, int socktype);
++ struct serverlist *serverlist,
++ enum locate_service_type svc,
++ krb5_boolean no_udp);
+
+ krb5_error_code k5_locate_kdc(krb5_context context, const krb5_data *realm,
+- struct serverlist *serverlist, int get_masters,
+- int socktype);
++ struct serverlist *serverlist,
++ krb5_boolean get_masters, krb5_boolean no_udp);
+
+ krb5_boolean k5_kdc_is_master(krb5_context context, const krb5_data *realm,
+ struct server_entry *server);
+@@ -103,7 +116,7 @@ int _krb5_conf_boolean (const char *);
+
+ krb5_error_code k5_sendto(krb5_context context, const krb5_data *message,
+ const struct serverlist *addrs,
+- int socktype1, int socktype2,
++ k5_transport_strategy strategy,
+ struct sendto_callback_info *callback_info,
+ krb5_data *reply, struct sockaddr *remoteaddr,
+ socklen_t *remoteaddrlen, int *server_used,
+diff --git a/src/lib/krb5/os/sendto_kdc.c b/src/lib/krb5/os/sendto_kdc.c
+index e3855a3..3f99ce8 100644
+--- a/src/lib/krb5/os/sendto_kdc.c
++++ b/src/lib/krb5/os/sendto_kdc.c
+@@ -104,6 +104,7 @@ struct conn_state {
+ size_t server_index;
+ struct conn_state *next;
+ time_ms endtime;
++ krb5_boolean defer;
+ };
+
+ /* Get current time in milliseconds. */
+@@ -293,6 +294,19 @@ cm_select_or_poll(const struct select_state *in, time_ms endtime,
+ }
+
+ static int
++socktype_for_transport(k5_transport transport)
++{
++ switch (transport) {
++ case UDP:
++ return SOCK_DGRAM;
++ case TCP:
++ return SOCK_STREAM;
++ default:
++ return 0;
++ }
++}
++
++static int
+ check_for_svc_unavailable (krb5_context context,
+ const krb5_data *reply,
+ void *msg_handler_data)
+@@ -330,11 +344,12 @@ check_for_svc_unavailable (krb5_context context,
+ krb5_error_code
+ krb5_sendto_kdc(krb5_context context, const krb5_data *message,
+ const krb5_data *realm, krb5_data *reply, int *use_master,
+- int tcp_only)
++ int no_udp)
+ {
+ krb5_error_code retval, err;
+ struct serverlist servers;
+- int socktype1 = 0, socktype2 = 0, server_used;
++ int server_used;
++ k5_transport_strategy strategy;
+
+ /*
+ * find KDC location(s) for realm
+@@ -349,9 +364,9 @@ krb5_sendto_kdc(krb5_context context, const krb5_data *message,
+ * should probably be returned as well.
+ */
+
+- TRACE_SENDTO_KDC(context, message->length, realm, *use_master, tcp_only);
++ TRACE_SENDTO_KDC(context, message->length, realm, *use_master, no_udp);
+
+- if (!tcp_only && context->udp_pref_limit < 0) {
++ if (!no_udp && context->udp_pref_limit < 0) {
+ int tmp;
+ retval = profile_get_integer(context->profile,
+ KRB5_CONF_LIBDEFAULTS, KRB5_CONF_UDP_PREFERENCE_LIMIT, 0,
+@@ -368,22 +383,21 @@ krb5_sendto_kdc(krb5_context context, const krb5_data *message,
+ context->udp_pref_limit = tmp;
+ }
+
+- if (tcp_only)
+- socktype1 = SOCK_STREAM, socktype2 = 0;
++ if (no_udp)
++ strategy = NO_UDP;
+ else if (message->length <= (unsigned int) context->udp_pref_limit)
+- socktype1 = SOCK_DGRAM, socktype2 = SOCK_STREAM;
++ strategy = UDP_FIRST;
+ else
+- socktype1 = SOCK_STREAM, socktype2 = SOCK_DGRAM;
++ strategy = UDP_LAST;
+
+- retval = k5_locate_kdc(context, realm, &servers, *use_master,
+- tcp_only ? SOCK_STREAM : 0);
++ retval = k5_locate_kdc(context, realm, &servers, *use_master, no_udp);
+ if (retval)
+ return retval;
+
+ err = 0;
+- retval = k5_sendto(context, message, &servers, socktype1, socktype2,
+- NULL, reply, NULL, NULL, &server_used,
+- check_for_svc_unavailable, &err);
++ retval = k5_sendto(context, message, &servers, strategy, NULL, reply,
++ NULL, NULL, &server_used, check_for_svc_unavailable,
++ &err);
+ if (retval == KRB5_KDC_UNREACH) {
+ if (err == KDC_ERR_SVC_UNAVAILABLE) {
+ retval = KRB5KDC_ERR_SVC_UNAVAILABLE;
+@@ -444,7 +458,7 @@ set_transport_message(struct conn_state *state, const krb5_data *message)
+ if (message == NULL || message->length == 0)
+ return;
+
+- if (state->addr.type == SOCK_STREAM) {
++ 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);
+@@ -457,8 +471,9 @@ set_transport_message(struct conn_state *state, const krb5_data *message)
+ }
+
+ static krb5_error_code
+-add_connection(struct conn_state **conns, struct addrinfo *ai,
+- size_t server_index, char **udpbufp)
++add_connection(struct conn_state **conns, k5_transport transport,
++ krb5_boolean defer, struct addrinfo *ai, size_t server_index,
++ char **udpbufp)
+ {
+ struct conn_state *state, **tailptr;
+
+@@ -467,14 +482,15 @@ add_connection(struct conn_state **conns, struct addrinfo *ai,
+ return ENOMEM;
+ state->state = INITIALIZING;
+ state->out.sgp = state->out.sgbuf;
+- state->addr.type = ai->ai_socktype;
++ state->addr.transport = transport;
+ state->addr.family = ai->ai_family;
+ state->addr.len = ai->ai_addrlen;
+ memcpy(&state->addr.saddr, ai->ai_addr, ai->ai_addrlen);
++ state->defer = defer;
+ state->fd = INVALID_SOCKET;
+ state->server_index = server_index;
+ SG_SET(&state->out.sgbuf[1], NULL, 0);
+- if (ai->ai_socktype == SOCK_STREAM) {
++ if (transport == TCP) {
+ state->service = service_tcp_fd;
+ } else {
+ state->service = service_udp_fd;
+@@ -549,32 +565,41 @@ translate_ai_error (int err)
+ */
+ static krb5_error_code
+ resolve_server(krb5_context context, const struct serverlist *servers,
+- size_t ind, int socktype1, int socktype2,
++ 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];
++ k5_transport transport;
+ struct addrinfo *addrs, *a, hint, ai;
++ krb5_boolean defer;
+ int err, result;
+ char portbuf[64];
+
+- /* Skip any stray entries of socktypes we don't want. */
+- if (entry->socktype != 0 && entry->socktype != socktype1 &&
+- entry->socktype != socktype2)
++ /* Skip UDP entries if we don't want UDP. */
++ if (strategy == NO_UDP && entry->transport == UDP)
+ return 0;
+
++ transport = (strategy == UDP_FIRST) ? UDP : TCP;
+ if (entry->hostname == NULL) {
+- ai.ai_socktype = entry->socktype;
++ /* Added by a module, so transport is either TCP or UDP. */
++ ai.ai_socktype = socktype_for_transport(entry->transport);
+ ai.ai_family = entry->family;
+ ai.ai_addrlen = entry->addrlen;
+ ai.ai_addr = (struct sockaddr *)&entry->addr;
+- return add_connection(conns, &ai, ind, udpbufp);
++ defer = (entry->transport != transport);
++ return add_connection(conns, entry->transport, defer, &ai, ind,
++ udpbufp);
+ }
+
++ /* If the entry has a specified transport, use it. */
++ if (entry->transport != TCP_OR_UDP)
++ transport = entry->transport;
++
+ memset(&hint, 0, sizeof(hint));
+ hint.ai_family = entry->family;
+- hint.ai_socktype = (entry->socktype != 0) ? entry->socktype : socktype1;
++ hint.ai_socktype = socktype_for_transport(transport);
+ hint.ai_flags = AI_ADDRCONFIG;
+ #ifdef AI_NUMERICSERV
+ hint.ai_flags |= AI_NUMERICSERV;
+@@ -586,15 +611,19 @@ resolve_server(krb5_context context, const struct serverlist *servers,
+ err = getaddrinfo(entry->hostname, portbuf, &hint, &addrs);
+ if (err)
+ return translate_ai_error(err);
+- /* Add each address with the preferred socktype. */
++
++ /* 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, a, ind, udpbufp);
+- if (retval == 0 && entry->socktype == 0 && socktype2 != 0) {
+- /* Add each address again with the non-preferred socktype. */
++ retval = add_connection(conns, transport, FALSE, a, ind, udpbufp);
++
++ /* For TCP_OR_UDP entries, add each address again with the non-preferred
++ * transport, unless we are avoiding UDP. Flag these as deferred. */
++ if (retval == 0 && entry->transport == TCP_OR_UDP && strategy != NO_UDP) {
++ transport = (strategy == UDP_FIRST) ? TCP : UDP;
+ for (a = addrs; a != 0 && retval == 0; a = a->ai_next) {
+- a->ai_socktype = socktype2;
+- retval = add_connection(conns, a, ind, udpbufp);
++ a->ai_socktype = socktype_for_transport(transport);
++ retval = add_connection(conns, transport, TRUE, a, ind, udpbufp);
+ }
+ }
+ freeaddrinfo(addrs);
+@@ -606,17 +635,18 @@ start_connection(krb5_context context, struct conn_state *state,
+ const krb5_data *message, struct select_state *selstate,
+ struct sendto_callback_info *callback_info)
+ {
+- int fd, e;
++ int fd, e, type;
+ static const int one = 1;
+ static const struct linger lopt = { 0, 0 };
+
+- fd = socket(state->addr.family, state->addr.type, 0);
++ type = socktype_for_transport(state->addr.transport);
++ fd = socket(state->addr.family, type, 0);
+ if (fd == INVALID_SOCKET)
+ return -1; /* try other hosts */
+ set_cloexec_fd(fd);
+ /* Make it non-blocking. */
+ ioctlsocket(fd, FIONBIO, (const void *) &one);
+- if (state->addr.type == SOCK_STREAM) {
++ if (state->addr.transport == TCP) {
+ setsockopt(fd, SOL_SOCKET, SO_LINGER, &lopt, sizeof(lopt));
+ TRACE_SENDTO_KDC_TCP_CONNECT(context, &state->addr);
+ }
+@@ -665,7 +695,7 @@ start_connection(krb5_context context, struct conn_state *state,
+ }
+ set_transport_message(state, message);
+
+- if (state->addr.type == SOCK_DGRAM) {
++ if (state->addr.transport == UDP) {
+ /* Send it now. */
+ ssize_t ret;
+ sg_buf *sg = &state->out.sgbuf[0];
+@@ -720,7 +750,7 @@ maybe_send(krb5_context context, struct conn_state *conn,
+ return -1;
+ }
+
+- if (conn->addr.type == SOCK_STREAM) {
++ if (conn->addr.transport != UDP) {
+ /* The select callback will handle flushing any data we
+ haven't written yet, and we only write it once. */
+ return -1;
+@@ -910,7 +940,7 @@ get_endtime(time_ms endtime, struct conn_state *conns)
+ struct conn_state *state;
+
+ for (state = conns; state != NULL; state = state->next) {
+- if (state->addr.type == SOCK_STREAM &&
++ if (state->addr.transport == TCP &&
+ (state->state == READING || state->state == WRITING) &&
+ state->endtime > endtime)
+ endtime = state->endtime;
+@@ -1008,7 +1038,7 @@ service_fds(krb5_context context, struct select_state *selstate,
+
+ krb5_error_code
+ k5_sendto(krb5_context context, const krb5_data *message,
+- const struct serverlist *servers, int socktype1, int socktype2,
++ const struct serverlist *servers, k5_transport_strategy strategy,
+ struct sendto_callback_info* callback_info, krb5_data *reply,
+ struct sockaddr *remoteaddr, socklen_t *remoteaddrlen,
+ int *server_used,
+@@ -1038,17 +1068,18 @@ k5_sendto(krb5_context context, const krb5_data *message,
+ cm_init_selstate(sel_state);
+
+ /* First pass: resolve server hosts, communicate with resulting addresses
+- * of the preferred socktype, and wait 1s for an answer from each. */
++ * of the preferred transport, and wait 1s for an answer from each. */
+ 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, socktype1, socktype2,
+- message, &udpbuf, &conns);
++ retval = resolve_server(context, servers, s, strategy, message,
++ &udpbuf, &conns);
+ if (retval)
+ goto cleanup;
+ for (state = *tailptr; state != NULL && !done; state = state->next) {
+- /* Contact each new connection whose socktype matches socktype1. */
+- if (state->addr.type != socktype1)
++ /* Contact each new connection, deferring those which use the
++ * non-preferred RFC 4120 transport. */
++ if (state->defer)
+ continue;
+ if (maybe_send(context, state, message, sel_state, callback_info))
+ continue;
+@@ -1057,10 +1088,10 @@ k5_sendto(krb5_context context, const krb5_data *message,
+ }
+ }
+
+- /* Complete the first pass by contacting servers of the non-preferred
+- * socktype (if given), waiting 1s for an answer from each. */
++ /* Complete the first pass by contacting servers of the non-preferred RFC
++ * 4120 transport (if given), waiting 1s for an answer from each. */
+ for (state = conns; state != NULL && !done; state = state->next) {
+- if (state->addr.type != socktype2)
++ if (!state->defer)
+ continue;
+ if (maybe_send(context, state, message, sel_state, callback_info))
+ continue;
+diff --git a/src/lib/krb5/os/t_locate_kdc.c b/src/lib/krb5/os/t_locate_kdc.c
+index 5453a4c..300aa71 100644
+--- a/src/lib/krb5/os/t_locate_kdc.c
++++ b/src/lib/krb5/os/t_locate_kdc.c
+@@ -29,18 +29,18 @@ kfatal (krb5_error_code err)
+ }
+
+ static const char *
+-stypename (int stype)
++ttypename (k5_transport ttype)
+ {
+ static char buf[20];
+- switch (stype) {
+- case SOCK_STREAM:
+- return "stream";
+- case SOCK_DGRAM:
+- return "dgram";
+- case SOCK_RAW:
+- return "raw";
++ switch (ttype) {
++ case TCP_OR_UDP:
++ return "tcp or udp";
++ case TCP:
++ return "tcp";
++ case UDP:
++ return "udp";
+ default:
+- snprintf(buf, sizeof(buf), "?%d", stype);
++ snprintf(buf, sizeof(buf), "?%d", ttype);
+ return buf;
+ }
+ }
+@@ -58,7 +58,7 @@ print_addrs (void)
+
+ if (entry->hostname != NULL) {
+ printf("%2d: host %s\t%s\tport %d\n", (int)i, entry->hostname,
+- stypename(entry->socktype), ntohs(entry->port));
++ ttypename(entry->transport), ntohs(entry->port));
+ continue;
+ }
+ err = getnameinfo((struct sockaddr *)&entry->addr, entry->addrlen,
+@@ -69,7 +69,7 @@ print_addrs (void)
+ gai_strerror(err));
+ } else {
+ printf("%2d: address %s\t%s\tport %s\n", (int)i, hostbuf,
+- stypename(entry->socktype), srvbuf);
++ ttypename(entry->transport), srvbuf);
+ }
+ }
+ }
+@@ -129,7 +129,7 @@ main (int argc, char *argv[])
+ break;
+
+ case LOOKUP_WHATEVER:
+- err = k5_locate_kdc(ctx, &realm, &sl, master, 0);
++ err = k5_locate_kdc(ctx, &realm, &sl, master, FALSE);
+ break;
+ }
+ if (err) kfatal (err);
+diff --git a/src/lib/krb5/os/t_std_conf.c b/src/lib/krb5/os/t_std_conf.c
+index e2ff572..6ee54d5 100644
+--- a/src/lib/krb5/os/t_std_conf.c
++++ b/src/lib/krb5/os/t_std_conf.c
+@@ -82,7 +82,7 @@ test_locate_kdc(krb5_context ctx, char *realm)
+
+ rlm.data = realm;
+ rlm.length = strlen(realm);
+- retval = k5_locate_kdc(ctx, &rlm, &servers, get_masters, 0);
++ retval = k5_locate_kdc(ctx, &rlm, &servers, get_masters, FALSE);
+ if (retval) {
+ com_err("krb5_locate_kdc", retval, 0);
+ return;
+diff --git a/src/lib/krb5/os/t_trace.c b/src/lib/krb5/os/t_trace.c
+index 36044f5..4cb2bd0 100644
+--- a/src/lib/krb5/os/t_trace.c
++++ b/src/lib/krb5/os/t_trace.c
+@@ -112,7 +112,7 @@ main (int argc, char *argv[])
+ TRACE(ctx, "size_t and const char *, as four-character hex hash: "
+ "{hashlenstr}", 1, NULL);
+
+- ra.type = SOCK_STREAM;
++ ra.transport = TCP;
+ addr_in = (struct sockaddr_in *)&ra.saddr;
+ addr_in->sin_family = AF_INET;
+ addr_in->sin_addr.s_addr = INADDR_ANY;
+@@ -121,10 +121,10 @@ main (int argc, char *argv[])
+ ra.family = AF_INET;
+ TRACE(ctx, "struct remote_address *, show socket type, address, port: "
+ "{raddr}", &ra);
+- ra.type = SOCK_DGRAM;
++ ra.transport = UDP;
+ TRACE(ctx, "struct remote_address *, show socket type, address, port: "
+ "{raddr}", &ra);
+- ra.type = 1234;
++ ra.transport = 1234;
+ addr_in->sin_family = AF_UNSPEC;
+ ra.family = AF_UNSPEC;
+ TRACE(ctx, "struct remote_address *, show socket type, address, port: "
+diff --git a/src/lib/krb5/os/t_trace.ref b/src/lib/krb5/os/t_trace.ref
+index 749d9c9..ca5818a 100644
+--- a/src/lib/krb5/os/t_trace.ref
++++ b/src/lib/krb5/os/t_trace.ref
+@@ -10,8 +10,8 @@ size_t and const char *, as four-character hex hash: 7B9A
+ size_t and const char *, as four-character hex hash: (null)
+ struct remote_address *, show socket type, address, port: stream 0.0.0.0:88
+ struct remote_address *, show socket type, address, port: dgram 0.0.0.0:88
+-struct remote_address *, show socket type, address, port: socktype1234 AF_UNSPEC
+-struct remote_address *, show socket type, address, port: socktype1234 af5678
++struct remote_address *, show socket type, address, port: transport1234 AF_UNSPEC
++struct remote_address *, show socket type, address, port: transport1234 af5678
+ krb5_data *, display as counted string: example.data
+ krb5_data *, display as counted string: (null)
+ krb5_data *, display as hex bytes: 6578616D706C652E64617461
+diff --git a/src/lib/krb5/os/trace.c b/src/lib/krb5/os/trace.c
+index 525742c..8319a86 100644
+--- a/src/lib/krb5/os/trace.c
++++ b/src/lib/krb5/os/trace.c
+@@ -197,12 +197,12 @@ trace_format(krb5_context context, const char *fmt, va_list ap)
+ }
+ } else if (strcmp(tmpbuf, "raddr") == 0) {
+ ra = va_arg(ap, struct remote_address *);
+- if (ra->type == SOCK_DGRAM)
++ if (ra->transport == UDP)
+ k5_buf_add(&buf, "dgram");
+- else if (ra->type == SOCK_STREAM)
++ else if (ra->transport == TCP)
+ k5_buf_add(&buf, "stream");
+ else
+- k5_buf_add_fmt(&buf, "socktype%d", ra->type);
++ k5_buf_add_fmt(&buf, "transport%d", ra->transport);
+
+ if (getnameinfo((struct sockaddr *)&ra->saddr, ra->len,
+ addrbuf, sizeof(addrbuf), portbuf, sizeof(portbuf),
+--
+2.1.0
+
diff --git a/0004-Build-support-for-TLS-used-by-HTTPS-proxy-support.patch b/0004-Build-support-for-TLS-used-by-HTTPS-proxy-support.patch
new file mode 100644
index 0000000..df4707d
--- /dev/null
+++ b/0004-Build-support-for-TLS-used-by-HTTPS-proxy-support.patch
@@ -0,0 +1,187 @@
+From d0be57ac45ea639baa3cff0dd2108c34e834bfa7 Mon Sep 17 00:00:00 2001
+From: "Robbie Harwood (frozencemetery)" <rharwood@club.cc.cmu.edu>
+Date: Fri, 16 Aug 2013 12:45:03 -0400
+Subject: [PATCH 04/13] Build support for TLS used by HTTPS proxy support
+
+Add a --with-proxy-tls-impl option to configure, taking 'openssl',
+'auto', or invocation as --without-proxy-tls-impl. Use related CFLAGS
+when building lib/krb5/os, and LIBS when linking libkrb5. Call the
+OpenSSL library startup functions during library initialization.
+
+ticket: 7929
+---
+ src/Makefile.in | 1 +
+ src/config/pre.in | 5 +++++
+ src/configure.in | 40 ++++++++++++++++++++++++++++++++++++++++
+ src/lib/krb5/Makefile.in | 3 ++-
+ src/lib/krb5/krb5_libinit.c | 2 ++
+ src/lib/krb5/os/Makefile.in | 2 +-
+ src/lib/krb5/os/os-proto.h | 1 +
+ src/lib/krb5/os/sendto_kdc.c | 14 ++++++++++++++
+ 8 files changed, 66 insertions(+), 2 deletions(-)
+
+diff --git a/src/Makefile.in b/src/Makefile.in
+index 1725093..5e2cf4e 100644
+--- a/src/Makefile.in
++++ b/src/Makefile.in
+@@ -553,6 +553,7 @@ pyrunenv.vals: Makefile
+ for i in $(RUN_VARS); do \
+ eval echo 'env['\\\'$$i\\\''] = '\\\'\$$$$i\\\'; \
+ done > $@
++ echo "proxy_tls_impl = '$(PROXY_TLS_IMPL)'" >> $@
+
+ runenv.py: pyrunenv.vals
+ echo 'env = {}' > $@
+diff --git a/src/config/pre.in b/src/config/pre.in
+index fbc5c11..e1d7e4b 100644
+--- a/src/config/pre.in
++++ b/src/config/pre.in
+@@ -428,6 +428,11 @@ PKINIT_CRYPTO_IMPL = @PKINIT_CRYPTO_IMPL@
+ PKINIT_CRYPTO_IMPL_CFLAGS = @PKINIT_CRYPTO_IMPL_CFLAGS@
+ PKINIT_CRYPTO_IMPL_LIBS = @PKINIT_CRYPTO_IMPL_LIBS@
+
++# TLS implementation selection for HTTPS proxy support
++PROXY_TLS_IMPL = @PROXY_TLS_IMPL@
++PROXY_TLS_IMPL_CFLAGS = @PROXY_TLS_IMPL_CFLAGS@
++PROXY_TLS_IMPL_LIBS = @PROXY_TLS_IMPL_LIBS@
++
+ # error table rules
+ #
+ ### /* these are invoked as $(...) foo.et, which works, but could be better */
+diff --git a/src/configure.in b/src/configure.in
+index 9bc4663..39e3738 100644
+--- a/src/configure.in
++++ b/src/configure.in
+@@ -272,6 +272,46 @@ AC_SUBST(PKINIT_CRYPTO_IMPL)
+ AC_SUBST(PKINIT_CRYPTO_IMPL_CFLAGS)
+ AC_SUBST(PKINIT_CRYPTO_IMPL_LIBS)
+
++# WITH_PROXY_TLS_IMPL
++
++AC_ARG_WITH([proxy-tls-impl],
++AC_HELP_STRING([--with-proxy-tls-impl=IMPL],
++ [use specified TLS implementation for HTTPS @<:@auto@:>@]),
++[PROXY_TLS_IMPL=$withval],[PROXY_TLS_IMPL=auto])
++case "$PROXY_TLS_IMPL" in
++openssl|auto)
++ AC_CHECK_LIB(ssl,SSL_CTX_new,[have_lib_ssl=true],[have_lib_ssl=false],
++ -lcrypto)
++ AC_MSG_CHECKING([for OpenSSL])
++ if test x$have_lib_ssl = xtrue ; then
++ AC_DEFINE(PROXY_TLS_IMPL_OPENSSL,1,
++ [Define if HTTPS TLS implementation is OpenSSL])
++ AC_MSG_RESULT([yes])
++ PROXY_TLS_IMPL_LIBS="-lssl -lcrypto"
++ PROXY_TLS_IMPL=openssl
++ AC_MSG_NOTICE(HTTPS support will use TLS from '$PROXY_TLS_IMPL')
++ else
++ if test "$PROXY_TLS_IMPL" = openssl ; then
++ AC_MSG_ERROR([OpenSSL not found!])
++ else
++ AC_MSG_WARN([OpenSSL not found!])
++ fi
++ PROXY_TLS_IMPL=no
++ AC_MSG_NOTICE(building without HTTPS support)
++ fi
++ ;;
++no)
++ AC_MSG_NOTICE(building without HTTPS support)
++ ;;
++*)
++ AC_MSG_ERROR([Unsupported HTTPS proxy TLS implementation $withval])
++ ;;
++esac
++
++AC_SUBST(PROXY_TLS_IMPL)
++AC_SUBST(PROXY_TLS_IMPL_CFLAGS)
++AC_SUBST(PROXY_TLS_IMPL_LIBS)
++
+ AC_ARG_ENABLE([aesni],
+ AC_HELP_STRING([--disable-aesni],[Do not build with AES-NI support]), ,
+ enable_aesni=check)
+diff --git a/src/lib/krb5/Makefile.in b/src/lib/krb5/Makefile.in
+index d9cddc1..472c008 100644
+--- a/src/lib/krb5/Makefile.in
++++ b/src/lib/krb5/Makefile.in
+@@ -56,7 +56,8 @@ RELDIR=krb5
+ SHLIB_EXPDEPS = \
+ $(TOPLIBD)/libk5crypto$(SHLIBEXT) \
+ $(COM_ERR_DEPLIB) $(SUPPORT_DEPLIB)
+-SHLIB_EXPLIBS=-lk5crypto -lcom_err $(SUPPORT_LIB) @GEN_LIB@ $(LIBS)
++SHLIB_EXPLIBS=-lk5crypto -lcom_err $(PROXY_TLS_IMPL_LIBS) $(SUPPORT_LIB) \
++ @GEN_LIB@ $(LIBS)
+
+ all-unix:: all-liblinks
+
+diff --git a/src/lib/krb5/krb5_libinit.c b/src/lib/krb5/krb5_libinit.c
+index f83d25b..f2382d1 100644
+--- a/src/lib/krb5/krb5_libinit.c
++++ b/src/lib/krb5/krb5_libinit.c
+@@ -58,6 +58,8 @@ 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 5add9f9..fb4001a 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=-I$(top_srcdir)/util/profile
++LOCALINCLUDES= $(PROXY_TLS_IMPL_CFLAGS) -I$(top_srcdir)/util/profile
+
+ ##DOS##BUILDTOP = ..\..\..
+ ##DOS##PREFIXDIR=os
+diff --git a/src/lib/krb5/os/os-proto.h b/src/lib/krb5/os/os-proto.h
+index 3196bca..f23dda5 100644
+--- a/src/lib/krb5/os/os-proto.h
++++ b/src/lib/krb5/os/os-proto.h
+@@ -184,5 +184,6 @@ 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 3f99ce8..c6aae8e 100644
+--- a/src/lib/krb5/os/sendto_kdc.c
++++ b/src/lib/krb5/os/sendto_kdc.c
+@@ -48,6 +48,10 @@
+ #endif
+ #endif
+
++#ifdef PROXY_TLS_IMPL_OPENSSL
++#include <openssl/ssl.h>
++#endif
++
+ #define MAX_PASS 3
+ #define DEFAULT_UDP_PREF_LIMIT 1465
+ #define HARD_UDP_LIMIT 32700 /* could probably do 64K-epsilon ? */
+@@ -107,6 +111,16 @@ struct conn_state {
+ krb5_boolean defer;
+ };
+
++void
++k5_sendto_kdc_initialize(void)
++{
++#ifdef PROXY_TLS_IMPL_OPENSSL
++ SSL_library_init();
++ SSL_load_error_strings();
++ OpenSSL_add_all_algorithms();
++#endif
++}
++
+ /* Get current time in milliseconds. */
+ static krb5_error_code
+ get_curtime_ms(time_ms *time_out)
+--
+2.1.0
+
diff --git a/0005-Add-ASN.1-codec-for-KKDCP-s-KDC-PROXY-MESSAGE.patch b/0005-Add-ASN.1-codec-for-KKDCP-s-KDC-PROXY-MESSAGE.patch
new file mode 100644
index 0000000..f650d69
--- /dev/null
+++ b/0005-Add-ASN.1-codec-for-KKDCP-s-KDC-PROXY-MESSAGE.patch
@@ -0,0 +1,352 @@
+From bb89afd7c59deea855d2818fe36ef7472b4abf2e Mon Sep 17 00:00:00 2001
+From: Nathaniel McCallum <npmccallum@redhat.com>
+Date: Mon, 9 Sep 2013 14:23:56 -0400
+Subject: [PATCH 05/13] Add ASN.1 codec for KKDCP's KDC-PROXY-MESSAGE
+
+Handle encoding and decoding [MS-KKDCP] proxy messages, including
+handling of the additional length bytes. Early versions of [MS-KKDCP]
+incorrectly omit that the size of the proxied message is prepended to
+the proxied message, as it is when we're using plain TCP, before
+encoding the proxy-message structure. This is fixed at least as of
+version 2.1 of the spec.
+
+[nalin@redhat.com: add tests]
+
+ticket: 7929
+---
+ src/include/k5-int.h | 13 +++++++++++++
+ src/lib/krb5/asn.1/asn1_k_encode.c | 14 ++++++++++++++
+ src/lib/krb5/krb/kfree.c | 10 ++++++++++
+ src/lib/krb5/libkrb5.exports | 3 +++
+ src/tests/asn.1/krb5_decode_test.c | 18 ++++++++++++++++++
+ src/tests/asn.1/krb5_encode_test.c | 8 ++++++++
+ src/tests/asn.1/ktest.c | 23 ++++++++++++++++++++++
+ src/tests/asn.1/ktest.h | 5 +++++
+ src/tests/asn.1/ktest_equal.c | 12 ++++++++++++
+ src/tests/asn.1/ktest_equal.h | 3 +++
+ src/tests/asn.1/reference_encode.out | 1 +
+ src/tests/asn.1/trval_reference.out | 37 ++++++++++++++++++++++++++++++++++++
+ 12 files changed, 147 insertions(+)
+
+diff --git a/src/include/k5-int.h b/src/include/k5-int.h
+index 096cd14..8f039ee 100644
+--- a/src/include/k5-int.h
++++ b/src/include/k5-int.h
+@@ -518,6 +518,12 @@ typedef struct _krb5_pa_otp_req {
+ krb5_data vendor;
+ } krb5_pa_otp_req;
+
++typedef struct _krb5_kkdcp_message {
++ krb5_data kerb_message;
++ krb5_data target_domain;
++ krb5_int32 dclocator_hint;
++} krb5_kkdcp_message;
++
+ #include <stdlib.h>
+ #include <string.h>
+
+@@ -898,6 +904,7 @@ void k5_free_otp_tokeninfo(krb5_context context, krb5_otp_tokeninfo *val);
+ void k5_free_pa_otp_challenge(krb5_context context,
+ krb5_pa_otp_challenge *val);
+ void k5_free_pa_otp_req(krb5_context context, krb5_pa_otp_req *val);
++void k5_free_kkdcp_message(krb5_context context, krb5_kkdcp_message *val);
+
+ /* #include "krb5/wordsize.h" -- comes in through base-defs.h. */
+ #include "com_err.h"
+@@ -1438,6 +1445,9 @@ encode_krb5_pa_otp_req(const krb5_pa_otp_req *, krb5_data **);
+ krb5_error_code
+ encode_krb5_pa_otp_enc_req(const krb5_data *, krb5_data **);
+
++krb5_error_code
++encode_krb5_kkdcp_message(const krb5_kkdcp_message *, krb5_data **);
++
+ /*************************************************************************
+ * End of prototypes for krb5_encode.c
+ *************************************************************************/
+@@ -1608,6 +1618,9 @@ decode_krb5_pa_otp_req(const krb5_data *, krb5_pa_otp_req **);
+ krb5_error_code
+ decode_krb5_pa_otp_enc_req(const krb5_data *, krb5_data **);
+
++krb5_error_code
++decode_krb5_kkdcp_message(const krb5_data *, krb5_kkdcp_message **);
++
+ struct _krb5_key_data; /* kdb.h */
+
+ struct ldap_seqof_key_data {
+diff --git a/src/lib/krb5/asn.1/asn1_k_encode.c b/src/lib/krb5/asn.1/asn1_k_encode.c
+index 7b9179d..4dc49c2 100644
+--- a/src/lib/krb5/asn.1/asn1_k_encode.c
++++ b/src/lib/krb5/asn.1/asn1_k_encode.c
+@@ -1711,3 +1711,17 @@ static const struct atype_info *pa_otp_enc_req_fields[] = {
+ };
+ DEFSEQTYPE(pa_otp_enc_req, krb5_data, pa_otp_enc_req_fields);
+ MAKE_CODEC(krb5_pa_otp_enc_req, pa_otp_enc_req);
++
++DEFFIELD(kkdcp_message_0, krb5_kkdcp_message,
++ kerb_message, 0, ostring_data);
++DEFFIELD(kkdcp_message_1, krb5_kkdcp_message,
++ target_domain, 1, opt_gstring_data);
++DEFFIELD(kkdcp_message_2, krb5_kkdcp_message,
++ dclocator_hint, 2, opt_int32);
++static const struct atype_info *kkdcp_message_fields[] = {
++ &k5_atype_kkdcp_message_0, &k5_atype_kkdcp_message_1,
++ &k5_atype_kkdcp_message_2
++};
++DEFSEQTYPE(kkdcp_message, krb5_kkdcp_message,
++ kkdcp_message_fields);
++MAKE_CODEC(krb5_kkdcp_message, kkdcp_message);
+diff --git a/src/lib/krb5/krb/kfree.c b/src/lib/krb5/krb/kfree.c
+index 32b2151..f86c619 100644
+--- a/src/lib/krb5/krb/kfree.c
++++ b/src/lib/krb5/krb/kfree.c
+@@ -821,3 +821,13 @@ k5_free_pa_otp_req(krb5_context context, krb5_pa_otp_req *val)
+ free(val->vendor.data);
+ free(val);
+ }
++
++void
++k5_free_kkdcp_message(krb5_context context, krb5_kkdcp_message *val)
++{
++ if (val == NULL)
++ return;
++ free(val->target_domain.data);
++ free(val->kerb_message.data);
++ free(val);
++}
+diff --git a/src/lib/krb5/libkrb5.exports b/src/lib/krb5/libkrb5.exports
+index 863ec02..2d0852d 100644
+--- a/src/lib/krb5/libkrb5.exports
++++ b/src/lib/krb5/libkrb5.exports
+@@ -25,6 +25,7 @@ decode_krb5_iakerb_finished
+ decode_krb5_iakerb_header
+ decode_krb5_kdc_req_body
+ decode_krb5_otp_tokeninfo
++decode_krb5_kkdcp_message
+ decode_krb5_pa_enc_ts
+ decode_krb5_pa_for_user
+ decode_krb5_pa_fx_fast_reply
+@@ -72,6 +73,7 @@ encode_krb5_iakerb_finished
+ encode_krb5_iakerb_header
+ encode_krb5_kdc_req_body
+ encode_krb5_otp_tokeninfo
++encode_krb5_kkdcp_message
+ encode_krb5_pa_enc_ts
+ encode_krb5_pa_for_user
+ encode_krb5_pa_fx_fast_reply
+@@ -113,6 +115,7 @@ k5_expand_path_tokens
+ k5_expand_path_tokens_extra
+ k5_free_algorithm_identifier
+ k5_free_otp_tokeninfo
++k5_free_kkdcp_message
+ k5_free_pa_otp_challenge
+ k5_free_pa_otp_req
+ k5_free_serverlist
+diff --git a/src/tests/asn.1/krb5_decode_test.c b/src/tests/asn.1/krb5_decode_test.c
+index 8719978..f12bb16 100644
+--- a/src/tests/asn.1/krb5_decode_test.c
++++ b/src/tests/asn.1/krb5_decode_test.c
+@@ -54,6 +54,8 @@ static void ktest_free_reply_key_pack(krb5_context context,
+ static void ktest_free_reply_key_pack_draft9(krb5_context context,
+ krb5_reply_key_pack_draft9 *val);
+ #endif
++static void ktest_free_kkdcp_message(krb5_context context,
++ krb5_kkdcp_message *val);
+
+ int main(argc, argv)
+ int argc;
+@@ -1077,6 +1079,13 @@ int main(argc, argv)
+ ktest_empty_data(&ref);
+ }
+
++ /****************************************************************/
++ /* decode_krb5_kkdcp_message */
++ {
++ setup(krb5_kkdcp_message,ktest_make_sample_kkdcp_message);
++ decode_run("kkdcp_message","","30 82 01 FC A0 82 01 EC 04 82 01 E8 6A 82 01 E4 30 82 01 E0 A1 03 02 01 05 A2 03 02 01 0A A3 26 30 24 30 10 A1 03 02 01 0D A2 09 04 07 70 61 2D 64 61 74 61 30 10 A1 03 02 01 0D A2 09 04 07 70 61 2D 64 61 74 61 A4 82 01 AA 30 82 01 A6 A0 07 03 05 00 FE DC BA 98 A1 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A2 10 1B 0E 41 54 48 45 4E 41 2E 4D 49 54 2E 45 44 55 A3 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A4 11 18 0F 31 39 39 34 30 36 31 30 30 36 30 33 31 37 5A A5 11 18 0F 31 39 39 34 30 36 31 30 30 36 30 33 31 37 5A A6 11 18 0F 31 39 39 34 30 36 31 30 30 36 30 33 31 37 5A A7 03 02 01 2A A8 08 30 06 02 01 00 02 01 01 A9 20 30 1E 30 0D A0 03 02 01 02 A1 06 04 04 12 D0 00 23 30 0D A0 03 02 01 02 A1 06 04 04 12 D0 00 23 AA 25 30 23 A0 03 02 01 00 A1 03 02 01 05 A2 17 04 15 6B 72 62 41 53 4E 2E 31 20 74 65 73 74 20 6D 65 73 73 61 67 65 AB 81 BF 30 81 BC 61 5C 30 5A A0 03 02 01 05 A1 10 1B 0E 41 54 48 45 4E 41 2E 4D 49 54 2E 45 44 55 A2 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A3 25 30 23 A0 03 02 01 00 A1 03 02 01 05 A2 17 04 15 6B 72 62 41 53 4E 2E 31 20 74 65 73 74 20 6D 65 73 73 61 67 65 61 5C 30 5A A0 03 02 01 05 A1 10 1B 0E 41 54 48 45 4E 41 2E 4D 49 54 2E 45 44 55 A2 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A3 25 30 23 A0 03 02 01 00 A1 03 02 01 05 A2 17 04 15 6B 72 62 41 53 4E 2E 31 20 74 65 73 74 20 6D 65 73 73 61 67 65 A1 0A 1B 08 6B 72 62 35 64 61 74 61",decode_krb5_kkdcp_message,ktest_equal_kkdcp_message,ktest_free_kkdcp_message);
++ }
++
+ #ifndef DISABLE_PKINIT
+
+ /****************************************************************/
+@@ -1262,3 +1271,12 @@ ktest_free_reply_key_pack_draft9(krb5_context context,
+ }
+
+ #endif /* not DISABLE_PKINIT */
++
++static void
++ktest_free_kkdcp_message(krb5_context context,
++ krb5_kkdcp_message *val)
++{
++ if (val)
++ ktest_empty_kkdcp_message(val);
++ free(val);
++}
+diff --git a/src/tests/asn.1/krb5_encode_test.c b/src/tests/asn.1/krb5_encode_test.c
+index 638f6fe..3ba8684 100644
+--- a/src/tests/asn.1/krb5_encode_test.c
++++ b/src/tests/asn.1/krb5_encode_test.c
+@@ -734,6 +734,14 @@ main(argc, argv)
+ encode_run(d, "pa_otp_enc_req", "", encode_krb5_pa_otp_enc_req);
+ ktest_empty_data(&d);
+ }
++ /****************************************************************/
++ /* encode_krb5_kkdcp_message */
++ {
++ krb5_kkdcp_message info;
++ ktest_make_sample_kkdcp_message(&info);
++ encode_run(info, "kkdcp_message", "", encode_krb5_kkdcp_message);
++ ktest_empty_kkdcp_message(&info);
++ }
+ #ifndef DISABLE_PKINIT
+ /****************************************************************/
+ /* encode_krb5_pa_pk_as_req */
+diff --git a/src/tests/asn.1/ktest.c b/src/tests/asn.1/ktest.c
+index aa41fd8..4ce9f70 100644
+--- a/src/tests/asn.1/ktest.c
++++ b/src/tests/asn.1/ktest.c
+@@ -933,6 +933,21 @@ ktest_make_sample_ldap_seqof_key_data(ldap_seqof_key_data *p)
+ }
+ #endif
+
++void
++ktest_make_sample_kkdcp_message(krb5_kkdcp_message *p)
++{
++ krb5_kdc_req req;
++ krb5_data *message;
++
++ ktest_make_sample_kdc_req(&req);
++ req.msg_type = KRB5_AS_REQ;
++ encode_krb5_as_req(&req, &message);
++ p->kerb_message = *message;
++ free(message);
++ ktest_empty_kdc_req(&req);
++ ktest_make_sample_data(&(p->target_domain));
++ p->dclocator_hint = 0;
++}
+
+ /****************************************************************/
+ /* destructors */
+@@ -1731,3 +1746,11 @@ ktest_empty_ldap_seqof_key_data(krb5_context ctx, ldap_seqof_key_data *p)
+ free(p->key_data);
+ }
+ #endif
++
++void
++ktest_empty_kkdcp_message(krb5_kkdcp_message *p)
++{
++ ktest_empty_data(&p->kerb_message);
++ ktest_empty_data(&p->target_domain);
++ p->dclocator_hint = -1;
++}
+diff --git a/src/tests/asn.1/ktest.h b/src/tests/asn.1/ktest.h
+index 67a6c69..a9ebb77 100644
+--- a/src/tests/asn.1/ktest.h
++++ b/src/tests/asn.1/ktest.h
+@@ -119,6 +119,9 @@ void ktest_make_sample_pkinit_supp_pub_info(krb5_pkinit_supp_pub_info *p);
+ #ifdef ENABLE_LDAP
+ void ktest_make_sample_ldap_seqof_key_data(ldap_seqof_key_data *p);
+ #endif
++
++void ktest_make_sample_kkdcp_message(krb5_kkdcp_message *p);
++
+ /*----------------------------------------------------------------------*/
+
+ void ktest_empty_authorization_data(krb5_authdata **ad);
+@@ -200,6 +203,8 @@ void ktest_empty_pkinit_supp_pub_info(krb5_pkinit_supp_pub_info *p);
+ void ktest_empty_ldap_seqof_key_data(krb5_context, ldap_seqof_key_data *p);
+ #endif
+
++void ktest_empty_kkdcp_message(krb5_kkdcp_message *p);
++
+ extern krb5_context test_context;
+ extern char *sample_principal_name;
+
+diff --git a/src/tests/asn.1/ktest_equal.c b/src/tests/asn.1/ktest_equal.c
+index 4e71242..39c35b5 100644
+--- a/src/tests/asn.1/ktest_equal.c
++++ b/src/tests/asn.1/ktest_equal.c
+@@ -1039,3 +1039,15 @@ ktest_equal_reply_key_pack_draft9(krb5_reply_key_pack_draft9 *ref,
+ }
+
+ #endif /* not DISABLE_PKINIT */
++
++int
++ktest_equal_kkdcp_message(krb5_kkdcp_message *ref, krb5_kkdcp_message *var)
++{
++ int p = TRUE;
++ if (ref == var) return TRUE;
++ else if (ref == NULL || var == NULL) return FALSE;
++ p = p && data_eq(ref->kerb_message, var->kerb_message);
++ p = p && data_eq(ref->target_domain, var->target_domain);
++ p = p && (ref->dclocator_hint == var->dclocator_hint);
++ return p;
++}
+diff --git a/src/tests/asn.1/ktest_equal.h b/src/tests/asn.1/ktest_equal.h
+index e75f86a..491653f 100644
+--- a/src/tests/asn.1/ktest_equal.h
++++ b/src/tests/asn.1/ktest_equal.h
+@@ -145,4 +145,7 @@ generic(ktest_equal_reply_key_pack, krb5_reply_key_pack);
+ generic(ktest_equal_reply_key_pack_draft9, krb5_reply_key_pack_draft9);
+ #endif /* not DISABLE_PKINIT */
+
++int ktest_equal_kkdcp_message(krb5_kkdcp_message *ref,
++ krb5_kkdcp_message *var);
++
+ #endif
+diff --git a/src/tests/asn.1/reference_encode.out b/src/tests/asn.1/reference_encode.out
+index 315e25b..b737da3 100644
+--- a/src/tests/asn.1/reference_encode.out
++++ b/src/tests/asn.1/reference_encode.out
+@@ -68,3 +68,4 @@ encode_krb5_pa_otp_challenge: 30 81 A5 80 08 6D 61 78 6E 6F 6E 63 65 81 0B 74 65
+ encode_krb5_pa_otp_req(optionals NULL): 30 2C 80 05 00 00 00 00 00 A2 23 A0 03 02 01 00 A1 03 02 01 05 A2 17 04 15 6B 72 62 41 53 4E 2E 31 20 74 65 73 74 20 6D 65 73 73 61 67 65
+ encode_krb5_pa_otp_req: 30 81 B9 80 05 00 60 00 00 00 81 05 6E 6F 6E 63 65 A2 23 A0 03 02 01 00 A1 03 02 01 05 A2 17 04 15 6B 72 62 41 53 4E 2E 31 20 74 65 73 74 20 6D 65 73 73 61 67 65 A3 0B 06 09 60 86 48 01 65 03 04 02 01 84 02 03 E8 85 05 66 72 6F 67 73 86 0A 6D 79 66 69 72 73 74 70 69 6E 87 05 68 61 72 6B 21 88 0F 31 39 39 34 30 36 31 30 30 36 30 33 31 37 5A 89 03 33 34 36 8A 01 02 8B 09 79 6F 75 72 74 6F 6B 65 6E 8C 28 75 72 6E 3A 69 65 74 66 3A 70 61 72 61 6D 73 3A 78 6D 6C 3A 6E 73 3A 6B 65 79 70 72 6F 76 3A 70 73 6B 63 3A 68 6F 74 70 8D 0B 45 78 61 6D 70 6C 65 63 6F 72 70
+ encode_krb5_pa_otp_enc_req: 30 0A 80 08 6B 72 62 35 64 61 74 61
++encode_krb5_kkdcp_message: 30 82 01 FC A0 82 01 EC 04 82 01 E8 6A 82 01 E4 30 82 01 E0 A1 03 02 01 05 A2 03 02 01 0A A3 26 30 24 30 10 A1 03 02 01 0D A2 09 04 07 70 61 2D 64 61 74 61 30 10 A1 03 02 01 0D A2 09 04 07 70 61 2D 64 61 74 61 A4 82 01 AA 30 82 01 A6 A0 07 03 05 00 FE DC BA 98 A1 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A2 10 1B 0E 41 54 48 45 4E 41 2E 4D 49 54 2E 45 44 55 A3 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A4 11 18 0F 31 39 39 34 30 36 31 30 30 36 30 33 31 37 5A A5 11 18 0F 31 39 39 34 30 36 31 30 30 36 30 33 31 37 5A A6 11 18 0F 31 39 39 34 30 36 31 30 30 36 30 33 31 37 5A A7 03 02 01 2A A8 08 30 06 02 01 00 02 01 01 A9 20 30 1E 30 0D A0 03 02 01 02 A1 06 04 04 12 D0 00 23 30 0D A0 03 02 01 02 A1 06 04 04 12 D0 00 23 AA 25 30 23 A0 03 02 01 00 A1 03 02 01 05 A2 17 04 15 6B 72 62 41 53 4E 2E 31 20 74 65 73 74 20 6D 65 73 73 61 67 65 AB 81 BF 30 81 BC 61 5C 30 5A A0 03 02 01 05 A1 10 1B 0E 41 54 48 45 4E 41 2E 4D 49 54 2E 45 44 55 A2 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A3 25 30 23 A0 03 02 01 00 A1 03 02 01 05 A2 17 04 15 6B 72 62 41 53 4E 2E 31 20 74 65 73 74 20 6D 65 73 73 61 67 65 61 5C 30 5A A0 03 02 01 05 A1 10 1B 0E 41 54 48 45 4E 41 2E 4D 49 54 2E 45 44 55 A2 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A3 25 30 23 A0 03 02 01 00 A1 03 02 01 05 A2 17 04 15 6B 72 62 41 53 4E 2E 31 20 74 65 73 74 20 6D 65 73 73 61 67 65 A1 0A 1B 08 6B 72 62 35 64 61 74 61
+diff --git a/src/tests/asn.1/trval_reference.out b/src/tests/asn.1/trval_reference.out
+index 461021e..599580c 100644
+--- a/src/tests/asn.1/trval_reference.out
++++ b/src/tests/asn.1/trval_reference.out
+@@ -1478,3 +1478,40 @@ encode_krb5_pa_otp_enc_req:
+ [Sequence/Sequence Of]
+ . [0] <8>
+ 6b 72 62 35 64 61 74 61 krb5data
++
++encode_krb5_kkdcp_message:
++
++[Sequence/Sequence Of]
++. [0] [Octet String] <488>
++ 6a 82 01 e4 30 82 01 e0 a1 03 02 01 05 a2 03 02 j...0...........
++ 01 0a a3 26 30 24 30 10 a1 03 02 01 0d a2 09 04 ...&0$0.........
++ 07 70 61 2d 64 61 74 61 30 10 a1 03 02 01 0d a2 .pa-data0.......
++ 09 04 07 70 61 2d 64 61 74 61 a4 82 01 aa 30 82 ...pa-data....0.
++ 01 a6 a0 07 03 05 00 fe dc ba 98 a1 1a 30 18 a0 .............0..
++ 03 02 01 01 a1 11 30 0f 1b 06 68 66 74 73 61 69 ......0...hftsai
++ 1b 05 65 78 74 72 61 a2 10 1b 0e 41 54 48 45 4e ..extra....ATHEN
++ 41 2e 4d 49 54 2e 45 44 55 a3 1a 30 18 a0 03 02 A.MIT.EDU..0....
++ 01 01 a1 11 30 0f 1b 06 68 66 74 73 61 69 1b 05 ....0...hftsai..
++ 65 78 74 72 61 a4 11 18 0f 31 39 39 34 30 36 31 extra....1994061
++ 30 30 36 30 33 31 37 5a a5 11 18 0f 31 39 39 34 0060317Z....1994
++ 30 36 31 30 30 36 30 33 31 37 5a a6 11 18 0f 31 0610060317Z....1
++ 39 39 34 30 36 31 30 30 36 30 33 31 37 5a a7 03 9940610060317Z..
++ 02 01 2a a8 08 30 06 02 01 00 02 01 01 a9 20 30 ..*..0........ 0
++ 1e 30 0d a0 03 02 01 02 a1 06 04 04 12 d0 00 23 .0.............#
++ 30 0d a0 03 02 01 02 a1 06 04 04 12 d0 00 23 aa 0.............#.
++ 25 30 23 a0 03 02 01 00 a1 03 02 01 05 a2 17 04 %0#.............
++ 15 6b 72 62 41 53 4e 2e 31 20 74 65 73 74 20 6d .krbASN.1 test m
++ 65 73 73 61 67 65 ab 81 bf 30 81 bc 61 5c 30 5a essage...0..a\0Z
++ a0 03 02 01 05 a1 10 1b 0e 41 54 48 45 4e 41 2e .........ATHENA.
++ 4d 49 54 2e 45 44 55 a2 1a 30 18 a0 03 02 01 01 MIT.EDU..0......
++ a1 11 30 0f 1b 06 68 66 74 73 61 69 1b 05 65 78 ..0...hftsai..ex
++ 74 72 61 a3 25 30 23 a0 03 02 01 00 a1 03 02 01 tra.%0#.........
++ 05 a2 17 04 15 6b 72 62 41 53 4e 2e 31 20 74 65 .....krbASN.1 te
++ 73 74 20 6d 65 73 73 61 67 65 61 5c 30 5a a0 03 st messagea\0Z..
++ 02 01 05 a1 10 1b 0e 41 54 48 45 4e 41 2e 4d 49 .......ATHENA.MI
++ 54 2e 45 44 55 a2 1a 30 18 a0 03 02 01 01 a1 11 T.EDU..0........
++ 30 0f 1b 06 68 66 74 73 61 69 1b 05 65 78 74 72 0...hftsai..extr
++ 61 a3 25 30 23 a0 03 02 01 00 a1 03 02 01 05 a2 a.%0#...........
++ 17 04 15 6b 72 62 41 53 4e 2e 31 20 74 65 73 74 ...krbASN.1 test
++ 20 6d 65 73 73 61 67 65 message
++. [1] [General string] "krb5data"
+--
+2.1.0
+
diff --git a/0006-Dispatch-style-protocol-switching-for-transport.patch b/0006-Dispatch-style-protocol-switching-for-transport.patch
new file mode 100644
index 0000000..e36544e
--- /dev/null
+++ b/0006-Dispatch-style-protocol-switching-for-transport.patch
@@ -0,0 +1,506 @@
+From 606e2ccc0a2546a23761f910482a55c5bf0f98ac Mon Sep 17 00:00:00 2001
+From: "Robbie Harwood (frozencemetery)" <rharwood@club.cc.cmu.edu>
+Date: Fri, 16 Aug 2013 14:48:55 -0400
+Subject: [PATCH 06/13] Dispatch-style protocol switching for transport
+
+Switch to using per-transport-type functions when a socket that we're
+using to communicate with a server becomes readable or writable, and add
+them as pointers to the connection state. The functions are passed the
+name of the realm of the server being contacted, as we expect to need
+this in the near future.
+
+[nalin@redhat.com: replace macros with typedefs]
+[nalin@redhat.com: compare transports with TCP_OR_UDP rather than with 0]
+
+ticket: 7929
+---
+ src/lib/krb5/os/changepw.c | 6 +-
+ src/lib/krb5/os/os-proto.h | 1 +
+ src/lib/krb5/os/sendto_kdc.c | 297 ++++++++++++++++++++++++-------------------
+ 3 files changed, 171 insertions(+), 133 deletions(-)
+
+diff --git a/src/lib/krb5/os/changepw.c b/src/lib/krb5/os/changepw.c
+index a1c9885..0ee427d 100644
+--- a/src/lib/krb5/os/changepw.c
++++ b/src/lib/krb5/os/changepw.c
+@@ -261,9 +261,9 @@ change_set_password(krb5_context context,
+ callback_info.pfn_cleanup = kpasswd_sendto_msg_cleanup;
+ krb5_free_data_contents(callback_ctx.context, &chpw_rep);
+
+- code = k5_sendto(callback_ctx.context, NULL, &sl, strategy,
+- &callback_info, &chpw_rep, ss2sa(&remote_addr),
+- &addrlen, NULL, NULL, NULL);
++ code = k5_sendto(callback_ctx.context, NULL, &creds->server->realm,
++ &sl, strategy, &callback_info, &chpw_rep,
++ ss2sa(&remote_addr), &addrlen, NULL, NULL, NULL);
+ if (code) {
+ /*
+ * Here we may want to switch to TCP on some errors.
+diff --git a/src/lib/krb5/os/os-proto.h b/src/lib/krb5/os/os-proto.h
+index f23dda5..e60ccd0 100644
+--- a/src/lib/krb5/os/os-proto.h
++++ b/src/lib/krb5/os/os-proto.h
+@@ -115,6 +115,7 @@ int _krb5_use_dns_kdc (krb5_context);
+ int _krb5_conf_boolean (const char *);
+
+ krb5_error_code k5_sendto(krb5_context context, const krb5_data *message,
++ const krb5_data *realm,
+ const struct serverlist *addrs,
+ k5_transport_strategy strategy,
+ struct sendto_callback_info *callback_info,
+diff --git a/src/lib/krb5/os/sendto_kdc.c b/src/lib/krb5/os/sendto_kdc.c
+index c6aae8e..28f1c4d 100644
+--- a/src/lib/krb5/os/sendto_kdc.c
++++ b/src/lib/krb5/os/sendto_kdc.c
+@@ -96,11 +96,18 @@ struct outgoing_message {
+ unsigned char msg_len_buf[4];
+ };
+
++struct conn_state;
++typedef krb5_boolean fd_handler_fn(krb5_context context,
++ const krb5_data *realm,
++ struct conn_state *conn,
++ struct select_state *selstate);
++
+ struct conn_state {
+ SOCKET fd;
+ enum conn_states state;
+- int (*service)(krb5_context context, struct conn_state *,
+- struct select_state *, int);
++ fd_handler_fn *service_connect;
++ fd_handler_fn *service_write;
++ fd_handler_fn *service_read;
+ struct remote_address addr;
+ struct incoming_message in;
+ struct outgoing_message out;
+@@ -409,9 +416,9 @@ krb5_sendto_kdc(krb5_context context, const krb5_data *message,
+ return retval;
+
+ err = 0;
+- retval = k5_sendto(context, message, &servers, strategy, NULL, reply,
+- NULL, NULL, &server_used, check_for_svc_unavailable,
+- &err);
++ retval = k5_sendto(context, message, realm, &servers, strategy, NULL,
++ reply, NULL, NULL, &server_used,
++ check_for_svc_unavailable, &err);
+ if (retval == KRB5_KDC_UNREACH) {
+ if (err == KDC_ERR_SVC_UNAVAILABLE) {
+ retval = KRB5KDC_ERR_SVC_UNAVAILABLE;
+@@ -457,10 +464,10 @@ cleanup:
+ * connections already in progress
+ */
+
+-static int service_tcp_fd(krb5_context context, struct conn_state *conn,
+- struct select_state *selstate, int ssflags);
+-static int service_udp_fd(krb5_context context, struct conn_state *conn,
+- struct select_state *selstate, int ssflags);
++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;
+
+ /* 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. */
+@@ -505,9 +512,13 @@ add_connection(struct conn_state **conns, k5_transport transport,
+ state->server_index = server_index;
+ SG_SET(&state->out.sgbuf[1], NULL, 0);
+ if (transport == TCP) {
+- state->service = service_tcp_fd;
++ state->service_connect = service_tcp_connect;
++ state->service_write = service_tcp_write;
++ state->service_read = service_tcp_read;
+ } else {
+- state->service = service_udp_fd;
++ state->service_connect = NULL;
++ state->service_write = NULL;
++ state->service_read = service_udp_read;
+
+ if (*udpbufp == NULL) {
+ *udpbufp = malloc(MAX_DGRAM_SIZE);
+@@ -788,9 +799,13 @@ maybe_send(krb5_context context, struct conn_state *conn,
+ }
+
+ static void
+-kill_conn(struct conn_state *conn, struct select_state *selstate)
++kill_conn(krb5_context context, struct conn_state *conn,
++ struct select_state *selstate)
+ {
++ if (socktype_for_transport(conn->addr.transport) == SOCK_STREAM)
++ TRACE_SENDTO_KDC_TCP_DISCONNECT(context, &conn->addr);
+ cm_remove_fd(selstate, conn->fd);
++
+ closesocket(conn->fd);
+ conn->fd = INVALID_SOCKET;
+ conn->state = FAILED;
+@@ -814,136 +829,157 @@ get_so_error(int fd)
+ return sockerr;
+ }
+
+-/* Process events on a TCP socket. Return 1 if we get a complete reply. */
+-static int
+-service_tcp_fd(krb5_context context, struct conn_state *conn,
+- struct select_state *selstate, int ssflags)
++/* Perform next step in sending. Return true on usable data. */
++static krb5_boolean
++service_dispatch(krb5_context context, const krb5_data *realm,
++ struct conn_state *conn, struct select_state *selstate,
++ int ssflags)
+ {
+- int e = 0;
+- ssize_t nwritten, nread;
+- SOCKET_WRITEV_TEMP tmp;
+- struct incoming_message *in = &conn->in;
+- struct outgoing_message *out = &conn->out;
+-
+ /* Check for a socket exception. */
+- if (ssflags & SSF_EXCEPTION)
+- goto kill_conn;
++ if (ssflags & SSF_EXCEPTION) {
++ kill_conn(context, conn, selstate);
++ return FALSE;
++ }
+
+ switch (conn->state) {
+ case CONNECTING:
+- /* Check whether the connection succeeded. */
+- e = get_so_error(conn->fd);
+- if (e) {
+- TRACE_SENDTO_KDC_TCP_ERROR_CONNECT(context, &conn->addr, e);
+- goto kill_conn;
+- }
+- conn->state = WRITING;
++ assert(conn->service_connect != NULL);
++ return conn->service_connect(context, realm, conn, selstate);
++ case WRITING:
++ assert(conn->service_write != NULL);
++ return conn->service_write(context, realm, conn, selstate);
++ case READING:
++ assert(conn->service_read != NULL);
++ return conn->service_read(context, realm, conn, selstate);
++ default:
++ abort();
++ }
++}
+
+- /* Record this connection's timeout for service_fds. */
+- if (get_curtime_ms(&conn->endtime) == 0)
+- conn->endtime += 10000;
++/* Initialize TCP transport. */
++static krb5_boolean
++service_tcp_connect(krb5_context context, const krb5_data *realm,
++ struct conn_state *conn, struct select_state *selstate)
++{
++ /* Check whether the connection succeeded. */
++ int e = get_so_error(conn->fd);
+
+- /* Fall through. */
+- case WRITING:
+- TRACE_SENDTO_KDC_TCP_SEND(context, &conn->addr);
+- nwritten = SOCKET_WRITEV(conn->fd, out->sgp, out->sg_count, tmp);
+- if (nwritten < 0) {
+- TRACE_SENDTO_KDC_TCP_ERROR_SEND(context, &conn->addr,
+- SOCKET_ERRNO);
+- goto kill_conn;
++ if (e) {
++ TRACE_SENDTO_KDC_TCP_ERROR_CONNECT(context, &conn->addr, e);
++ kill_conn(context, conn, selstate);
++ return FALSE;
++ }
++
++ conn->state = WRITING;
++
++ /* Record this connection's timeout for service_fds. */
++ if (get_curtime_ms(&conn->endtime) == 0)
++ conn->endtime += 10000;
++
++ return service_tcp_write(context, realm, conn, selstate);
++}
++
++/* Sets conn->state to READING when done. */
++static krb5_boolean
++service_tcp_write(krb5_context context, const krb5_data *realm,
++ struct conn_state *conn, struct select_state *selstate)
++{
++ ssize_t nwritten;
++ SOCKET_WRITEV_TEMP tmp;
++
++ TRACE_SENDTO_KDC_TCP_SEND(context, &conn->addr);
++ nwritten = SOCKET_WRITEV(conn->fd, conn->out.sgp, conn->out.sg_count, tmp);
++ if (nwritten < 0) {
++ TRACE_SENDTO_KDC_TCP_ERROR_SEND(context, &conn->addr, SOCKET_ERRNO);
++ kill_conn(context, conn, selstate);
++ return FALSE;
++ }
++ while (nwritten) {
++ sg_buf *sgp = conn->out.sgp;
++ if ((size_t)nwritten < SG_LEN(sgp)) {
++ SG_ADVANCE(sgp, (size_t)nwritten);
++ nwritten = 0;
++ } else {
++ nwritten -= SG_LEN(sgp);
++ conn->out.sgp++;
++ conn->out.sg_count--;
+ }
+- while (nwritten) {
+- sg_buf *sgp = out->sgp;
+- if ((size_t) nwritten < SG_LEN(sgp)) {
+- SG_ADVANCE(sgp, (size_t) nwritten);
+- nwritten = 0;
+- } else {
+- nwritten -= SG_LEN(sgp);
+- out->sgp++;
+- out->sg_count--;
+- }
++ }
++ if (conn->out.sg_count == 0) {
++ /* Done writing, switch to reading. */
++ cm_read(selstate, conn->fd);
++ conn->state = READING;
++ }
++ return FALSE;
++}
++
++/* Return true on usable data. */
++static krb5_boolean
++service_tcp_read(krb5_context context, const krb5_data *realm,
++ struct conn_state *conn, struct select_state *selstate)
++{
++ ssize_t nread;
++ int e = 0;
++ struct incoming_message *in = &conn->in;
++
++ if (in->bufsizebytes_read == 4) {
++ /* Reading data. */
++ nread = SOCKET_READ(conn->fd, &in->buf[in->pos], in->n_left);
++ if (nread <= 0) {
++ e = nread ? SOCKET_ERRNO : ECONNRESET;
++ TRACE_SENDTO_KDC_TCP_ERROR_RECV(context, &conn->addr, e);
++ kill_conn(context, conn, selstate);
++ return FALSE;
+ }
+- if (out->sg_count == 0) {
+- /* Done writing, switch to reading. */
+- cm_read(selstate, conn->fd);
+- conn->state = READING;
+- in->bufsizebytes_read = 0;
+- in->bufsize = 0;
+- in->pos = 0;
+- in->buf = NULL;
+- in->n_left = 0;
++ in->n_left -= nread;
++ in->pos += nread;
++ if (in->n_left <= 0)
++ return TRUE;
++ } else {
++ /* Reading length. */
++ nread = SOCKET_READ(conn->fd, in->bufsizebytes + in->bufsizebytes_read,
++ 4 - in->bufsizebytes_read);
++ if (nread <= 0) {
++ e = nread ? SOCKET_ERRNO : ECONNRESET;
++ TRACE_SENDTO_KDC_TCP_ERROR_RECV_LEN(context, &conn->addr, e);
++ kill_conn(context, conn, selstate);
++ return FALSE;
+ }
+- return 0;
+-
+- case READING:
++ in->bufsizebytes_read += nread;
+ if (in->bufsizebytes_read == 4) {
+- /* Reading data. */
+- nread = SOCKET_READ(conn->fd, &in->buf[in->pos], in->n_left);
+- if (nread <= 0) {
+- e = nread ? SOCKET_ERRNO : ECONNRESET;
+- TRACE_SENDTO_KDC_TCP_ERROR_RECV(context, &conn->addr, e);
+- goto kill_conn;
+- }
+- in->n_left -= nread;
+- in->pos += nread;
+- if (in->n_left <= 0)
+- return 1;
+- } else {
+- /* Reading length. */
+- nread = SOCKET_READ(conn->fd,
+- in->bufsizebytes + in->bufsizebytes_read,
+- 4 - in->bufsizebytes_read);
+- if (nread <= 0) {
+- e = nread ? SOCKET_ERRNO : ECONNRESET;
+- TRACE_SENDTO_KDC_TCP_ERROR_RECV_LEN(context, &conn->addr, e);
+- goto kill_conn;
++ unsigned long len = load_32_be(in->bufsizebytes);
++ /* Arbitrary 1M cap. */
++ if (len > 1 * 1024 * 1024) {
++ kill_conn(context, conn, selstate);
++ return FALSE;
+ }
+- in->bufsizebytes_read += nread;
+- if (in->bufsizebytes_read == 4) {
+- unsigned long len = load_32_be(in->bufsizebytes);
+- /* Arbitrary 1M cap. */
+- if (len > 1 * 1024 * 1024)
+- goto kill_conn;
+- in->bufsize = in->n_left = len;
+- in->pos = 0;
+- in->buf = malloc(len);
+- if (in->buf == NULL)
+- goto kill_conn;
++ in->bufsize = in->n_left = len;
++ in->pos = 0;
++ in->buf = malloc(len);
++ if (in->buf == NULL) {
++ kill_conn(context, conn, selstate);
++ return FALSE;
+ }
+ }
+- break;
+-
+- default:
+- abort();
+ }
+- return 0;
+-
+-kill_conn:
+- TRACE_SENDTO_KDC_TCP_DISCONNECT(context, &conn->addr);
+- kill_conn(conn, selstate);
+- return 0;
++ return FALSE;
+ }
+
+-/* Process events on a UDP socket. Return 1 if we get a reply. */
+-static int
+-service_udp_fd(krb5_context context, struct conn_state *conn,
+- struct select_state *selstate, int ssflags)
++/* Process events on a UDP socket. Return true if we get a reply. */
++static krb5_boolean
++service_udp_read(krb5_context context, const krb5_data *realm,
++ struct conn_state *conn, struct select_state *selstate)
+ {
+ int nread;
+
+- if (!(ssflags & (SSF_READ|SSF_EXCEPTION)))
+- abort();
+- if (conn->state != READING)
+- abort();
+-
+ nread = recv(conn->fd, conn->in.buf, conn->in.bufsize, 0);
+ if (nread < 0) {
+ TRACE_SENDTO_KDC_UDP_ERROR_RECV(context, &conn->addr, SOCKET_ERRNO);
+- kill_conn(conn, selstate);
+- return 0;
++ kill_conn(context, conn, selstate);
++ return FALSE;
+ }
+ conn->in.pos = nread;
+- return 1;
++ return TRUE;
+ }
+
+ /* Return the maximum of endtime and the endtime fields of all currently active
+@@ -965,7 +1001,7 @@ get_endtime(time_ms endtime, struct conn_state *conns)
+ static krb5_boolean
+ service_fds(krb5_context context, struct select_state *selstate,
+ time_ms interval, struct conn_state *conns,
+- struct select_state *seltemp,
++ struct select_state *seltemp, const krb5_data *realm,
+ int (*msg_handler)(krb5_context, const krb5_data *, void *),
+ void *msg_handler_data, struct conn_state **winner_out)
+ {
+@@ -977,7 +1013,7 @@ service_fds(krb5_context context, struct select_state *selstate,
+
+ e = get_curtime_ms(&endtime);
+ if (e)
+- return 1;
++ return TRUE;
+ endtime += interval;
+
+ e = 0;
+@@ -991,7 +1027,7 @@ service_fds(krb5_context context, struct select_state *selstate,
+
+ if (selret == 0)
+ /* Timeout, return to caller. */
+- return 0;
++ return FALSE;
+
+ /* Got something on a socket, process it. */
+ for (state = conns; state != NULL; state = state->next) {
+@@ -1003,7 +1039,7 @@ service_fds(krb5_context context, struct select_state *selstate,
+ if (!ssflags)
+ continue;
+
+- if (state->service(context, state, selstate, ssflags)) {
++ if (service_dispatch(context, realm, state, selstate, ssflags)) {
+ int stop = 1;
+
+ if (msg_handler != NULL) {
+@@ -1014,14 +1050,14 @@ service_fds(krb5_context context, struct select_state *selstate,
+
+ if (stop) {
+ *winner_out = state;
+- return 1;
++ return TRUE;
+ }
+ }
+ }
+ }
+ if (e != 0)
+- return 1;
+- return 0;
++ return TRUE;
++ return FALSE;
+ }
+
+ /*
+@@ -1052,7 +1088,8 @@ service_fds(krb5_context context, struct select_state *selstate,
+
+ krb5_error_code
+ k5_sendto(krb5_context context, const krb5_data *message,
+- const struct serverlist *servers, k5_transport_strategy strategy,
++ const krb5_data *realm, const struct serverlist *servers,
++ k5_transport_strategy strategy,
+ struct sendto_callback_info* callback_info, krb5_data *reply,
+ struct sockaddr *remoteaddr, socklen_t *remoteaddrlen,
+ int *server_used,
+@@ -1098,7 +1135,7 @@ k5_sendto(krb5_context context, const krb5_data *message,
+ if (maybe_send(context, state, message, sel_state, callback_info))
+ continue;
+ done = service_fds(context, sel_state, 1000, conns, seltemp,
+- msg_handler, msg_handler_data, &winner);
++ realm, msg_handler, msg_handler_data, &winner);
+ }
+ }
+
+@@ -1110,13 +1147,13 @@ k5_sendto(krb5_context context, const krb5_data *message,
+ if (maybe_send(context, state, message, sel_state, callback_info))
+ continue;
+ done = service_fds(context, sel_state, 1000, conns, seltemp,
+- msg_handler, msg_handler_data, &winner);
++ realm, msg_handler, msg_handler_data, &winner);
+ }
+
+ /* Wait for two seconds at the end of the first pass. */
+ if (!done) {
+ done = service_fds(context, sel_state, 2000, conns, seltemp,
+- msg_handler, msg_handler_data, &winner);
++ realm, msg_handler, msg_handler_data, &winner);
+ }
+
+ /* Make remaining passes over all of the connections. */
+@@ -1126,14 +1163,14 @@ k5_sendto(krb5_context context, const krb5_data *message,
+ if (maybe_send(context, state, message, sel_state, callback_info))
+ continue;
+ done = service_fds(context, sel_state, 1000, conns, seltemp,
+- msg_handler, msg_handler_data, &winner);
++ realm, msg_handler, msg_handler_data, &winner);
+ if (sel_state->nfds == 0)
+ break;
+ }
+ /* Wait for the delay backoff at the end of this pass. */
+ if (!done) {
+ done = service_fds(context, sel_state, delay, conns, seltemp,
+- msg_handler, msg_handler_data, &winner);
++ realm, msg_handler, msg_handler_data, &winner);
+ }
+ if (sel_state->nfds == 0)
+ break;
+--
+2.1.0
+
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
+
diff --git a/0008-Load-custom-anchors-when-using-KKDCP.patch b/0008-Load-custom-anchors-when-using-KKDCP.patch
new file mode 100644
index 0000000..19fe964
--- /dev/null
+++ b/0008-Load-custom-anchors-when-using-KKDCP.patch
@@ -0,0 +1,312 @@
+From f220067c2969aab107bd1300ad1cb8d4855389a7 Mon Sep 17 00:00:00 2001
+From: Nalin Dahyabhai <nalin@dahyabhai.net>
+Date: Thu, 17 Apr 2014 17:17:13 -0400
+Subject: [PATCH 08/13] Load custom anchors when using KKDCP
+
+Add an http_anchors per-realm setting which we'll apply when using an
+HTTPS proxy, more or less mimicking the syntax of its similarly-named
+PKINIT counterpart. We only check the [realms] section, though.
+
+ticket: 7929
+---
+ doc/admin/conf_files/krb5_conf.rst | 26 ++++++
+ src/include/k5-int.h | 1 +
+ src/include/k5-trace.h | 7 ++
+ src/lib/krb5/os/sendto_kdc.c | 169 ++++++++++++++++++++++++++++++++++++-
+ 4 files changed, 201 insertions(+), 2 deletions(-)
+
+diff --git a/doc/admin/conf_files/krb5_conf.rst b/doc/admin/conf_files/krb5_conf.rst
+index 19ea9c9..c069327 100644
+--- a/doc/admin/conf_files/krb5_conf.rst
++++ b/doc/admin/conf_files/krb5_conf.rst
+@@ -428,6 +428,32 @@ following tags may be specified in the realm's subsection:
+ (for example, when converting ``rcmd.hostname`` to
+ ``host/hostname.domain``).
+
++**http_anchors**
++ When KDCs and kpasswd servers are accessed through HTTPS proxies, this tag
++ can be used to specify the location of the CA certificate which should be
++ trusted to issue the certificate for a proxy server. If left unspecified,
++ the system-wide default set of CA certificates is used.
++
++ The syntax for values is similar to that of values for the
++ **pkinit_anchors** tag:
++
++ **FILE:** *filename*
++
++ *filename* is assumed to be the name of an OpenSSL-style ca-bundle file.
++
++ **DIR:** *dirname*
++
++ *dirname* is assumed to be an directory which contains CA certificates.
++ All files in the directory will be examined; if they contain certificates
++ (in PEM format), they will be used.
++
++ **ENV:** *envvar*
++
++ *envvar* specifies the name of an environment variable which has been set
++ to a value conforming to one of the previous values. For example,
++ ``ENV:X509_PROXY_CA``, where environment variable ``X509_PROXY_CA`` has
++ been set to ``FILE:/tmp/my_proxy.pem``.
++
+ **kdc**
+ The name or address of a host running a KDC for that realm. An
+ optional port number, separated from the hostname by a colon, may
+diff --git a/src/include/k5-int.h b/src/include/k5-int.h
+index 8f039ee..187d16d 100644
+--- a/src/include/k5-int.h
++++ b/src/include/k5-int.h
+@@ -212,6 +212,7 @@ typedef unsigned char u_char;
+ #define KRB5_CONF_EXTRA_ADDRESSES "extra_addresses"
+ #define KRB5_CONF_FORWARDABLE "forwardable"
+ #define KRB5_CONF_HOST_BASED_SERVICES "host_based_services"
++#define KRB5_CONF_HTTP_ANCHORS "http_anchors"
+ #define KRB5_CONF_IGNORE_ACCEPTOR_HOSTNAME "ignore_acceptor_hostname"
+ #define KRB5_CONF_IPROP_ENABLE "iprop_enable"
+ #define KRB5_CONF_IPROP_MASTER_ULOGSIZE "iprop_master_ulogsize"
+diff --git a/src/include/k5-trace.h b/src/include/k5-trace.h
+index f0d79f1..046bc95 100644
+--- a/src/include/k5-trace.h
++++ b/src/include/k5-trace.h
+@@ -324,6 +324,13 @@ 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_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) \
+ TRACE(c, "HTTPS error connecting to {raddr}", raddr)
+ #define TRACE_SENDTO_KDC_HTTPS_ERROR_RECV(c, raddr, err) \
+diff --git a/src/lib/krb5/os/sendto_kdc.c b/src/lib/krb5/os/sendto_kdc.c
+index a4727c4..4bd8698 100644
+--- a/src/lib/krb5/os/sendto_kdc.c
++++ b/src/lib/krb5/os/sendto_kdc.c
+@@ -77,6 +77,9 @@
+ #ifdef PROXY_TLS_IMPL_OPENSSL
+ #include <openssl/err.h>
+ #include <openssl/ssl.h>
++#include <openssl/x509.h>
++#include <openssl/x509v3.h>
++#include <dirent.h>
+ #endif
+
+ #define MAX_PASS 3
+@@ -152,6 +155,11 @@ struct conn_state {
+ } http;
+ };
+
++#ifdef PROXY_TLS_IMPL_OPENSSL
++/* Extra-data identifier, used to pass context into the verify callback. */
++static int ssl_ex_context_id = -1;
++#endif
++
+ void
+ k5_sendto_kdc_initialize(void)
+ {
+@@ -159,6 +167,8 @@ k5_sendto_kdc_initialize(void)
+ SSL_library_init();
+ SSL_load_error_strings();
+ OpenSSL_add_all_algorithms();
++
++ ssl_ex_context_id = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
+ #endif
+ }
+
+@@ -1147,6 +1157,152 @@ flush_ssl_errors(krb5_context context)
+ }
+ }
+
++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)
++{
++ 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;
++}
++
++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;
++
++ realmz = k5memdup0(realm->data, realm->length, &err);
++ if (realmz == 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 int
++ssl_verify_callback(int preverify_ok, X509_STORE_CTX *store_ctx)
++{
++ X509 *x;
++ SSL *ssl;
++ BIO *bio;
++ krb5_context context;
++ int err, depth;
++ const char *cert = NULL, *errstr;
++ 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);
++ /* 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;
++ }
++ /* All done. */
++ return 1;
++}
++
+ /*
+ * 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
+@@ -1156,10 +1312,14 @@ 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;
+
++ if (ssl_ex_context_id == -1)
++ goto kill_conn;
++
+ /* Do general SSL library setup. */
+ ctx = SSL_CTX_new(SSLv23_client_method());
+ if (ctx == NULL)
+@@ -1167,14 +1327,19 @@ setup_ssl(krb5_context context, const krb5_data *realm,
+ 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))
++ 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;
++
+ /* Tell the SSL library about the socket. */
+ if (!SSL_set_fd(ssl, conn->fd))
+ goto kill_conn;
+--
+2.1.0
+
diff --git a/0009-Check-names-in-the-server-s-cert-when-using-KKDCP.patch b/0009-Check-names-in-the-server-s-cert-when-using-KKDCP.patch
new file mode 100644
index 0000000..14e3354
--- /dev/null
+++ b/0009-Check-names-in-the-server-s-cert-when-using-KKDCP.patch
@@ -0,0 +1,562 @@
+From f7825e81b1ebf533c1dba9f84ae9ad36073a89cf Mon Sep 17 00:00:00 2001
+From: Nalin Dahyabhai <nalin@dahyabhai.net>
+Date: Thu, 17 Apr 2014 17:19:03 -0400
+Subject: [PATCH 09/13] Check names in the server's cert when using KKDCP
+
+When we connect to a KDC using an HTTPS proxy, check that the naming
+information in the certificate matches the name or address which we
+extracted from the server URL in the configuration.
+
+ticket: 7929
+---
+ src/include/k5-trace.h | 5 +
+ src/lib/krb5/os/Makefile.in | 3 +
+ src/lib/krb5/os/checkhost.c | 251 +++++++++++++++++++++++++++++++++++++++++++
+ src/lib/krb5/os/checkhost.h | 39 +++++++
+ src/lib/krb5/os/deps | 14 ++-
+ src/lib/krb5/os/sendto_kdc.c | 53 +++++++--
+ 6 files changed, 355 insertions(+), 10 deletions(-)
+ create mode 100644 src/lib/krb5/os/checkhost.c
+ create mode 100644 src/lib/krb5/os/checkhost.h
+
+diff --git a/src/include/k5-trace.h b/src/include/k5-trace.h
+index 046bc95..9e75b29 100644
+--- a/src/include/k5-trace.h
++++ b/src/include/k5-trace.h
+@@ -324,6 +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, \
+diff --git a/src/lib/krb5/os/Makefile.in b/src/lib/krb5/os/Makefile.in
+index fb4001a..fa8a093 100644
+--- a/src/lib/krb5/os/Makefile.in
++++ b/src/lib/krb5/os/Makefile.in
+@@ -13,6 +13,7 @@ STLIBOBJS= \
+ c_ustime.o \
+ ccdefname.o \
+ changepw.o \
++ checkhost.o \
+ dnsglue.o \
+ dnssrv.o \
+ expand_path.o \
+@@ -59,6 +60,7 @@ OBJS= \
+ $(OUTPRE)c_ustime.$(OBJEXT) \
+ $(OUTPRE)ccdefname.$(OBJEXT) \
+ $(OUTPRE)changepw.$(OBJEXT) \
++ $(OUTPRE)checkhost.$(OBJEXT) \
+ $(OUTPRE)dnsglue.$(OBJEXT) \
+ $(OUTPRE)dnssrv.$(OBJEXT) \
+ $(OUTPRE)expand_path.$(OBJEXT) \
+@@ -105,6 +107,7 @@ SRCS= \
+ $(srcdir)/c_ustime.c \
+ $(srcdir)/ccdefname.c \
+ $(srcdir)/changepw.c \
++ $(srcdir)/checkhost.c \
+ $(srcdir)/dnsglue.c \
+ $(srcdir)/dnssrv.c \
+ $(srcdir)/expand_path.c \
+diff --git a/src/lib/krb5/os/checkhost.c b/src/lib/krb5/os/checkhost.c
+new file mode 100644
+index 0000000..a91615d
+--- /dev/null
++++ b/src/lib/krb5/os/checkhost.c
+@@ -0,0 +1,251 @@
++/*
++ * Copyright 2014 Red Hat, Inc. All rights reserved.
++ *
++ * 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"
++
++#ifdef PROXY_TLS_IMPL_OPENSSL
++#include <openssl/ssl.h>
++#include <openssl/x509.h>
++#include <openssl/x509v3.h>
++#include "checkhost.h"
++
++/* 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 ssize_t
++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 the passed-in expected IP address matches any of the names we
++ * can recover from the server certificate, false otherwise.
++ */
++krb5_boolean
++k5_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;
++ size_t name_length;
++ union {
++ struct in_addr in;
++ struct in6_addr in6;
++ } name;
++
++ /* Parse the IP address into an octet string. */
++ ip = M_ASN1_OCTET_STRING_new();
++ if (ip == NULL)
++ return FALSE;
++
++ if (inet_aton(text, &name.in) == 1)
++ name_length = sizeof(name.in);
++ else if (inet_pton(AF_INET6, text, &name.in6) == 1)
++ name_length = sizeof(name.in6);
++ else
++ name_length = 0;
++
++ if (name_length == 0) {
++ ASN1_OCTET_STRING_free(ip);
++ return FALSE;
++ }
++ M_ASN1_OCTET_STRING_set(ip, &name, name_length);
++
++ /* 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 (matched)
++ return TRUE;
++ 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) == name_length &&
++ strncmp(text, buf, name_length) == 0;
++ }
++
++ /* We didn't find a match. */
++ return FALSE;
++}
++
++/*
++ * Return true if the passed-in expected name matches any of the names we can
++ * recover from a server certificate, false otherwise.
++ */
++krb5_boolean
++k5_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;
++}
++#endif
+diff --git a/src/lib/krb5/os/checkhost.h b/src/lib/krb5/os/checkhost.h
+new file mode 100644
+index 0000000..b9d751e
+--- /dev/null
++++ b/src/lib/krb5/os/checkhost.h
+@@ -0,0 +1,39 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/*
++ * Copyright 2014 Red Hat, Inc. All rights reserved.
++ *
++ * 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.
++ */
++
++/*
++ * Certificate subjectAltName check prototypes.
++ */
++
++#ifndef KRB5_LIBOS_CHECKHOST_PROTO__
++#define KRB5_LIBOS_CHECKHOST_PROTO__
++
++krb5_boolean k5_check_cert_servername(X509 *x, const char *expected);
++krb5_boolean k5_check_cert_address(X509 *x, const char *expected);
++
++#endif /* KRB5_LIBOS_CHECKHOST_PROTO__ */
+diff --git a/src/lib/krb5/os/deps b/src/lib/krb5/os/deps
+index 3dd6d46..d56ff30 100644
+--- a/src/lib/krb5/os/deps
++++ b/src/lib/krb5/os/deps
+@@ -49,6 +49,17 @@ 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 \
+@@ -418,7 +429,8 @@ 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 os-proto.h sendto_kdc.c
++ $(top_srcdir)/include/socket-utils.h checkhost.h os-proto.h \
++ sendto_kdc.c
+ sn2princ.so sn2princ.po $(OUTPRE)sn2princ.$(OBJEXT): \
+ $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
+ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
+diff --git a/src/lib/krb5/os/sendto_kdc.c b/src/lib/krb5/os/sendto_kdc.c
+index 4bd8698..f083c0f 100644
+--- a/src/lib/krb5/os/sendto_kdc.c
++++ b/src/lib/krb5/os/sendto_kdc.c
+@@ -80,6 +80,7 @@
+ #include <openssl/x509.h>
+ #include <openssl/x509v3.h>
+ #include <dirent.h>
++#include "checkhost.h"
+ #endif
+
+ #define MAX_PASS 3
+@@ -148,6 +149,7 @@ struct conn_state {
+ krb5_boolean defer;
+ struct {
+ const char *uri_path;
++ const char *servername;
+ char *https_request;
+ #ifdef PROXY_TLS_IMPL_OPENSSL
+ SSL *ssl;
+@@ -158,6 +160,7 @@ struct conn_state {
+ #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
+@@ -169,6 +172,7 @@ k5_sendto_kdc_initialize(void)
+ OpenSSL_add_all_algorithms();
+
+ 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
+ }
+
+@@ -635,7 +639,8 @@ set_transport_message(struct conn_state *state, const krb5_data *realm,
+ static krb5_error_code
+ add_connection(struct conn_state **conns, k5_transport transport,
+ krb5_boolean defer, struct addrinfo *ai, size_t server_index,
+- const krb5_data *realm, const char *uri_path, char **udpbufp)
++ const krb5_data *realm, const char *hostname,
++ const char *uri_path, char **udpbufp)
+ {
+ struct conn_state *state, **tailptr;
+
+@@ -661,6 +666,7 @@ add_connection(struct conn_state **conns, k5_transport transport,
+ state->service_write = service_https_write;
+ state->service_read = service_https_read;
+ state->http.uri_path = uri_path;
++ state->http.servername = hostname;
+ } else {
+ state->service_connect = NULL;
+ state->service_write = NULL;
+@@ -760,8 +766,8 @@ resolve_server(krb5_context context, const krb5_data *realm,
+ ai.ai_addrlen = entry->addrlen;
+ ai.ai_addr = (struct sockaddr *)&entry->addr;
+ defer = (entry->transport != transport);
+- return add_connection(conns, entry->transport, defer, &ai, ind,
+- realm, entry->uri_path, udpbufp);
++ return add_connection(conns, entry->transport, defer, &ai, ind, realm,
++ NULL, entry->uri_path, udpbufp);
+ }
+
+ /* If the entry has a specified transport, use it. */
+@@ -787,7 +793,7 @@ resolve_server(krb5_context context, const krb5_data *realm,
+ retval = 0;
+ for (a = addrs; a != 0 && retval == 0; a = a->ai_next) {
+ retval = add_connection(conns, transport, FALSE, a, ind, realm,
+- entry->uri_path, udpbufp);
++ entry->hostname, entry->uri_path, udpbufp);
+ }
+
+ /* For TCP_OR_UDP entries, add each address again with the non-preferred
+@@ -797,7 +803,7 @@ resolve_server(krb5_context context, const krb5_data *realm,
+ 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, realm,
+- entry->uri_path, udpbufp);
++ entry->hostname, entry->uri_path, udpbufp);
+ }
+ }
+ freeaddrinfo(addrs);
+@@ -1260,6 +1266,20 @@ cleanup:
+ 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);
++ }
++}
++
+ static int
+ ssl_verify_callback(int preverify_ok, X509_STORE_CTX *store_ctx)
+ {
+@@ -1268,12 +1288,14 @@ ssl_verify_callback(int preverify_ok, X509_STORE_CTX *store_ctx)
+ BIO *bio;
+ krb5_context context;
+ int err, depth;
+- const char *cert = NULL, *errstr;
++ 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) {
+@@ -1299,8 +1321,19 @@ ssl_verify_callback(int preverify_ok, X509_STORE_CTX *store_ctx)
+ }
+ return 0;
+ }
+- /* All done. */
+- return 1;
++ /* 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);
++ }
++ /* The name didn't match. */
++ return 0;
+ }
+
+ /*
+@@ -1317,7 +1350,7 @@ setup_ssl(krb5_context context, const krb5_data *realm,
+ SSL_CTX *ctx = NULL;
+ SSL *ssl = NULL;
+
+- if (ssl_ex_context_id == -1)
++ if (ssl_ex_context_id == -1 || ssl_ex_conn_id == -1)
+ goto kill_conn;
+
+ /* Do general SSL library setup. */
+@@ -1339,6 +1372,8 @@ setup_ssl(krb5_context context, const krb5_data *realm,
+
+ 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))
+--
+2.1.0
+
diff --git a/0010-Add-some-longer-form-docs-for-HTTPS.patch b/0010-Add-some-longer-form-docs-for-HTTPS.patch
new file mode 100644
index 0000000..88f1327
--- /dev/null
+++ b/0010-Add-some-longer-form-docs-for-HTTPS.patch
@@ -0,0 +1,86 @@
+From b52acabf478e8d1aa19f7823aade81eed1553143 Mon Sep 17 00:00:00 2001
+From: Nalin Dahyabhai <nalin@dahyabhai.net>
+Date: Tue, 22 Apr 2014 16:31:14 -0400
+Subject: [PATCH 10/13] Add some longer-form docs for HTTPS
+
+Add some longer-form documentation for the new HTTPS support, walking a
+prospective administrator through generating a bare minimal signing
+setup, deploying a WSGI-based proxy server onto an Apache httpd server
+using mod_ssl and mod_wsgi, and configuring clients to use it.
+
+ticket: 7929
+---
+ doc/admin/https.rst | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
+ doc/admin/index.rst | 1 +
+ 2 files changed, 49 insertions(+)
+ create mode 100644 doc/admin/https.rst
+
+diff --git a/doc/admin/https.rst b/doc/admin/https.rst
+new file mode 100644
+index 0000000..b4e68b2
+--- /dev/null
++++ b/doc/admin/https.rst
+@@ -0,0 +1,48 @@
++.. _https:
++
++HTTPS proxy configuration
++=========================
++
++In addition to being able to use UDP or TCP to communicate directly
++with a KDC as is outlined in RFC4120, and with kpasswd services in a
++similar fashion, the client libraries can attempt to use an HTTPS
++proxy server to communicate with a KDC or kpasswd service, using the
++protocol outlined in [MS-KKDCP].
++
++Communicating with a KDC through an HTTPS proxy allows clients to
++contact servers when network firewalls might otherwise prevent them
++from doing so. The use of TLS also encrypts all traffic between the
++clients and the KDC, preventing observers from conducting password
++dictionary attacks or from observing the client and server principals
++being authenticated, at additional computational cost to both clients
++and servers.
++
++An HTTPS proxy server is provided as a feature in some versions of
++Microsoft Windows Server, and a WSGI implementation named `kdcproxy`
++is available in the python package index.
++
++
++Configuring the clients
++-----------------------
++
++To use an HTTPS proxy, a client host must trust the CA which issued
++that proxy's SSL certificate. If that CA's certificate is not in the
++system-wide default set of trusted certificates, configure the
++following relation in the client host's :ref:`krb5.conf(5)` file in
++the appropriate :ref:`realms` subsection::
++
++ http_anchors = FILE:/etc/krb5/cacert.pem
++
++Adjust the pathname to match the path of the file which contains a
++copy of the CA's certificate. The `http_anchors` option is documented
++more fully in :ref:`krb5.conf(5)`.
++
++Configure the client to access the KDC and kpasswd service by
++specifying their locations in its :ref:`krb5.conf(5)` file in the form
++of HTTPS URLs for the proxy server::
++
++ kdc = https://server.fqdn/KdcProxy
++ kpasswd_server = https://server.fqdn/KdcProxy
++
++If the proxy and client are properly configured, client commands such
++as ``kinit``, ``kvno``, and ``kpasswd`` should all function normally.
+diff --git a/doc/admin/index.rst b/doc/admin/index.rst
+index 3406843..3cd57f5 100644
+--- a/doc/admin/index.rst
++++ b/doc/admin/index.rst
+@@ -17,6 +17,7 @@ For administrators
+ otp.rst
+ princ_dns.rst
+ enctypes.rst
++ https.rst
+
+ .. toctree::
+ :maxdepth: 1
+--
+2.1.0
+
diff --git a/0011-Have-k5test.py-provide-runenv-to-python-tests.patch b/0011-Have-k5test.py-provide-runenv-to-python-tests.patch
new file mode 100644
index 0000000..218e629
--- /dev/null
+++ b/0011-Have-k5test.py-provide-runenv-to-python-tests.patch
@@ -0,0 +1,64 @@
+From f78f8b1a46534db3a4547323ba952c1fa1b41fe9 Mon Sep 17 00:00:00 2001
+From: Nalin Dahyabhai <nalin@dahyabhai.net>
+Date: Fri, 30 May 2014 17:13:31 -0400
+Subject: [PATCH 11/13] Have k5test.py provide 'runenv' to python tests
+
+Expose the formerly-internal _runenv module as k5test.runenv, so that
+settings we store in the top-level runenv.py will be available to them.
+
+ticket: 7929
+---
+ src/util/k5test.py | 15 ++++++++++-----
+ 1 file changed, 10 insertions(+), 5 deletions(-)
+
+diff --git a/src/util/k5test.py b/src/util/k5test.py
+index a2bfbee..8cb477d 100644
+--- a/src/util/k5test.py
++++ b/src/util/k5test.py
+@@ -177,6 +177,12 @@ Scripts may use the following functions and variables:
+
+ * args: Positional arguments left over after flags are processed.
+
++* runenv: The contents of $srctop/runenv.py, containing a dictionary
++ 'env' which specifies additional variables to be added to the realm
++ environment, and a variable 'proxy_tls_impl', which indicates which
++ SSL implementation (if any) is being used by libkrb5's support for
++ contacting KDCs and kpasswd servers over HTTPS.
++
+ * verbose: Whether the script is running verbosely.
+
+ * testpass: The command-line test pass argument. The script does not
+@@ -526,9 +532,9 @@ def _match_cmdnum(cmdnum, ind):
+ # Return an environment suitable for running programs in the build
+ # tree. It is safe to modify the result.
+ def _build_env():
+- global buildtop, _runenv
++ global buildtop, runenv
+ env = os.environ.copy()
+- for (k, v) in _runenv.iteritems():
++ for (k, v) in runenv.env.iteritems():
+ if v.find('./') == 0:
+ env[k] = os.path.join(buildtop, v)
+ else:
+@@ -544,8 +550,7 @@ def _import_runenv():
+ runenv_py = os.path.join(buildtop, 'runenv.py')
+ if not os.path.exists(runenv_py):
+ fail('You must run "make runenv.py" in %s first.' % buildtop)
+- module = imp.load_source('runenv', runenv_py)
+- return module.env
++ return imp.load_source('runenv', runenv_py)
+
+
+ # Merge the nested dictionaries cfg1 and cfg2 into a new dictionary.
+@@ -1174,7 +1179,7 @@ _cmd_index = 1
+ buildtop = _find_buildtop()
+ srctop = _find_srctop()
+ plugins = os.path.join(buildtop, 'plugins')
+-_runenv = _import_runenv()
++runenv = _import_runenv()
+ hostname = _get_hostname()
+ null_input = open(os.devnull, 'r')
+
+--
+2.1.0
+
diff --git a/0012-Add-a-simple-KDC-proxy-test-server.patch b/0012-Add-a-simple-KDC-proxy-test-server.patch
new file mode 100644
index 0000000..48aa032
--- /dev/null
+++ b/0012-Add-a-simple-KDC-proxy-test-server.patch
@@ -0,0 +1,526 @@
+From 142255ba9af4ce1016a8eadf147e599ee490f1f7 Mon Sep 17 00:00:00 2001
+From: Nalin Dahyabhai <nalin@dahyabhai.net>
+Date: Fri, 7 Feb 2014 18:03:29 -0500
+Subject: [PATCH 12/13] Add a simple KDC proxy test server
+
+This proxy server uses python-paste to run the kdcproxy from
+https://pypi.python.org/pypi/kdcproxy. It should be used along
+with the proxy.pem certificate in ../tests/dejagnu/proxy-certs.
+
+ticket: 7929
+---
+ src/tests/dejagnu/proxy-certs/ca.pem | 28 +++++
+ src/tests/dejagnu/proxy-certs/make-certs.sh | 124 +++++++++++++++++++++++
+ src/tests/dejagnu/proxy-certs/proxy-badsig.pem | 56 ++++++++++
+ src/tests/dejagnu/proxy-certs/proxy-ideal.pem | 56 ++++++++++
+ src/tests/dejagnu/proxy-certs/proxy-no-match.pem | 54 ++++++++++
+ src/tests/dejagnu/proxy-certs/proxy-san.pem | 56 ++++++++++
+ src/tests/dejagnu/proxy-certs/proxy-subject.pem | 54 ++++++++++
+ src/util/paste-kdcproxy.py | 18 ++++
+ 8 files changed, 446 insertions(+)
+ create mode 100644 src/tests/dejagnu/proxy-certs/ca.pem
+ create mode 100755 src/tests/dejagnu/proxy-certs/make-certs.sh
+ create mode 100644 src/tests/dejagnu/proxy-certs/proxy-badsig.pem
+ create mode 100644 src/tests/dejagnu/proxy-certs/proxy-ideal.pem
+ create mode 100644 src/tests/dejagnu/proxy-certs/proxy-no-match.pem
+ create mode 100644 src/tests/dejagnu/proxy-certs/proxy-san.pem
+ create mode 100644 src/tests/dejagnu/proxy-certs/proxy-subject.pem
+ create mode 100755 src/util/paste-kdcproxy.py
+
+diff --git a/src/tests/dejagnu/proxy-certs/ca.pem b/src/tests/dejagnu/proxy-certs/ca.pem
+new file mode 100644
+index 0000000..e0f8dc7
+--- /dev/null
++++ b/src/tests/dejagnu/proxy-certs/ca.pem
+@@ -0,0 +1,28 @@
++-----BEGIN CERTIFICATE-----
++MIIEuzCCA6OgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBmTELMAkGA1UEBhMCVVMx
++FjAUBgNVBAgTDU1hc3NhY2h1c2V0dHMxEjAQBgNVBAcTCUNhbWJyaWRnZTEMMAoG
++A1UEChMDTUlUMSIwIAYDVQQLExlJbnNlY3VyZSBLZXJiZXJvcyB0ZXN0IENBMSww
++KgYDVQQDFCN0ZXN0IHN1aXRlIENBOyBkbyBub3QgdXNlIG90aGVyd2lzZTAeFw0x
++NDA1MDIxOTA2MDhaFw0yNTA0MTQxOTA2MDhaMIGZMQswCQYDVQQGEwJVUzEWMBQG
++A1UECBMNTWFzc2FjaHVzZXR0czESMBAGA1UEBxMJQ2FtYnJpZGdlMQwwCgYDVQQK
++EwNNSVQxIjAgBgNVBAsTGUluc2VjdXJlIEtlcmJlcm9zIHRlc3QgQ0ExLDAqBgNV
++BAMUI3Rlc3Qgc3VpdGUgQ0E7IGRvIG5vdCB1c2Ugb3RoZXJ3aXNlMIIBIjANBgkq
++hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1zudnpN8FP7iLn1vgkyTSn/RQxXx1yt6
++zikHaMrVPjkjXPPUoCFpWS3eeI4aQFoj93L5MwZDmSxOflBAqLwV2AMAacrYnNPJ
++IkHtbYKdVsvw9b4INTWqV9/DOODO7UowyMppmO35/pUXaLL+AjHjLw1/EhQ3ZYtq
++fpAMOkf5TnS5GtqZFlrYgZKE8vTC8BxDKM7FYhWYz7kp/tG3S8O/RTnP7Nd+h1Yd
++pmlHBGfuwIRIJz5xNw6KIcCy3Q0NNoKnh00WVwLmR+x11BGSkMjiZZkwJ5D0RObS
++g13QD/itrGoV2gtPzjQgNPfTrjsMvyOWAAFrWVR3QLTxnnmXsqnXvwIDAQABo4IB
++CjCCAQYwHQYDVR0OBBYEFHO5+DSYzq8rvQhUldyvn0y4AqlHMIHGBgNVHSMEgb4w
++gbuAFHO5+DSYzq8rvQhUldyvn0y4AqlHoYGfpIGcMIGZMQswCQYDVQQGEwJVUzEW
++MBQGA1UECBMNTWFzc2FjaHVzZXR0czESMBAGA1UEBxMJQ2FtYnJpZGdlMQwwCgYD
++VQQKEwNNSVQxIjAgBgNVBAsTGUluc2VjdXJlIEtlcmJlcm9zIHRlc3QgQ0ExLDAq
++BgNVBAMUI3Rlc3Qgc3VpdGUgQ0E7IGRvIG5vdCB1c2Ugb3RoZXJ3aXNlggEBMAsG
++A1UdDwQEAwIB/jAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAM
++Mf4ptC6WoQBH3GoTfgBL0WlIeYeSFmLO7IaSjpK0FV6F/yF7iPFSXcpmu23m6USY
++LRSxnAvxFTi+h1S5Za9O2Pjq88R9nHmesg4v8HJqOw4HpkDowYo2lumjIMfAutyR
++MQUOujYJW1WyZ2PidN5M1exDeMgQN9nVjUCx/WKD9fnzOjOOR1Sc8Us2KpoyccIi
++A+ABHubCvSO3cln0Sp7qjkssJScZtouzPu8FYiroTIR+1oSIKTpJiik1EptlsTea
++L6fHTMHspFhZaiUJFHWTBAgn/dT+UkFntHdHGI6HWBThFVW05hKoarBA7N25W7FN
++AHyfC0lKds4qFiBQkpdi
++-----END CERTIFICATE-----
+diff --git a/src/tests/dejagnu/proxy-certs/make-certs.sh b/src/tests/dejagnu/proxy-certs/make-certs.sh
+new file mode 100755
+index 0000000..1191bf0
+--- /dev/null
++++ b/src/tests/dejagnu/proxy-certs/make-certs.sh
+@@ -0,0 +1,124 @@
++#!/bin/sh -e
++
++PWD=`pwd`
++NAMETYPE=1
++KEYSIZE=2048
++DAYS=4000
++REALM=KRBTEST.COM
++TLS_SERVER_EKU=1.3.6.1.5.5.7.3.1
++PROXY_EKU_LIST=$TLS_SERVER_EKU
++
++cat > openssl.cnf << EOF
++[req]
++prompt = no
++distinguished_name = \$ENV::SUBJECT
++
++[ca]
++default_ca = test_ca
++
++[test_ca]
++new_certs_dir = $PWD
++serial = $PWD/ca.srl
++database = $PWD/ca.db
++certificate = $PWD/ca.pem
++private_key = $PWD/privkey.pem
++default_days = $DAYS
++x509_extensions = exts_proxy
++policy = proxyname
++default_md = sha1
++unique_subject = no
++email_in_dn = no
++
++[signer]
++CN = test CA certificate
++C = US
++ST = Massachusetts
++L = Cambridge
++O = MIT
++OU = Insecure Kerberos test CA
++CN = test suite CA; do not use otherwise
++
++[proxy]
++C = US
++ST = Massachusetts
++O = KRBTEST.COM
++CN = PROXYinSubject
++
++[localhost]
++C = US
++ST = Massachusetts
++O = KRBTEST.COM
++CN = localhost
++
++[proxyname]
++C = supplied
++ST = supplied
++O = supplied
++CN = supplied
++
++[exts_ca]
++subjectKeyIdentifier = hash
++authorityKeyIdentifier = keyid:always,issuer:always
++keyUsage = nonRepudiation,digitalSignature,keyEncipherment,dataEncipherment,keyAgreement,keyCertSign,cRLSign
++basicConstraints = critical,CA:TRUE
++
++[exts_proxy]
++subjectKeyIdentifier = hash
++authorityKeyIdentifier = keyid:always,issuer:always
++keyUsage = nonRepudiation,digitalSignature,keyEncipherment,keyAgreement
++basicConstraints = critical,CA:FALSE
++subjectAltName = DNS:proxyŠubjectÄltÑame,DNS:proxySubjectAltName,IP:127.0.0.1,IP:::1,DNS:localhost
++extendedKeyUsage = $PROXY_EKU_LIST
++
++[exts_proxy_no_san]
++subjectKeyIdentifier = hash
++authorityKeyIdentifier = keyid:always,issuer:always
++keyUsage = nonRepudiation,digitalSignature,keyEncipherment,keyAgreement
++basicConstraints = critical,CA:FALSE
++extendedKeyUsage = $PROXY_EKU_LIST
++EOF
++
++# Generate a private key.
++openssl genrsa $KEYSIZE -nodes > privkey.pem
++
++# Generate a "CA" certificate.
++SUBJECT=signer openssl req -config openssl.cnf -new -x509 -extensions exts_ca \
++ -set_serial 1 -days $DAYS -key privkey.pem -out ca.pem
++
++# Generate proxy certificate signing requests.
++SUBJECT=proxy openssl req -config openssl.cnf -new -key privkey.pem \
++ -out proxy.csr
++SUBJECT=localhost openssl req -config openssl.cnf -new -key privkey.pem \
++ -out localhost.csr
++
++# Issue the certificate with the right name in a subjectAltName.
++echo 02 > ca.srl
++cat /dev/null > ca.db
++SUBJECT=proxy openssl ca -config openssl.cnf -extensions exts_proxy \
++ -batch -days $DAYS -notext -out tmp.pem -in proxy.csr
++cat privkey.pem tmp.pem > proxy-san.pem
++
++# Issue a certificate that only has the name in the subject field
++SUBJECT=proxy openssl ca -config openssl.cnf -extensions exts_proxy_no_san \
++ -batch -days $DAYS -notext -out tmp.pem -in localhost.csr
++cat privkey.pem tmp.pem > proxy-subject.pem
++
++# Issue a certificate that doesn't include any matching name values.
++SUBJECT=proxy openssl ca -config openssl.cnf -extensions exts_proxy_no_san \
++ -batch -days $DAYS -notext -out tmp.pem -in proxy.csr
++cat privkey.pem tmp.pem > proxy-no-match.pem
++
++# Issue a certificate that contains all matching name values.
++SUBJECT=proxy openssl ca -config openssl.cnf -extensions exts_proxy \
++ -batch -days $DAYS -notext -out tmp.pem -in localhost.csr
++cat privkey.pem tmp.pem > proxy-ideal.pem
++
++# Corrupt the signature on the certificate.
++SUBJECT=proxy openssl x509 -outform der -in proxy-ideal.pem -out bad.der
++length=`od -Ad bad.der | tail -n 1 | awk '{print $1}'`
++dd if=/dev/zero bs=1 of=bad.der count=16 seek=`expr $length - 16`
++SUBJECT=proxy openssl x509 -inform der -in bad.der -out tmp.pem
++cat privkey.pem tmp.pem > proxy-badsig.pem
++
++# Clean up.
++rm -f openssl.cnf proxy.csr localhost.csr privkey.pem ca.db ca.db.old ca.srl ca.srl.old ca.db.attr ca.db.attr.old 02.pem 03.pem 04.pem 05.pem tmp.pem bad.der
+diff --git a/src/tests/dejagnu/proxy-certs/proxy-badsig.pem b/src/tests/dejagnu/proxy-certs/proxy-badsig.pem
+new file mode 100644
+index 0000000..2b31f7d
+--- /dev/null
++++ b/src/tests/dejagnu/proxy-certs/proxy-badsig.pem
+@@ -0,0 +1,56 @@
++-----BEGIN RSA PRIVATE KEY-----
++MIIEpQIBAAKCAQEA1zudnpN8FP7iLn1vgkyTSn/RQxXx1yt6zikHaMrVPjkjXPPU
++oCFpWS3eeI4aQFoj93L5MwZDmSxOflBAqLwV2AMAacrYnNPJIkHtbYKdVsvw9b4I
++NTWqV9/DOODO7UowyMppmO35/pUXaLL+AjHjLw1/EhQ3ZYtqfpAMOkf5TnS5GtqZ
++FlrYgZKE8vTC8BxDKM7FYhWYz7kp/tG3S8O/RTnP7Nd+h1YdpmlHBGfuwIRIJz5x
++Nw6KIcCy3Q0NNoKnh00WVwLmR+x11BGSkMjiZZkwJ5D0RObSg13QD/itrGoV2gtP
++zjQgNPfTrjsMvyOWAAFrWVR3QLTxnnmXsqnXvwIDAQABAoIBAQCqvhpeMDXhGgoo
++Q03wmfrGwPsrMv91aIK1hYrhMPdVs1JAbRYiKh8+pcq07FYa8udRaB4UwkVh/+oM
++/nEs6niRsl/jjQ2l68TFrnNByroynvr6l9Q/EeGecF6Ygo7lY1OsFhcLQM5vjarS
++XhxvdU/6hcRmfS8tGRpUaMWqfmpiN3YgJcgt8SoYhiwAYDTMJjNyWC61lO7IqNVR
++4kntiM24sfAu1sdZynX8Gp2GrpNChapEuhilQ8RayjuStEYr2abcSIjfZFHQXN7o
++TnjL+AQUzc/ZTXDGnIe9ZzZeFz8UCueeoN6KPxfrq9UUWRL6qt7gOIMdhYR6lFxt
++6pj6kLhxAoGBAO5DTnTKDfCMY2/AsTzCJvMGSY0bT1rsdDxrpqjrbUSeMHV3s5Lm
++vEPnnm+05FD/vi99+HZjHXAZFkhA3ubij2qWFPBnQ5YUoh17IW/Ae4bzY2uXikgL
++tLZ+R+OrcGYQQlvPn//PLsxbfdk5vraqzm08kIX0T4o4Iz8ST5NFJ8hVAoGBAOdB
++ahXr14563Cjeu0pSQ1nXoz3IXdnDwePXasYhxQHl8Ayk8qZS5pt7r07H3dqq6pvn
++e09gZINJe47B9UhkR3H5bPyz/kujKS4zqo3Zlbryzm3V0BWqjNj+j8E2YuQKNQr+
++c480jn2FzwW66w0i3n4U4KUn1w2/iq5AnVzyNkPDAoGAWLYEsyU79XE/4K79DqM3
++P0r6/afKbw8U5B4syj4FzAOeBU6RNMPmGt5VNkBCtgnSdPpRFTsoDcG5cyN8GrkG
++Lug8WZoJJwr9pT5gH6yqEX/zZ27f1J1PJpd0CsedLNMm8eonJ2arhPkXrVZ7tKV6
++AGAJa2agatUmAmi96hZYjpUCgYEA32abJEgsedEIhFb/GYI03ELryRCaUXfCA+gj
++lvoihn3qE1z5qGGns4adyX5dPRQmBqxtvDXDg+zl9vg6i0+MkXdCqTD8tXcOnjp9
++RgFvmyVa9FI8beHPpQTuPNncWK3fpho/6pT8Hhi48LEsxwjrZWOnzQSaxQZH46Q6
++IQNAFt8CgYEAkflxXvA2/2naix+riaBzv5EVJB7ilbfWiWtq2LEAtwrQ5XNFjrtK
++g45jKrZ/ezAzTfPa5Dwn4xcImd0MIavnJhDu2ATxMGB0GATLlDH2HZvU7UwKLpTW
++6Hlol4yRcX4GSEOxJ2ZpWYNIOYH0yDf1qLJXs1j8Fi3zWRe+V1kff4w=
++-----END RSA PRIVATE KEY-----
++-----BEGIN CERTIFICATE-----
++MIIE3TCCA8WgAwIBAgIBBTANBgkqhkiG9w0BAQUFADCBmTELMAkGA1UEBhMCVVMx
++FjAUBgNVBAgTDU1hc3NhY2h1c2V0dHMxEjAQBgNVBAcTCUNhbWJyaWRnZTEMMAoG
++A1UEChMDTUlUMSIwIAYDVQQLExlJbnNlY3VyZSBLZXJiZXJvcyB0ZXN0IENBMSww
++KgYDVQQDFCN0ZXN0IHN1aXRlIENBOyBkbyBub3QgdXNlIG90aGVyd2lzZTAeFw0x
++NDA1MDIxOTA2MDlaFw0yNTA0MTQxOTA2MDlaME8xCzAJBgNVBAYTAlVTMRYwFAYD
++VQQIEw1NYXNzYWNodXNldHRzMRQwEgYDVQQKEwtLUkJURVNULkNPTTESMBAGA1UE
++AxMJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1zud
++npN8FP7iLn1vgkyTSn/RQxXx1yt6zikHaMrVPjkjXPPUoCFpWS3eeI4aQFoj93L5
++MwZDmSxOflBAqLwV2AMAacrYnNPJIkHtbYKdVsvw9b4INTWqV9/DOODO7UowyMpp
++mO35/pUXaLL+AjHjLw1/EhQ3ZYtqfpAMOkf5TnS5GtqZFlrYgZKE8vTC8BxDKM7F
++YhWYz7kp/tG3S8O/RTnP7Nd+h1YdpmlHBGfuwIRIJz5xNw6KIcCy3Q0NNoKnh00W
++VwLmR+x11BGSkMjiZZkwJ5D0RObSg13QD/itrGoV2gtPzjQgNPfTrjsMvyOWAAFr
++WVR3QLTxnnmXsqnXvwIDAQABo4IBdzCCAXMwHQYDVR0OBBYEFHO5+DSYzq8rvQhU
++ldyvn0y4AqlHMIHGBgNVHSMEgb4wgbuAFHO5+DSYzq8rvQhUldyvn0y4AqlHoYGf
++pIGcMIGZMQswCQYDVQQGEwJVUzEWMBQGA1UECBMNTWFzc2FjaHVzZXR0czESMBAG
++A1UEBxMJQ2FtYnJpZGdlMQwwCgYDVQQKEwNNSVQxIjAgBgNVBAsTGUluc2VjdXJl
++IEtlcmJlcm9zIHRlc3QgQ0ExLDAqBgNVBAMUI3Rlc3Qgc3VpdGUgQ0E7IGRvIG5v
++dCB1c2Ugb3RoZXJ3aXNlggEBMAsGA1UdDwQEAwID6DAMBgNVHRMBAf8EAjAAMFkG
++A1UdEQRSMFCCFnByb3h5xaB1YmplY3TDhGx0w5FhbWWCE3Byb3h5U3ViamVjdEFs
++dE5hbWWHBH8AAAGHEAAAAAAAAAAAAAAAAAAAAAGCCWxvY2FsaG9zdDATBgNVHSUE
++DDAKBggrBgEFBQcDATANBgkqhkiG9w0BAQUFAAOCAQEAfTctgFjQSaevBi64q7yh
++GNsK3PqeNEALZz4pSXRbOwm0E4RpYIS7uqg1C4zJ5Zbd4V/dOX7q+T/iBS7gErzS
++rj21jH3Ggc92TmXzcFxMDCxLV0hO8xFkqg3P4sslJESOHxvEMTTf5s893yUb8vJ/
++DCvZXXRoRwPot9MFozkmcQcaTNunREWFvn4i4JXcMCSAfWTd+/VkpVsy69u3tj68
++7G2/K5nalvZikutEC+DyfyBuvDAoxIYzCi3VtQxCalW28Q5hzWV21QsvKTP5QBsh
++RaU2r0O58lZPPvrOrtWQBCudUgsnoraVLrjJshEQ4z/ZAAAAAAAAAAAAAAAAAAAA
++AA==
++-----END CERTIFICATE-----
+diff --git a/src/tests/dejagnu/proxy-certs/proxy-ideal.pem b/src/tests/dejagnu/proxy-certs/proxy-ideal.pem
+new file mode 100644
+index 0000000..4588f7d
+--- /dev/null
++++ b/src/tests/dejagnu/proxy-certs/proxy-ideal.pem
+@@ -0,0 +1,56 @@
++-----BEGIN RSA PRIVATE KEY-----
++MIIEpQIBAAKCAQEA1zudnpN8FP7iLn1vgkyTSn/RQxXx1yt6zikHaMrVPjkjXPPU
++oCFpWS3eeI4aQFoj93L5MwZDmSxOflBAqLwV2AMAacrYnNPJIkHtbYKdVsvw9b4I
++NTWqV9/DOODO7UowyMppmO35/pUXaLL+AjHjLw1/EhQ3ZYtqfpAMOkf5TnS5GtqZ
++FlrYgZKE8vTC8BxDKM7FYhWYz7kp/tG3S8O/RTnP7Nd+h1YdpmlHBGfuwIRIJz5x
++Nw6KIcCy3Q0NNoKnh00WVwLmR+x11BGSkMjiZZkwJ5D0RObSg13QD/itrGoV2gtP
++zjQgNPfTrjsMvyOWAAFrWVR3QLTxnnmXsqnXvwIDAQABAoIBAQCqvhpeMDXhGgoo
++Q03wmfrGwPsrMv91aIK1hYrhMPdVs1JAbRYiKh8+pcq07FYa8udRaB4UwkVh/+oM
++/nEs6niRsl/jjQ2l68TFrnNByroynvr6l9Q/EeGecF6Ygo7lY1OsFhcLQM5vjarS
++XhxvdU/6hcRmfS8tGRpUaMWqfmpiN3YgJcgt8SoYhiwAYDTMJjNyWC61lO7IqNVR
++4kntiM24sfAu1sdZynX8Gp2GrpNChapEuhilQ8RayjuStEYr2abcSIjfZFHQXN7o
++TnjL+AQUzc/ZTXDGnIe9ZzZeFz8UCueeoN6KPxfrq9UUWRL6qt7gOIMdhYR6lFxt
++6pj6kLhxAoGBAO5DTnTKDfCMY2/AsTzCJvMGSY0bT1rsdDxrpqjrbUSeMHV3s5Lm
++vEPnnm+05FD/vi99+HZjHXAZFkhA3ubij2qWFPBnQ5YUoh17IW/Ae4bzY2uXikgL
++tLZ+R+OrcGYQQlvPn//PLsxbfdk5vraqzm08kIX0T4o4Iz8ST5NFJ8hVAoGBAOdB
++ahXr14563Cjeu0pSQ1nXoz3IXdnDwePXasYhxQHl8Ayk8qZS5pt7r07H3dqq6pvn
++e09gZINJe47B9UhkR3H5bPyz/kujKS4zqo3Zlbryzm3V0BWqjNj+j8E2YuQKNQr+
++c480jn2FzwW66w0i3n4U4KUn1w2/iq5AnVzyNkPDAoGAWLYEsyU79XE/4K79DqM3
++P0r6/afKbw8U5B4syj4FzAOeBU6RNMPmGt5VNkBCtgnSdPpRFTsoDcG5cyN8GrkG
++Lug8WZoJJwr9pT5gH6yqEX/zZ27f1J1PJpd0CsedLNMm8eonJ2arhPkXrVZ7tKV6
++AGAJa2agatUmAmi96hZYjpUCgYEA32abJEgsedEIhFb/GYI03ELryRCaUXfCA+gj
++lvoihn3qE1z5qGGns4adyX5dPRQmBqxtvDXDg+zl9vg6i0+MkXdCqTD8tXcOnjp9
++RgFvmyVa9FI8beHPpQTuPNncWK3fpho/6pT8Hhi48LEsxwjrZWOnzQSaxQZH46Q6
++IQNAFt8CgYEAkflxXvA2/2naix+riaBzv5EVJB7ilbfWiWtq2LEAtwrQ5XNFjrtK
++g45jKrZ/ezAzTfPa5Dwn4xcImd0MIavnJhDu2ATxMGB0GATLlDH2HZvU7UwKLpTW
++6Hlol4yRcX4GSEOxJ2ZpWYNIOYH0yDf1qLJXs1j8Fi3zWRe+V1kff4w=
++-----END RSA PRIVATE KEY-----
++-----BEGIN CERTIFICATE-----
++MIIE3TCCA8WgAwIBAgIBBTANBgkqhkiG9w0BAQUFADCBmTELMAkGA1UEBhMCVVMx
++FjAUBgNVBAgTDU1hc3NhY2h1c2V0dHMxEjAQBgNVBAcTCUNhbWJyaWRnZTEMMAoG
++A1UEChMDTUlUMSIwIAYDVQQLExlJbnNlY3VyZSBLZXJiZXJvcyB0ZXN0IENBMSww
++KgYDVQQDFCN0ZXN0IHN1aXRlIENBOyBkbyBub3QgdXNlIG90aGVyd2lzZTAeFw0x
++NDA1MDIxOTA2MDlaFw0yNTA0MTQxOTA2MDlaME8xCzAJBgNVBAYTAlVTMRYwFAYD
++VQQIEw1NYXNzYWNodXNldHRzMRQwEgYDVQQKEwtLUkJURVNULkNPTTESMBAGA1UE
++AxMJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1zud
++npN8FP7iLn1vgkyTSn/RQxXx1yt6zikHaMrVPjkjXPPUoCFpWS3eeI4aQFoj93L5
++MwZDmSxOflBAqLwV2AMAacrYnNPJIkHtbYKdVsvw9b4INTWqV9/DOODO7UowyMpp
++mO35/pUXaLL+AjHjLw1/EhQ3ZYtqfpAMOkf5TnS5GtqZFlrYgZKE8vTC8BxDKM7F
++YhWYz7kp/tG3S8O/RTnP7Nd+h1YdpmlHBGfuwIRIJz5xNw6KIcCy3Q0NNoKnh00W
++VwLmR+x11BGSkMjiZZkwJ5D0RObSg13QD/itrGoV2gtPzjQgNPfTrjsMvyOWAAFr
++WVR3QLTxnnmXsqnXvwIDAQABo4IBdzCCAXMwHQYDVR0OBBYEFHO5+DSYzq8rvQhU
++ldyvn0y4AqlHMIHGBgNVHSMEgb4wgbuAFHO5+DSYzq8rvQhUldyvn0y4AqlHoYGf
++pIGcMIGZMQswCQYDVQQGEwJVUzEWMBQGA1UECBMNTWFzc2FjaHVzZXR0czESMBAG
++A1UEBxMJQ2FtYnJpZGdlMQwwCgYDVQQKEwNNSVQxIjAgBgNVBAsTGUluc2VjdXJl
++IEtlcmJlcm9zIHRlc3QgQ0ExLDAqBgNVBAMUI3Rlc3Qgc3VpdGUgQ0E7IGRvIG5v
++dCB1c2Ugb3RoZXJ3aXNlggEBMAsGA1UdDwQEAwID6DAMBgNVHRMBAf8EAjAAMFkG
++A1UdEQRSMFCCFnByb3h5xaB1YmplY3TDhGx0w5FhbWWCE3Byb3h5U3ViamVjdEFs
++dE5hbWWHBH8AAAGHEAAAAAAAAAAAAAAAAAAAAAGCCWxvY2FsaG9zdDATBgNVHSUE
++DDAKBggrBgEFBQcDATANBgkqhkiG9w0BAQUFAAOCAQEAfTctgFjQSaevBi64q7yh
++GNsK3PqeNEALZz4pSXRbOwm0E4RpYIS7uqg1C4zJ5Zbd4V/dOX7q+T/iBS7gErzS
++rj21jH3Ggc92TmXzcFxMDCxLV0hO8xFkqg3P4sslJESOHxvEMTTf5s893yUb8vJ/
++DCvZXXRoRwPot9MFozkmcQcaTNunREWFvn4i4JXcMCSAfWTd+/VkpVsy69u3tj68
++7G2/K5nalvZikutEC+DyfyBuvDAoxIYzCi3VtQxCalW28Q5hzWV21QsvKTP5QBsh
++RaU2r0O58lZPPvrOrtWQBCudUgsnoraVLrjJshEQ4z/ZA9fVtX2ndCSIoyWpWk01
++gQ==
++-----END CERTIFICATE-----
+diff --git a/src/tests/dejagnu/proxy-certs/proxy-no-match.pem b/src/tests/dejagnu/proxy-certs/proxy-no-match.pem
+new file mode 100644
+index 0000000..a97c1c7
+--- /dev/null
++++ b/src/tests/dejagnu/proxy-certs/proxy-no-match.pem
+@@ -0,0 +1,54 @@
++-----BEGIN RSA PRIVATE KEY-----
++MIIEpQIBAAKCAQEA1zudnpN8FP7iLn1vgkyTSn/RQxXx1yt6zikHaMrVPjkjXPPU
++oCFpWS3eeI4aQFoj93L5MwZDmSxOflBAqLwV2AMAacrYnNPJIkHtbYKdVsvw9b4I
++NTWqV9/DOODO7UowyMppmO35/pUXaLL+AjHjLw1/EhQ3ZYtqfpAMOkf5TnS5GtqZ
++FlrYgZKE8vTC8BxDKM7FYhWYz7kp/tG3S8O/RTnP7Nd+h1YdpmlHBGfuwIRIJz5x
++Nw6KIcCy3Q0NNoKnh00WVwLmR+x11BGSkMjiZZkwJ5D0RObSg13QD/itrGoV2gtP
++zjQgNPfTrjsMvyOWAAFrWVR3QLTxnnmXsqnXvwIDAQABAoIBAQCqvhpeMDXhGgoo
++Q03wmfrGwPsrMv91aIK1hYrhMPdVs1JAbRYiKh8+pcq07FYa8udRaB4UwkVh/+oM
++/nEs6niRsl/jjQ2l68TFrnNByroynvr6l9Q/EeGecF6Ygo7lY1OsFhcLQM5vjarS
++XhxvdU/6hcRmfS8tGRpUaMWqfmpiN3YgJcgt8SoYhiwAYDTMJjNyWC61lO7IqNVR
++4kntiM24sfAu1sdZynX8Gp2GrpNChapEuhilQ8RayjuStEYr2abcSIjfZFHQXN7o
++TnjL+AQUzc/ZTXDGnIe9ZzZeFz8UCueeoN6KPxfrq9UUWRL6qt7gOIMdhYR6lFxt
++6pj6kLhxAoGBAO5DTnTKDfCMY2/AsTzCJvMGSY0bT1rsdDxrpqjrbUSeMHV3s5Lm
++vEPnnm+05FD/vi99+HZjHXAZFkhA3ubij2qWFPBnQ5YUoh17IW/Ae4bzY2uXikgL
++tLZ+R+OrcGYQQlvPn//PLsxbfdk5vraqzm08kIX0T4o4Iz8ST5NFJ8hVAoGBAOdB
++ahXr14563Cjeu0pSQ1nXoz3IXdnDwePXasYhxQHl8Ayk8qZS5pt7r07H3dqq6pvn
++e09gZINJe47B9UhkR3H5bPyz/kujKS4zqo3Zlbryzm3V0BWqjNj+j8E2YuQKNQr+
++c480jn2FzwW66w0i3n4U4KUn1w2/iq5AnVzyNkPDAoGAWLYEsyU79XE/4K79DqM3
++P0r6/afKbw8U5B4syj4FzAOeBU6RNMPmGt5VNkBCtgnSdPpRFTsoDcG5cyN8GrkG
++Lug8WZoJJwr9pT5gH6yqEX/zZ27f1J1PJpd0CsedLNMm8eonJ2arhPkXrVZ7tKV6
++AGAJa2agatUmAmi96hZYjpUCgYEA32abJEgsedEIhFb/GYI03ELryRCaUXfCA+gj
++lvoihn3qE1z5qGGns4adyX5dPRQmBqxtvDXDg+zl9vg6i0+MkXdCqTD8tXcOnjp9
++RgFvmyVa9FI8beHPpQTuPNncWK3fpho/6pT8Hhi48LEsxwjrZWOnzQSaxQZH46Q6
++IQNAFt8CgYEAkflxXvA2/2naix+riaBzv5EVJB7ilbfWiWtq2LEAtwrQ5XNFjrtK
++g45jKrZ/ezAzTfPa5Dwn4xcImd0MIavnJhDu2ATxMGB0GATLlDH2HZvU7UwKLpTW
++6Hlol4yRcX4GSEOxJ2ZpWYNIOYH0yDf1qLJXs1j8Fi3zWRe+V1kff4w=
++-----END RSA PRIVATE KEY-----
++-----BEGIN CERTIFICATE-----
++MIIEhzCCA2+gAwIBAgIBBDANBgkqhkiG9w0BAQUFADCBmTELMAkGA1UEBhMCVVMx
++FjAUBgNVBAgTDU1hc3NhY2h1c2V0dHMxEjAQBgNVBAcTCUNhbWJyaWRnZTEMMAoG
++A1UEChMDTUlUMSIwIAYDVQQLExlJbnNlY3VyZSBLZXJiZXJvcyB0ZXN0IENBMSww
++KgYDVQQDFCN0ZXN0IHN1aXRlIENBOyBkbyBub3QgdXNlIG90aGVyd2lzZTAeFw0x
++NDA1MDIxOTA2MDhaFw0yNTA0MTQxOTA2MDhaMFQxCzAJBgNVBAYTAlVTMRYwFAYD
++VQQIEw1NYXNzYWNodXNldHRzMRQwEgYDVQQKEwtLUkJURVNULkNPTTEXMBUGA1UE
++AxMOUFJPWFlpblN1YmplY3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
++AQDXO52ek3wU/uIufW+CTJNKf9FDFfHXK3rOKQdoytU+OSNc89SgIWlZLd54jhpA
++WiP3cvkzBkOZLE5+UECovBXYAwBpytic08kiQe1tgp1Wy/D1vgg1NapX38M44M7t
++SjDIymmY7fn+lRdosv4CMeMvDX8SFDdli2p+kAw6R/lOdLka2pkWWtiBkoTy9MLw
++HEMozsViFZjPuSn+0bdLw79FOc/s136HVh2maUcEZ+7AhEgnPnE3DoohwLLdDQ02
++gqeHTRZXAuZH7HXUEZKQyOJlmTAnkPRE5tKDXdAP+K2sahXaC0/ONCA099OuOwy/
++I5YAAWtZVHdAtPGeeZeyqde/AgMBAAGjggEcMIIBGDAdBgNVHQ4EFgQUc7n4NJjO
++ryu9CFSV3K+fTLgCqUcwgcYGA1UdIwSBvjCBu4AUc7n4NJjOryu9CFSV3K+fTLgC
++qUehgZ+kgZwwgZkxCzAJBgNVBAYTAlVTMRYwFAYDVQQIEw1NYXNzYWNodXNldHRz
++MRIwEAYDVQQHEwlDYW1icmlkZ2UxDDAKBgNVBAoTA01JVDEiMCAGA1UECxMZSW5z
++ZWN1cmUgS2VyYmVyb3MgdGVzdCBDQTEsMCoGA1UEAxQjdGVzdCBzdWl0ZSBDQTsg
++ZG8gbm90IHVzZSBvdGhlcndpc2WCAQEwCwYDVR0PBAQDAgPoMAwGA1UdEwEB/wQC
++MAAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcNAQEFBQADggEBAMsP++r4
++vki0mBJg3POpp0i+H6zNMimoYLLtM5NvwXinfFuFQKbwLm8QWuHVifjfCYxMUm+l
++iL5cS/bq+SUWGDmrlOhsuu4+aYaxgNiEyki5Rol6miSOHbfOhzX8yp0EBPpq08dg
++SEdrTd/FIl4qgkkb1A4RJYZRErn/fbsyjJN66KIfSOXJuC8XMBf03Vw9f2rdrHJa
++r5lVGvqa4wjO2MPq9vVK52VFrbU/zuyyCUtggyIOwGLGSY0Axtbci+IHToDBQes+
++6W4WwSUCssWfIZXQDLjFw1oRHnN43fXmX5vsVLi7YvOFHOAa1BDnDtCTZit26xVA
++Mdic66hR2jHP0TE=
++-----END CERTIFICATE-----
+diff --git a/src/tests/dejagnu/proxy-certs/proxy-san.pem b/src/tests/dejagnu/proxy-certs/proxy-san.pem
+new file mode 100644
+index 0000000..ac8bbaa
+--- /dev/null
++++ b/src/tests/dejagnu/proxy-certs/proxy-san.pem
+@@ -0,0 +1,56 @@
++-----BEGIN RSA PRIVATE KEY-----
++MIIEpQIBAAKCAQEA1zudnpN8FP7iLn1vgkyTSn/RQxXx1yt6zikHaMrVPjkjXPPU
++oCFpWS3eeI4aQFoj93L5MwZDmSxOflBAqLwV2AMAacrYnNPJIkHtbYKdVsvw9b4I
++NTWqV9/DOODO7UowyMppmO35/pUXaLL+AjHjLw1/EhQ3ZYtqfpAMOkf5TnS5GtqZ
++FlrYgZKE8vTC8BxDKM7FYhWYz7kp/tG3S8O/RTnP7Nd+h1YdpmlHBGfuwIRIJz5x
++Nw6KIcCy3Q0NNoKnh00WVwLmR+x11BGSkMjiZZkwJ5D0RObSg13QD/itrGoV2gtP
++zjQgNPfTrjsMvyOWAAFrWVR3QLTxnnmXsqnXvwIDAQABAoIBAQCqvhpeMDXhGgoo
++Q03wmfrGwPsrMv91aIK1hYrhMPdVs1JAbRYiKh8+pcq07FYa8udRaB4UwkVh/+oM
++/nEs6niRsl/jjQ2l68TFrnNByroynvr6l9Q/EeGecF6Ygo7lY1OsFhcLQM5vjarS
++XhxvdU/6hcRmfS8tGRpUaMWqfmpiN3YgJcgt8SoYhiwAYDTMJjNyWC61lO7IqNVR
++4kntiM24sfAu1sdZynX8Gp2GrpNChapEuhilQ8RayjuStEYr2abcSIjfZFHQXN7o
++TnjL+AQUzc/ZTXDGnIe9ZzZeFz8UCueeoN6KPxfrq9UUWRL6qt7gOIMdhYR6lFxt
++6pj6kLhxAoGBAO5DTnTKDfCMY2/AsTzCJvMGSY0bT1rsdDxrpqjrbUSeMHV3s5Lm
++vEPnnm+05FD/vi99+HZjHXAZFkhA3ubij2qWFPBnQ5YUoh17IW/Ae4bzY2uXikgL
++tLZ+R+OrcGYQQlvPn//PLsxbfdk5vraqzm08kIX0T4o4Iz8ST5NFJ8hVAoGBAOdB
++ahXr14563Cjeu0pSQ1nXoz3IXdnDwePXasYhxQHl8Ayk8qZS5pt7r07H3dqq6pvn
++e09gZINJe47B9UhkR3H5bPyz/kujKS4zqo3Zlbryzm3V0BWqjNj+j8E2YuQKNQr+
++c480jn2FzwW66w0i3n4U4KUn1w2/iq5AnVzyNkPDAoGAWLYEsyU79XE/4K79DqM3
++P0r6/afKbw8U5B4syj4FzAOeBU6RNMPmGt5VNkBCtgnSdPpRFTsoDcG5cyN8GrkG
++Lug8WZoJJwr9pT5gH6yqEX/zZ27f1J1PJpd0CsedLNMm8eonJ2arhPkXrVZ7tKV6
++AGAJa2agatUmAmi96hZYjpUCgYEA32abJEgsedEIhFb/GYI03ELryRCaUXfCA+gj
++lvoihn3qE1z5qGGns4adyX5dPRQmBqxtvDXDg+zl9vg6i0+MkXdCqTD8tXcOnjp9
++RgFvmyVa9FI8beHPpQTuPNncWK3fpho/6pT8Hhi48LEsxwjrZWOnzQSaxQZH46Q6
++IQNAFt8CgYEAkflxXvA2/2naix+riaBzv5EVJB7ilbfWiWtq2LEAtwrQ5XNFjrtK
++g45jKrZ/ezAzTfPa5Dwn4xcImd0MIavnJhDu2ATxMGB0GATLlDH2HZvU7UwKLpTW
++6Hlol4yRcX4GSEOxJ2ZpWYNIOYH0yDf1qLJXs1j8Fi3zWRe+V1kff4w=
++-----END RSA PRIVATE KEY-----
++-----BEGIN CERTIFICATE-----
++MIIE4jCCA8qgAwIBAgIBAjANBgkqhkiG9w0BAQUFADCBmTELMAkGA1UEBhMCVVMx
++FjAUBgNVBAgTDU1hc3NhY2h1c2V0dHMxEjAQBgNVBAcTCUNhbWJyaWRnZTEMMAoG
++A1UEChMDTUlUMSIwIAYDVQQLExlJbnNlY3VyZSBLZXJiZXJvcyB0ZXN0IENBMSww
++KgYDVQQDFCN0ZXN0IHN1aXRlIENBOyBkbyBub3QgdXNlIG90aGVyd2lzZTAeFw0x
++NDA1MDIxOTA2MDhaFw0yNTA0MTQxOTA2MDhaMFQxCzAJBgNVBAYTAlVTMRYwFAYD
++VQQIEw1NYXNzYWNodXNldHRzMRQwEgYDVQQKEwtLUkJURVNULkNPTTEXMBUGA1UE
++AxMOUFJPWFlpblN1YmplY3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
++AQDXO52ek3wU/uIufW+CTJNKf9FDFfHXK3rOKQdoytU+OSNc89SgIWlZLd54jhpA
++WiP3cvkzBkOZLE5+UECovBXYAwBpytic08kiQe1tgp1Wy/D1vgg1NapX38M44M7t
++SjDIymmY7fn+lRdosv4CMeMvDX8SFDdli2p+kAw6R/lOdLka2pkWWtiBkoTy9MLw
++HEMozsViFZjPuSn+0bdLw79FOc/s136HVh2maUcEZ+7AhEgnPnE3DoohwLLdDQ02
++gqeHTRZXAuZH7HXUEZKQyOJlmTAnkPRE5tKDXdAP+K2sahXaC0/ONCA099OuOwy/
++I5YAAWtZVHdAtPGeeZeyqde/AgMBAAGjggF3MIIBczAdBgNVHQ4EFgQUc7n4NJjO
++ryu9CFSV3K+fTLgCqUcwgcYGA1UdIwSBvjCBu4AUc7n4NJjOryu9CFSV3K+fTLgC
++qUehgZ+kgZwwgZkxCzAJBgNVBAYTAlVTMRYwFAYDVQQIEw1NYXNzYWNodXNldHRz
++MRIwEAYDVQQHEwlDYW1icmlkZ2UxDDAKBgNVBAoTA01JVDEiMCAGA1UECxMZSW5z
++ZWN1cmUgS2VyYmVyb3MgdGVzdCBDQTEsMCoGA1UEAxQjdGVzdCBzdWl0ZSBDQTsg
++ZG8gbm90IHVzZSBvdGhlcndpc2WCAQEwCwYDVR0PBAQDAgPoMAwGA1UdEwEB/wQC
++MAAwWQYDVR0RBFIwUIIWcHJveHnFoHViamVjdMOEbHTDkWFtZYITcHJveHlTdWJq
++ZWN0QWx0TmFtZYcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAAYIJbG9jYWxob3N0MBMG
++A1UdJQQMMAoGCCsGAQUFBwMBMA0GCSqGSIb3DQEBBQUAA4IBAQAH6AWuyRLzMbKq
++MUlyg9ZIar8p0Ms0/UEaa6Xm3/cfm6HSujtgcYlDN3M86Z3zWzWdTrOHsRr/YSG3
++H3YDhJToKqxcjgho+1xdBPm0xuFsJcypRqGj/mIaJSoa+wC2AdY1EdE+URsh87XC
++SHYNbxAVo8qBHMjtROm6AKb2YusYqHnkT+U6nc4Pn9UnIzmu4wfoSB+X1vtY24TP
++AtXNYQEG4BkgSrcsgoL+z/+wtZLU8QFk6JRO7Bedq711Oh/taEasZHjRAmnqC5TB
++Ab2fnwWuoVZHqz2qydeywXUKrZlctuRVdjE++wOt9xuMPKFGo0PKDw/SymCe61Q8
++Nc/d2mhz
++-----END CERTIFICATE-----
+diff --git a/src/tests/dejagnu/proxy-certs/proxy-subject.pem b/src/tests/dejagnu/proxy-certs/proxy-subject.pem
+new file mode 100644
+index 0000000..e17918f
+--- /dev/null
++++ b/src/tests/dejagnu/proxy-certs/proxy-subject.pem
+@@ -0,0 +1,54 @@
++-----BEGIN RSA PRIVATE KEY-----
++MIIEpQIBAAKCAQEA1zudnpN8FP7iLn1vgkyTSn/RQxXx1yt6zikHaMrVPjkjXPPU
++oCFpWS3eeI4aQFoj93L5MwZDmSxOflBAqLwV2AMAacrYnNPJIkHtbYKdVsvw9b4I
++NTWqV9/DOODO7UowyMppmO35/pUXaLL+AjHjLw1/EhQ3ZYtqfpAMOkf5TnS5GtqZ
++FlrYgZKE8vTC8BxDKM7FYhWYz7kp/tG3S8O/RTnP7Nd+h1YdpmlHBGfuwIRIJz5x
++Nw6KIcCy3Q0NNoKnh00WVwLmR+x11BGSkMjiZZkwJ5D0RObSg13QD/itrGoV2gtP
++zjQgNPfTrjsMvyOWAAFrWVR3QLTxnnmXsqnXvwIDAQABAoIBAQCqvhpeMDXhGgoo
++Q03wmfrGwPsrMv91aIK1hYrhMPdVs1JAbRYiKh8+pcq07FYa8udRaB4UwkVh/+oM
++/nEs6niRsl/jjQ2l68TFrnNByroynvr6l9Q/EeGecF6Ygo7lY1OsFhcLQM5vjarS
++XhxvdU/6hcRmfS8tGRpUaMWqfmpiN3YgJcgt8SoYhiwAYDTMJjNyWC61lO7IqNVR
++4kntiM24sfAu1sdZynX8Gp2GrpNChapEuhilQ8RayjuStEYr2abcSIjfZFHQXN7o
++TnjL+AQUzc/ZTXDGnIe9ZzZeFz8UCueeoN6KPxfrq9UUWRL6qt7gOIMdhYR6lFxt
++6pj6kLhxAoGBAO5DTnTKDfCMY2/AsTzCJvMGSY0bT1rsdDxrpqjrbUSeMHV3s5Lm
++vEPnnm+05FD/vi99+HZjHXAZFkhA3ubij2qWFPBnQ5YUoh17IW/Ae4bzY2uXikgL
++tLZ+R+OrcGYQQlvPn//PLsxbfdk5vraqzm08kIX0T4o4Iz8ST5NFJ8hVAoGBAOdB
++ahXr14563Cjeu0pSQ1nXoz3IXdnDwePXasYhxQHl8Ayk8qZS5pt7r07H3dqq6pvn
++e09gZINJe47B9UhkR3H5bPyz/kujKS4zqo3Zlbryzm3V0BWqjNj+j8E2YuQKNQr+
++c480jn2FzwW66w0i3n4U4KUn1w2/iq5AnVzyNkPDAoGAWLYEsyU79XE/4K79DqM3
++P0r6/afKbw8U5B4syj4FzAOeBU6RNMPmGt5VNkBCtgnSdPpRFTsoDcG5cyN8GrkG
++Lug8WZoJJwr9pT5gH6yqEX/zZ27f1J1PJpd0CsedLNMm8eonJ2arhPkXrVZ7tKV6
++AGAJa2agatUmAmi96hZYjpUCgYEA32abJEgsedEIhFb/GYI03ELryRCaUXfCA+gj
++lvoihn3qE1z5qGGns4adyX5dPRQmBqxtvDXDg+zl9vg6i0+MkXdCqTD8tXcOnjp9
++RgFvmyVa9FI8beHPpQTuPNncWK3fpho/6pT8Hhi48LEsxwjrZWOnzQSaxQZH46Q6
++IQNAFt8CgYEAkflxXvA2/2naix+riaBzv5EVJB7ilbfWiWtq2LEAtwrQ5XNFjrtK
++g45jKrZ/ezAzTfPa5Dwn4xcImd0MIavnJhDu2ATxMGB0GATLlDH2HZvU7UwKLpTW
++6Hlol4yRcX4GSEOxJ2ZpWYNIOYH0yDf1qLJXs1j8Fi3zWRe+V1kff4w=
++-----END RSA PRIVATE KEY-----
++-----BEGIN CERTIFICATE-----
++MIIEgjCCA2qgAwIBAgIBAzANBgkqhkiG9w0BAQUFADCBmTELMAkGA1UEBhMCVVMx
++FjAUBgNVBAgTDU1hc3NhY2h1c2V0dHMxEjAQBgNVBAcTCUNhbWJyaWRnZTEMMAoG
++A1UEChMDTUlUMSIwIAYDVQQLExlJbnNlY3VyZSBLZXJiZXJvcyB0ZXN0IENBMSww
++KgYDVQQDFCN0ZXN0IHN1aXRlIENBOyBkbyBub3QgdXNlIG90aGVyd2lzZTAeFw0x
++NDA1MDIxOTA2MDhaFw0yNTA0MTQxOTA2MDhaME8xCzAJBgNVBAYTAlVTMRYwFAYD
++VQQIEw1NYXNzYWNodXNldHRzMRQwEgYDVQQKEwtLUkJURVNULkNPTTESMBAGA1UE
++AxMJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1zud
++npN8FP7iLn1vgkyTSn/RQxXx1yt6zikHaMrVPjkjXPPUoCFpWS3eeI4aQFoj93L5
++MwZDmSxOflBAqLwV2AMAacrYnNPJIkHtbYKdVsvw9b4INTWqV9/DOODO7UowyMpp
++mO35/pUXaLL+AjHjLw1/EhQ3ZYtqfpAMOkf5TnS5GtqZFlrYgZKE8vTC8BxDKM7F
++YhWYz7kp/tG3S8O/RTnP7Nd+h1YdpmlHBGfuwIRIJz5xNw6KIcCy3Q0NNoKnh00W
++VwLmR+x11BGSkMjiZZkwJ5D0RObSg13QD/itrGoV2gtPzjQgNPfTrjsMvyOWAAFr
++WVR3QLTxnnmXsqnXvwIDAQABo4IBHDCCARgwHQYDVR0OBBYEFHO5+DSYzq8rvQhU
++ldyvn0y4AqlHMIHGBgNVHSMEgb4wgbuAFHO5+DSYzq8rvQhUldyvn0y4AqlHoYGf
++pIGcMIGZMQswCQYDVQQGEwJVUzEWMBQGA1UECBMNTWFzc2FjaHVzZXR0czESMBAG
++A1UEBxMJQ2FtYnJpZGdlMQwwCgYDVQQKEwNNSVQxIjAgBgNVBAsTGUluc2VjdXJl
++IEtlcmJlcm9zIHRlc3QgQ0ExLDAqBgNVBAMUI3Rlc3Qgc3VpdGUgQ0E7IGRvIG5v
++dCB1c2Ugb3RoZXJ3aXNlggEBMAsGA1UdDwQEAwID6DAMBgNVHRMBAf8EAjAAMBMG
++A1UdJQQMMAoGCCsGAQUFBwMBMA0GCSqGSIb3DQEBBQUAA4IBAQCzGPT+QOrl9mbJ
++nsGlPlLUOF+PYz0a/9V/iznlofxwCXiRi2ryMpLFbjLeOvjLJ3UzyNKtmEeudTBM
++yfR4i8tb9WA7Oh0BjK1+kD4688bAUXiIDhueKBjonmPvMd9kq3MDd4vDLkcZk6R4
++4IcbdwhzSBmnJH8ha2J82XShPpRq5CZNR9+vTyFwGdGWdPDjTMiXoXAmpRemcEgO
++iO4Gxvcrg/Z06Ys3eLze7QHNMAEwXhC4rUR34j5I2zgU7CEhff3AktLmnKVa8go8
++4BJT/n3XGB+3gdAEihQmgCEZetHH+YxAR0Ppn3ty7fpAlOnbRJqpeu6TMN8x/lL8
++c6JtDWRG
++-----END CERTIFICATE-----
+diff --git a/src/util/paste-kdcproxy.py b/src/util/paste-kdcproxy.py
+new file mode 100755
+index 0000000..1e56b89
+--- /dev/null
++++ b/src/util/paste-kdcproxy.py
+@@ -0,0 +1,18 @@
++#!/usr/bin/python
++import kdcproxy
++from paste import httpserver
++import os
++import sys
++
++if len(sys.argv) > 1:
++ port = sys.argv[1]
++else:
++ port = 8443
++if len(sys.argv) > 2:
++ pem = sys.argv[2]
++else:
++ pem = '*'
++server = httpserver.serve(kdcproxy.Application(), port=port, ssl_pem=pem,
++ start_loop=False)
++os.write(sys.stdout.fileno(), 'proxy server ready\n')
++server.serve_forever()
+--
+2.1.0
+
diff --git a/0013-Add-tests-for-MS-KKDCP-client-support.patch b/0013-Add-tests-for-MS-KKDCP-client-support.patch
new file mode 100644
index 0000000..fdb6eca
--- /dev/null
+++ b/0013-Add-tests-for-MS-KKDCP-client-support.patch
@@ -0,0 +1,259 @@
+Tweaked context for src/tests/Makefile.in because t_salt.py hadn't yet been
+added as a test in 1.12, and the rdreq and s2p helpers weren't there yet.
+
+From 3e2c7cc557048faac3400ae41b0228bd37a82a4c Mon Sep 17 00:00:00 2001
+From: Nalin Dahyabhai <nalin@dahyabhai.net>
+Date: Fri, 7 Feb 2014 18:56:10 -0500
+Subject: [PATCH 13/13] Add tests for MS-KKDCP client support
+
+Exercise the MS-KKDCP client support using the test proxy server, for
+AS, TGS, and kpasswd requests while also checking the certificate
+verification and name checks.
+
+ticket: 7929
+---
+ src/tests/Makefile.in | 1 +
+ src/tests/t_proxy.py | 219 ++++++++++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 220 insertions(+)
+ create mode 100644 src/tests/t_proxy.py
+
+diff --git a/src/tests/Makefile.in b/src/tests/Makefile.in
+index 7347ed6..536f5cb 100644
+--- a/src/tests/Makefile.in
++++ b/src/tests/Makefile.in
+@@ -134,6 +134,7 @@ check-pytests:: t_init_creds t_localauth
+ $(RUNPYTEST) $(srcdir)/jsonwalker.py -d $(srcdir)/au_dict.json \
+ -i au.log
+ $(RUNPYTEST) $(srcdir)/t_bogus_kdc_req.py $(PYTESTFLAGS)
++ $(RUNPYTEST) $(srcdir)/t_proxy.py $(PYTESTFLAGS)
+
+ clean::
+ $(RM) gcred hist hrealm kdbtest plugorder responder
+diff --git a/src/tests/t_proxy.py b/src/tests/t_proxy.py
+new file mode 100644
+index 0000000..e4e3d48
+--- /dev/null
++++ b/src/tests/t_proxy.py
+@@ -0,0 +1,219 @@
++#!/usr/bin/python
++from k5test import *
++
++# Skip this test if we're missing proxy functionality or parts of the proxy.
++if runenv.proxy_tls_impl == 'no':
++ success('Warning: not testing proxy support because feature ' +
++ 'was not enabled')
++ exit(0)
++try:
++ from paste import httpserver
++except:
++ success('Warning: not testing proxy support because python ' +
++ 'paste.httpserver module not found')
++ exit(0)
++try:
++ import kdcproxy
++except:
++ success('Warning: not testing proxy support because python ' +
++ 'kdcproxy module not found')
++ exit(0)
++
++# Construct a krb5.conf fragment configuring the client to use a local proxy
++# server.
++proxysubjectpem = os.path.join(srctop, 'tests', 'dejagnu', 'proxy-certs',
++ 'proxy-subject.pem')
++proxysanpem = os.path.join(srctop, 'tests', 'dejagnu', 'proxy-certs',
++ 'proxy-san.pem')
++proxyidealpem = os.path.join(srctop, 'tests', 'dejagnu', 'proxy-certs',
++ 'proxy-ideal.pem')
++proxywrongpem = os.path.join(srctop, 'tests', 'dejagnu', 'proxy-certs',
++ 'proxy-no-match.pem')
++proxybadpem = os.path.join(srctop, 'tests', 'dejagnu', 'proxy-certs',
++ 'proxy-badsig.pem')
++proxyca = os.path.join(srctop, 'tests', 'dejagnu', 'proxy-certs', 'ca.pem')
++proxyurl = 'https://localhost:$port5/KdcProxy'
++proxyurlupcase = 'https://LocalHost:$port5/KdcProxy'
++proxyurl4 = 'https://127.0.0.1:$port5/KdcProxy'
++proxyurl6 = 'https://[::1]:$port5/KdcProxy'
++
++unanchored_krb5_conf = {'realms': {'$realm': {
++ 'kdc': proxyurl,
++ 'kpasswd_server': proxyurl}}}
++anchored_name_krb5_conf = {'realms': {'$realm': {
++ 'kdc': proxyurl,
++ 'kpasswd_server': proxyurl,
++ 'http_anchors': 'FILE:%s' % proxyca}}}
++anchored_upcasename_krb5_conf = {'realms': {'$realm': {
++ 'kdc': proxyurlupcase,
++ 'kpasswd_server': proxyurlupcase,
++ 'http_anchors': 'FILE:%s' % proxyca}}}
++anchored_kadmin_krb5_conf = {'realms': {'$realm': {
++ 'kdc': proxyurl,
++ 'admin_server': proxyurl,
++ 'http_anchors': 'FILE:%s' % proxyca}}}
++anchored_ipv4_krb5_conf = {'realms': {'$realm': {
++ 'kdc': proxyurl4,
++ 'kpasswd_server': proxyurl4,
++ 'http_anchors': 'FILE:%s' % proxyca}}}
++kpasswd_input = (password('user') + '\n' + password('user') + '\n' +
++ password('user') + '\n')
++
++def start_proxy(realm, keycertpem):
++ proxy_conf_path = os.path.join(realm.testdir, 'kdcproxy.conf')
++ proxy_exec_path = os.path.join(srctop, 'util', 'paste-kdcproxy.py')
++ conf = open(proxy_conf_path, 'w')
++ conf.write('[%s]\n' % realm.realm)
++ conf.write('kerberos = kerberos://localhost:%d\n' % realm.portbase)
++ conf.write('kpasswd = kpasswd://localhost:%d\n' % (realm.portbase + 2))
++ conf.close()
++ realm.env['KDCPROXY_CONFIG'] = proxy_conf_path
++ cmd = [proxy_exec_path, str(realm.server_port()), keycertpem]
++ return realm.start_server(cmd, sentinel='proxy server ready')
++
++# Fail: untrusted issuer and hostname doesn't match.
++output("running pass 1: issuer not trusted and hostname doesn't match\n")
++realm = K5Realm(krb5_conf=unanchored_krb5_conf, get_creds=False,
++ create_host=False)
++proxy = start_proxy(realm, proxywrongpem)
++realm.kinit(realm.user_princ, password=password('user'), expected_code=1)
++stop_daemon(proxy)
++realm.stop()
++
++# Fail: untrusted issuer, host name matches subject.
++output("running pass 2: subject matches, issuer not trusted\n")
++realm = K5Realm(krb5_conf=unanchored_krb5_conf, get_creds=False,
++ create_host=False)
++proxy = start_proxy(realm, proxysubjectpem)
++realm.kinit(realm.user_princ, password=password('user'), expected_code=1)
++stop_daemon(proxy)
++realm.stop()
++
++# Fail: untrusted issuer, host name matches subjectAltName.
++output("running pass 3: subjectAltName matches, issuer not trusted\n")
++realm = K5Realm(krb5_conf=unanchored_krb5_conf, get_creds=False,
++ create_host=False)
++proxy = start_proxy(realm, proxysanpem)
++realm.kinit(realm.user_princ, password=password('user'), expected_code=1)
++stop_daemon(proxy)
++realm.stop()
++
++# Fail: untrusted issuer, certificate signature is bad.
++output("running pass 4: subject matches, issuer not trusted\n")
++realm = K5Realm(krb5_conf=unanchored_krb5_conf, get_creds=False,
++ create_host=False)
++proxy = start_proxy(realm, proxybadpem)
++realm.kinit(realm.user_princ, password=password('user'), expected_code=1)
++stop_daemon(proxy)
++realm.stop()
++
++# Fail: trusted issuer but hostname doesn't match.
++output("running pass 5: issuer trusted but hostname doesn't match\n")
++realm = K5Realm(krb5_conf=anchored_name_krb5_conf, get_creds=False,
++ create_host=False)
++proxy = start_proxy(realm, proxywrongpem)
++realm.kinit(realm.user_princ, password=password('user'), expected_code=1)
++stop_daemon(proxy)
++realm.stop()
++
++# Succeed: trusted issuer and host name matches subject.
++output("running pass 6: issuer trusted, subject matches\n")
++realm = K5Realm(krb5_conf=anchored_name_krb5_conf, start_kadmind=True,
++ get_creds=False)
++proxy = start_proxy(realm, proxysubjectpem)
++realm.kinit(realm.user_princ, password=password('user'))
++realm.run([kvno, realm.host_princ])
++realm.run([kpasswd, realm.user_princ], input=kpasswd_input)
++stop_daemon(proxy)
++realm.stop()
++
++# Succeed: trusted issuer and host name matches subjectAltName.
++output("running pass 7: issuer trusted, subjectAltName matches\n")
++realm = K5Realm(krb5_conf=anchored_name_krb5_conf, start_kadmind=True,
++ get_creds=False)
++proxy = start_proxy(realm, proxysanpem)
++realm.kinit(realm.user_princ, password=password('user'))
++realm.run([kvno, realm.host_princ])
++realm.run([kpasswd, realm.user_princ], input=kpasswd_input)
++stop_daemon(proxy)
++realm.stop()
++
++# Fail: certificate signature is bad.
++output("running pass 8: issuer trusted and subjectAltName matches, sig bad\n")
++realm = K5Realm(krb5_conf=anchored_name_krb5_conf,
++ get_creds=False,
++ create_host=False)
++proxy = start_proxy(realm, proxybadpem)
++realm.kinit(realm.user_princ, password=password('user'), expected_code=1)
++stop_daemon(proxy)
++realm.stop()
++
++# Fail: trusted issuer but IP doesn't match.
++output("running pass 9: issuer trusted but no name matches IP\n")
++realm = K5Realm(krb5_conf=anchored_ipv4_krb5_conf, get_creds=False,
++ create_host=False)
++proxy = start_proxy(realm, proxywrongpem)
++realm.kinit(realm.user_princ, password=password('user'), expected_code=1)
++stop_daemon(proxy)
++realm.stop()
++
++# Fail: trusted issuer, but subject does not match.
++output("running pass 10: issuer trusted, but subject does not match IP\n")
++realm = K5Realm(krb5_conf=anchored_ipv4_krb5_conf, get_creds=False,
++ create_host=False)
++proxy = start_proxy(realm, proxysubjectpem)
++realm.kinit(realm.user_princ, password=password('user'), expected_code=1)
++stop_daemon(proxy)
++realm.stop()
++
++# Succeed: trusted issuer and host name matches subjectAltName.
++output("running pass 11: issuer trusted, subjectAltName matches IP\n")
++realm = K5Realm(krb5_conf=anchored_ipv4_krb5_conf, start_kadmind=True,
++ get_creds=False)
++proxy = start_proxy(realm, proxysanpem)
++realm.kinit(realm.user_princ, password=password('user'))
++realm.run([kvno, realm.host_princ])
++realm.run([kpasswd, realm.user_princ], input=kpasswd_input)
++stop_daemon(proxy)
++realm.stop()
++
++# Fail: certificate signature is bad.
++output("running pass 12: issuer trusted, names don't match, signature bad\n")
++realm = K5Realm(krb5_conf=anchored_ipv4_krb5_conf, get_creds=False,
++ create_host=False)
++proxy = start_proxy(realm, proxybadpem)
++realm.kinit(realm.user_princ, password=password('user'), expected_code=1)
++stop_daemon(proxy)
++realm.stop()
++
++# Succeed: trusted issuer and host name matches subject, using kadmin
++# configuration to find kpasswdd.
++output("running pass 13: issuer trusted, subject matches\n")
++realm = K5Realm(krb5_conf=anchored_kadmin_krb5_conf, start_kadmind=True,
++ get_creds=False, create_host=False)
++proxy = start_proxy(realm, proxysubjectpem)
++realm.run([kpasswd, realm.user_princ], input=kpasswd_input)
++stop_daemon(proxy)
++realm.stop()
++
++# Succeed: trusted issuer and host name matches subjectAltName, using
++# kadmin configuration to find kpasswdd.
++output("running pass 14: issuer trusted, subjectAltName matches\n")
++realm = K5Realm(krb5_conf=anchored_kadmin_krb5_conf, start_kadmind=True,
++ get_creds=False, create_host=False)
++proxy = start_proxy(realm, proxysanpem)
++realm.run([kpasswd, realm.user_princ], input=kpasswd_input)
++stop_daemon(proxy)
++realm.stop()
++
++# Succeed: trusted issuer and host name matches subjectAltName (give or take
++# case).
++output("running pass 15: issuer trusted, subjectAltName case-insensitive\n")
++realm = K5Realm(krb5_conf=anchored_upcasename_krb5_conf, start_kadmind=True,
++ get_creds=False, create_host=False)
++proxy = start_proxy(realm, proxysanpem)
++realm.run([kpasswd, realm.user_princ], input=kpasswd_input)
++stop_daemon(proxy)
++realm.stop()
++
++success('MS-KKDCP proxy')
+--
+2.1.0
+
diff --git a/krb5-1.12-system-exts.patch b/krb5-1.12-system-exts.patch
index fc518c4..b90fb1b 100644
--- a/krb5-1.12-system-exts.patch
+++ b/krb5-1.12-system-exts.patch
@@ -25,8 +25,8 @@ smaller change here.
+#define _GNU_SOURCE
+
#include "fake-addrinfo.h"
+ #include "k5-tls.h"
#include "k5-int.h"
-
--- krb5-1.11/src/util/support/fake-addrinfo.c
+++ krb5-1.11/src/util/support/fake-addrinfo.c
@@ -101,6 +101,9 @@
diff --git a/krb5-1.10-kpasswd_tcp.patch b/krb5-1.12ish-kpasswd_tcp.patch
index fd8da8e..4fdfca4 100644
--- a/krb5-1.10-kpasswd_tcp.patch
+++ b/krb5-1.12ish-kpasswd_tcp.patch
@@ -4,8 +4,8 @@ to wait for UDP to fail, so this might not be ideal. RT #5868.
--- krb5/src/lib/krb5/os/changepw.c
+++ krb5/src/lib/krb5/os/changepw.c
@@ -270,10 +270,22 @@ change_set_password(krb5_context context
- &callback_info, &chpw_rep, ss2sa(&remote_addr),
- &addrlen, NULL, NULL, NULL);
+ &sl, strategy, &callback_info, &chpw_rep,
+ ss2sa(&remote_addr), &addrlen, NULL, NULL, NULL);
if (code) {
- /*
- * Here we may want to switch to TCP on some errors.
@@ -14,14 +14,14 @@ to wait for UDP to fail, so this might not be ideal. RT #5868.
+ /* if we're not using a stream socket, and it's an error which
+ * might reasonably be specific to a datagram "connection", try
+ * again with a stream socket */
-+ if (!use_tcp) {
++ if (!no_udp) {
+ switch (code) {
+ case KRB5_KDC_UNREACH:
+ case KRB5_REALM_CANT_RESOLVE:
+ case KRB5KRB_ERR_RESPONSE_TOO_BIG:
-+ /* should we do this for more result codes than these? */
++ /* should we do this for more result codes than these? */
+ k5_free_serverlist (&sl);
-+ use_tcp = 1;
++ no_udp = 1;
+ continue;
+ default:
+ break;
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 */
diff --git a/krb5-master-move-otp-sockets.patch b/krb5-master-move-otp-sockets.patch
index 350a818..8d86930 100644
--- a/krb5-master-move-otp-sockets.patch
+++ b/krb5-master-move-otp-sockets.patch
@@ -93,7 +93,7 @@ index a8bc990..1725093 100644
+++ b/src/Makefile.in
@@ -64,6 +64,7 @@ INSTALLMKDIRS = $(KRB5ROOT) $(KRB5MANROOT) $(KRB5OTHERMKDIRS) \
$(KRB5_AD_MODULE_DIR) \
- $(KRB5_LIBKRB5_MODULE_DIR) \
+ $(KRB5_LIBKRB5_MODULE_DIR) $(KRB5_TLS_MODULE_DIR) \
@localstatedir@ @localstatedir@/krb5kdc \
+ @runstatedir@ @runstatedir@/krb5kdc \
$(KRB5_INCSUBDIRS) $(datadir) $(EXAMPLEDIR) \
diff --git a/krb5.spec b/krb5.spec
index 393c0c2..17cff94 100644
--- a/krb5.spec
+++ b/krb5.spec
@@ -84,7 +84,7 @@ Patch23: krb5-1.3.1-dns.patch
Patch29: krb5-1.10-kprop-mktemp.patch
Patch30: krb5-1.3.4-send-pr-tempfile.patch
Patch39: krb5-1.12-api.patch
-Patch59: krb5-1.10-kpasswd_tcp.patch
+Patch59: krb5-1.12ish-kpasswd_tcp.patch
Patch60: krb5-1.12.1-pam.patch
Patch63: krb5-1.12-selinux-label.patch
Patch71: krb5-1.11-dirsrv-accountlock.patch
@@ -109,6 +109,21 @@ Patch205: 0005-Copy-config-entries-to-the-ksu-target-ccache.patch
Patch206: 0006-Use-more-randomness-for-ksu-secondary-cache-names.patch
Patch207: 0007-Make-krb5_cc_new_unique-create-DIR-directories.patch
Patch300: krb5-1.12-kpasswd-skip-address-check.patch
+Patch301: 0000-Refactor-cm-functions-in-sendto_kdc.c.patch
+Patch302: 0001-Simplify-sendto_kdc.c.patch
+Patch303: 0002-Add-helper-to-determine-if-a-KDC-is-the-master.patch
+Patch304: 0003-Use-k5_transport-_strategy-enums-for-k5_sendto.patch
+Patch305: 0004-Build-support-for-TLS-used-by-HTTPS-proxy-support.patch
+Patch306: 0005-Add-ASN.1-codec-for-KKDCP-s-KDC-PROXY-MESSAGE.patch
+Patch307: 0006-Dispatch-style-protocol-switching-for-transport.patch
+Patch308: 0007-HTTPS-transport-Microsoft-KKDCPP-implementation.patch
+Patch309: 0008-Load-custom-anchors-when-using-KKDCP.patch
+Patch310: 0009-Check-names-in-the-server-s-cert-when-using-KKDCP.patch
+Patch311: 0010-Add-some-longer-form-docs-for-HTTPS.patch
+Patch312: 0011-Have-k5test.py-provide-runenv-to-python-tests.patch
+Patch313: 0012-Add-a-simple-KDC-proxy-test-server.patch
+Patch314: 0013-Add-tests-for-MS-KKDCP-client-support.patch
+Patch315: krb5-1.12ish-tls-plugins.patch
License: MIT
URL: http://web.mit.edu/kerberos/www/
@@ -320,6 +335,22 @@ ln -s NOTICE LICENSE
%patch207 -p1 -b .Make-krb5_cc_new_unique-create-DIR-directories
%patch300 -p1 -b .kpasswd-skip-address-check
+%patch301 -p1 -b .Refactor-cm-functions-in-sendto_kdc.c
+%patch302 -p1 -b .Simplify-sendto_kdc.c
+%patch303 -p1 -b .Add-helper-to-determine-if-a-KDC-is-the-master
+%patch304 -p1 -b .Use-k5_transport-_strategy-enums-for-k5_sendto
+%patch305 -p1 -b .Build-support-for-TLS-used-by-HTTPS-proxy-support
+%patch306 -p1 -b .Add-ASN.1-codec-for-KKDCP-s-KDC-PROXY-MESSAGE
+%patch307 -p1 -b .Dispatch-style-protocol-switching-for-transport
+%patch308 -p1 -b .HTTPS-transport-Microsoft-KKDCPP-implementation
+%patch309 -p1 -b .Load-custom-anchors-when-using-KKDCP
+%patch310 -p1 -b .Check-names-in-the-server-s-cert-when-using-KKDCP
+%patch311 -p1 -b .Add-some-longer-form-docs-for-HTTPS
+%patch312 -p1 -b .Have-k5test.py-provide-runenv-to-python-tests
+%patch313 -p1 -b .Add-a-simple-KDC-proxy-test-server
+%patch314 -p1 -b .Add-tests-for-MS-KKDCP-client-support
+%patch315 -p1 -b .tls-plugins
+chmod u+x src/util/paste-kdcproxy.py
%patch1 -p1 -b .pwdch-fast
@@ -630,6 +661,7 @@ done
# Plug-in directories.
install -pdm 755 $RPM_BUILD_ROOT/%{_libdir}/krb5/plugins/preauth
install -pdm 755 $RPM_BUILD_ROOT/%{_libdir}/krb5/plugins/kdb
+install -pdm 755 $RPM_BUILD_ROOT/%{_libdir}/krb5/plugins/tls
install -pdm 755 $RPM_BUILD_ROOT/%{_libdir}/krb5/plugins/authdata
# The rest of the binaries, headers, libraries, and docs.
@@ -967,6 +999,7 @@ exit 0
%dir %{_libdir}/krb5
%dir %{_libdir}/krb5/plugins
%dir %{_libdir}/krb5/plugins/*
+%{_libdir}/krb5/plugins/tls/k5tls.so
%{_libdir}/krb5/plugins/kdb/db2.so
%dir %{_var}/kerberos
%dir %{_var}/kerberos/krb5
@@ -1036,6 +1069,11 @@ exit 0
between NAT and upcoming HTTPS support, can cause us to erroneously
report an error to the user when the server actually reported success
(RT #7886)
+- backport support for accessing KDCs and kpasswd services via HTTPS
+ proxies (marked by being specified as https URIs instead as hostnames
+ or hostname-and-port), such as the one implemented in python-kdcproxy
+ (RT #7929, #109919), and pick up a subsequent patch to build HTTPS
+ as a plugin
* Thu Aug 28 2014 Nalin Dahyabhai <nalin@redhat.com> - 1.12.2-5
- backport fix for trying all compatible keys when not being strict about