summaryrefslogtreecommitdiffstats
path: root/0008-Load-custom-anchors-when-using-KKDCP.patch
diff options
context:
space:
mode:
Diffstat (limited to '0008-Load-custom-anchors-when-using-KKDCP.patch')
-rw-r--r--0008-Load-custom-anchors-when-using-KKDCP.patch312
1 files changed, 312 insertions, 0 deletions
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
+