diff options
author | Nalin Dahyabhai <nalin@dahyabhai.net> | 2014-04-17 17:17:13 -0400 |
---|---|---|
committer | Greg Hudson <ghudson@mit.edu> | 2014-06-02 18:09:47 -0400 |
commit | f220067c2969aab107bd1300ad1cb8d4855389a7 (patch) | |
tree | 169dadfa34b5fdd4e0635cef321150b0e1240973 /src/lib | |
parent | d950809ff49e3e7603594186d77135a09ab6b1b2 (diff) | |
download | krb5-f220067c2969aab107bd1300ad1cb8d4855389a7.tar.gz krb5-f220067c2969aab107bd1300ad1cb8d4855389a7.tar.xz krb5-f220067c2969aab107bd1300ad1cb8d4855389a7.zip |
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
Diffstat (limited to 'src/lib')
-rw-r--r-- | src/lib/krb5/os/sendto_kdc.c | 169 |
1 files changed, 167 insertions, 2 deletions
diff --git a/src/lib/krb5/os/sendto_kdc.c b/src/lib/krb5/os/sendto_kdc.c index a4727c42f..4bd869833 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; |