diff options
Diffstat (limited to '0008-Load-custom-anchors-when-using-KKDCP.patch')
-rw-r--r-- | 0008-Load-custom-anchors-when-using-KKDCP.patch | 312 |
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 + |