From fd6597614e5a85a82b349b617a76c745b39af7b0 Mon Sep 17 00:00:00 2001 From: Nicolas Williams Date: Tue, 29 Nov 2022 16:17:45 -0600 Subject: [PATCH] bx509d: Add test of IPC CSR authorizer We have a CSR authorizer plugin for calling to an IPC service. In this commit we add test implementation of such a service. We also remove the simple_csr_authorizer plugin and fold its functionality into the new test_csr_authorizer functionality. --- kdc/Makefile.am | 15 +- kdc/ipc_csr_authorizer.c | 17 +- kdc/simple_csr_authorizer.c | 343 ------------------- kdc/test_csr_authorizer.c | 513 ++++++++++++++++++++++++++++- tests/kdc/Makefile.am | 2 +- tests/kdc/check-bx509.in | 263 ++++++++++++--- tests/kdc/check-httpkadmind.in | 103 +++--- tests/kdc/check-pkinit.in | 5 + tests/kdc/krb5-bx509.conf.in | 5 - tests/kdc/krb5-httpkadmind.conf.in | 4 - tests/kdc/krb5-pkinit.conf.in | 4 +- 11 files changed, 801 insertions(+), 473 deletions(-) delete mode 100644 kdc/simple_csr_authorizer.c diff --git a/kdc/Makefile.am b/kdc/Makefile.am index 48248d824..ca5835930 100644 --- a/kdc/Makefile.am +++ b/kdc/Makefile.am @@ -6,8 +6,9 @@ WFLAGS += $(WFLAGS_ENUM_CONV) AM_CPPFLAGS += $(INCLUDE_libintl) $(INCLUDE_openssl_crypto) -I$(srcdir)/../lib/krb5 -lib_LTLIBRARIES = simple_csr_authorizer.la ipc_csr_authorizer.la \ - libkdc.la negotiate_token_validator.la +lib_LTLIBRARIES = ipc_csr_authorizer.la \ + negotiate_token_validator.la \ + libkdc.la if HAVE_CJWT lib_LTLIBRARIES += cjwt_token_validator.la @@ -97,8 +98,6 @@ endif negotiate_token_validator_la_SOURCES = negotiate_token_validator.c negotiate_token_validator_la_LDFLAGS = -module $(LIB_gssapi) # CSR Authorizer plugins (for kdc/kx509 and bx509d) -simple_csr_authorizer_la_SOURCES = simple_csr_authorizer.c -simple_csr_authorizer_la_LDFLAGS = -module ipc_csr_authorizer_la_SOURCES = ipc_csr_authorizer.c ipc_csr_authorizer_la_LDFLAGS = -module \ $(top_builddir)/lib/krb5/libkrb5.la \ @@ -155,7 +154,6 @@ ALL_OBJECTS += $(digest_service_OBJECTS) ALL_OBJECTS += $(bx509d_OBJECTS) ALL_OBJECTS += $(httpkadmind_OBJECTS) ALL_OBJECTS += $(cjwt_token_validator_la_OBJECTS) -ALL_OBJECTS += $(simple_csr_authorizer_la_OBJECTS) ALL_OBJECTS += $(test_token_validator_OBJECTS) ALL_OBJECTS += $(test_csr_authorizer_OBJECTS) ALL_OBJECTS += $(test_kdc_ca_OBJECTS) @@ -237,7 +235,12 @@ digest_service_LDADD = \ kdc_replay_LDADD = libkdc.la $(LDADD) $(LIB_pidfile) kdc_tester_LDADD = libkdc.la $(LDADD) $(LIB_pidfile) $(LIB_heimbase) test_token_validator_LDADD = libkdc.la $(LDADD) $(LIB_pidfile) $(LIB_heimbase) $(LIB_gssapi) -test_csr_authorizer_LDADD = libkdc.la $(top_builddir)/lib/hx509/libhx509.la $(LDADD) $(LIB_pidfile) $(LIB_heimbase) +test_csr_authorizer_LDADD = libkdc.la \ + $(top_builddir)/lib/hx509/libhx509.la \ + $(LDADD) \ + $(LIB_pidfile) \ + $(LIB_heimbase) \ + $(top_builddir)/lib/ipc/libheim-ipcs.la test_kdc_ca_LDADD = libkdc.la $(top_builddir)/lib/hx509/libhx509.la $(LDADD) $(LIB_pidfile) $(LIB_heimbase) include_HEADERS = kdc.h $(srcdir)/kdc-protos.h diff --git a/kdc/ipc_csr_authorizer.c b/kdc/ipc_csr_authorizer.c index d90f056c5..87d6deb26 100644 --- a/kdc/ipc_csr_authorizer.c +++ b/kdc/ipc_csr_authorizer.c @@ -503,12 +503,23 @@ authorize(void *ctx, int do_check = 0; int piecemeal_check_ok = 1; - if ((svc = krb5_config_get_string(context, NULL, app ? app : "kdc", - "ipc_csr_authorizer", "service", NULL)) - == NULL) + if ((svc = krb5_config_get_string_default(context, NULL, + "ANY:org.h5l.csr_authorizer", + app ? app : "kdc", + "ipc_csr_authorizer", "service", + NULL)) == NULL) return KRB5_PLUGIN_NO_HANDLE; if ((ret = heim_ipc_init_context(svc, &ipc))) { + /* + * If the IPC authorizer is optional, then fallback on whatever is + * next. + */ + if (krb5_config_get_bool_default(context, NULL, FALSE, + app ? app : "kdc", + "ipc_csr_authorizer", "optional", + NULL)) + return KRB5_PLUGIN_NO_HANDLE; krb5_set_error_message(context, ret, "Could not set up IPC client " "end-point for service %s", svc); return ret; diff --git a/kdc/simple_csr_authorizer.c b/kdc/simple_csr_authorizer.c deleted file mode 100644 index b46a8931a..000000000 --- a/kdc/simple_csr_authorizer.c +++ /dev/null @@ -1,343 +0,0 @@ -/* - * Copyright (c) 2019 Kungliga Tekniska Högskolan - * (Royal Institute of Technology, Stockholm, Sweden). - * 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. - * - * 3. Neither the name of the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE 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 plugin authorizes requested certificate SANs and EKUs by checking for - * existence of files of the form: - * - * - * ///- - * - * where is the value of: - * - * [kdc] simple_csr_authorizer_directory = PATH - * - * is a requesting client principal name with all characters other than - * alphanumeric, '-', '_', and non-leading '.' URL-encoded. - * - * is one of: - * - * - pkinit (SAN) - * - xmpp (SAN) - * - email (SAN) - * - ms-upn (SAN) - * - dnsname (SAN) - * - eku (EKU OID) - * - * and is a display form of the SAN or EKU OID, with SANs URL-encoded - * just like principal names (see above). - * - * OIDs are of the form "1.2.3.4.5". - * - * Only digitalSignature and nonRepudiation key usage values are permitted. - */ -#define _GNU_SOURCE 1 - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -/* - * string_encode_sz() and string_encode() encode a string to be safe for use as - * a file name. They function very much like URL encoders, but '~' also gets - * encoded, and '@', '-', '_', and non-leading '.' do not. - * - * A corresponding decoder is not needed. - */ -static size_t -string_encode_sz(const char *in) -{ - size_t sz = strlen(in); - int first = 1; - - while (*in) { - char c = *(in++); - - switch (c) { - case '@': - case '-': - case '_': - break; - case '.': - if (first) - sz += 2; - break; - default: - if (!isalnum(c)) - sz += 2; - } - first = 0; - } - return sz; -} - -static char * -string_encode(const char *in) -{ - size_t len = strlen(in); - size_t sz = string_encode_sz(in); - size_t i, k; - char *s; - int first = 1; - - if ((s = malloc(sz + 1)) == NULL) - return NULL; - s[sz] = '\0'; - - for (i = k = 0; i < len; i++, first = 0) { - unsigned char c = ((const unsigned char *)in)[i]; - - switch (c) { - case '@': - case '-': - case '_': - s[k++] = c; - break; - case '.': - if (first) { - s[k++] = '%'; - s[k++] = "0123456789abcdef"[(c&0xff)>>4]; - s[k++] = "0123456789abcdef"[(c&0x0f)]; - } else { - s[k++] = c; - } - break; - default: - if (isalnum(c)) { - s[k++] = c; - } else { - s[k++] = '%'; - s[k++] = "0123456789abcdef"[(c&0xff)>>4]; - s[k++] = "0123456789abcdef"[(c&0x0f)]; - } - } - } - return s; -} - -static void -frees(char **s) -{ - free(*s); - *s = NULL; -} - -static KRB5_LIB_CALL krb5_error_code -authorize(void *ctx, - krb5_context context, - const char *app, - hx509_request csr, - krb5_const_principal client, - krb5_boolean *result) -{ - krb5_error_code ret; - hx509_context hx509ctx = NULL; - KeyUsage ku; - const char *d; - size_t i; - char *princ = NULL; - char *s = NULL; - - if ((d = krb5_config_get_string(context, NULL, app ? app : "kdc", - "simple_csr_authorizer_directory", - NULL)) == NULL) - return KRB5_PLUGIN_NO_HANDLE; - - if ((ret = hx509_context_init(&hx509ctx))) - return ret; - - if ((ret = krb5_unparse_name(context, client, &princ))) - goto out; - - s = string_encode(princ); - free(princ); - princ = NULL; - if (s == NULL) - goto enomem; - - princ = s; - s = NULL; - - for (i = 0; ret == 0; i++) { - hx509_san_type san_type; - struct stat st; - const char *prefix; - char *san; - char *p; - - ret = hx509_request_get_san(csr, i, &san_type, &s); - if (ret) - break; - switch (san_type) { - case HX509_SAN_TYPE_EMAIL: - prefix = "email"; - break; - case HX509_SAN_TYPE_DNSNAME: - prefix = "dnsname"; - break; - case HX509_SAN_TYPE_XMPP: - prefix = "xmpp"; - break; - case HX509_SAN_TYPE_PKINIT: - prefix = "pkinit"; - break; - case HX509_SAN_TYPE_MS_UPN: - prefix = "ms-upn"; - break; - default: - ret = ENOTSUP; - break; - } - if (ret) - break; - - if ((san = string_encode(s)) == NULL || - asprintf(&p, "%s/%s/%s-%s", d, princ, prefix, san) == -1 || - p == NULL) { - free(san); - goto enomem; - } - ret = stat(p, &st) == -1 ? errno : 0; - free(san); - free(p); - frees(&s); - if (ret) - goto skip; - ret = hx509_request_authorize_san(csr, i); - } - frees(&s); - if (ret == HX509_NO_ITEM) - ret = 0; - if (ret) - goto out; - - for (i = 0; ret == 0; i++) { - struct stat st; - char *p; - - ret = hx509_request_get_eku(csr, i, &s); - if (ret) - break; - if (asprintf(&p, "%s/%s/eku-%s", d, princ, s) == -1 || p == NULL) - goto enomem; - ret = stat(p, &st) == -1 ? errno : 0; - free(p); - frees(&s); - if (ret) - goto skip; - ret = hx509_request_authorize_eku(csr, i); - } - if (ret == HX509_NO_ITEM) - ret = 0; - if (ret) - goto out; - - ku = int2KeyUsage(0); - ku.digitalSignature = 1; - ku.nonRepudiation = 1; - hx509_request_authorize_ku(csr, ku); - - *result = TRUE; - ret = 0; - goto out; - -skip: - /* Allow another plugin to get a crack at this */ - ret = KRB5_PLUGIN_NO_HANDLE; - goto out; - -enomem: - ret = krb5_enomem(context); - goto out; - -out: - hx509_context_free(&hx509ctx); - free(princ); - free(s); - return ret; -} - -static KRB5_LIB_CALL krb5_error_code -simple_csr_authorizer_init(krb5_context context, void **c) -{ - *c = NULL; - return 0; -} - -static KRB5_LIB_CALL void -simple_csr_authorizer_fini(void *c) -{ -} - -static krb5plugin_csr_authorizer_ftable plug_desc = - { 1, simple_csr_authorizer_init, simple_csr_authorizer_fini, authorize }; - -static krb5plugin_csr_authorizer_ftable *plugs[] = { &plug_desc }; - -static uintptr_t -simple_csr_authorizer_get_instance(const char *libname) -{ - if (strcmp(libname, "krb5") == 0) - return krb5_get_instance(libname); - if (strcmp(libname, "kdc") == 0) - return kdc_get_instance(libname); - if (strcmp(libname, "hx509") == 0) - return hx509_get_instance(libname); - return 0; -} - -krb5_plugin_load_ft kdc_csr_authorizer_plugin_load; - -krb5_error_code KRB5_CALLCONV -kdc_csr_authorizer_plugin_load(heim_pcontext context, - krb5_get_instance_func_t *get_instance, - size_t *num_plugins, - krb5_plugin_common_ftable_cp **plugins) -{ - *get_instance = simple_csr_authorizer_get_instance; - *num_plugins = sizeof(plugs) / sizeof(plugs[0]); - *plugins = (krb5_plugin_common_ftable_cp *)plugs; - return 0; -} diff --git a/kdc/test_csr_authorizer.c b/kdc/test_csr_authorizer.c index 1d526f77b..da8de0f52 100644 --- a/kdc/test_csr_authorizer.c +++ b/kdc/test_csr_authorizer.c @@ -1,8 +1,84 @@ +/* + * Copyright (c) 2022 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * 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. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE 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 "kdc_locl.h" +#include + +/* + * This program implements two things: + * + * - a utility for testing the `kdc_authorize_csr()' function and the plugins + * that uses, + * + * and + * + * - a server for the IPC authorizer. + * + * For the latter, requested certificate SANs and EKUs are authorized by + * checking for existence of files of the form: + * + * ///- + * + * where is given as an option. + * + * is a requesting client principal name with all characters other than + * alphanumeric, '-', '_', and non-leading '.' URL-encoded. + * + * is one of: + * + * - pkinit (SAN) + * - xmpp (SAN) + * - email (SAN) + * - ms-upn (SAN) + * - dnsname (SAN) + * - eku (EKU OID) + * + * and is a display form of the SAN or EKU OID, with SANs URL-encoded + * just like principal names (see above). + * + * OIDs are of the form "1.2.3.4.5". + * + * Only digitalSignature and nonRepudiation key usage values are permitted. + */ static int help_flag; static int version_flag; +static int daemon_flag; +static int daemon_child_flag = -1; +static int ignore_flag = 0; +static int server_flag = 0; static const char *app_string = "kdc"; +static const char *socket_dir; +static const char *authz_dir; struct getargs args[] = { { "help", 'h', arg_flag, &help_flag, @@ -11,6 +87,18 @@ struct getargs args[] = { "Print version", NULL }, { "app", 'a', arg_string, &app_string, "App to test (kdc or bx509); default: kdc", "APPNAME" }, + { "socket-dir", 'S', arg_string, &socket_dir, + "IPC socket directory", "DIR" }, + { "authorization-dir", 'A', arg_string, &authz_dir, + "authorization directory", "DIR" }, + { "server", '\0', arg_flag, &server_flag, + "Server mode", NULL }, + { "ignore", 'I', arg_flag, &ignore_flag, + "ignore requests", NULL }, + { "daemon", 'd', arg_flag, &daemon_flag, + "daemonize", NULL }, + { "daemon-child", '\0', arg_flag, &daemon_child_flag, + "internal-use-only option", NULL }, }; size_t num_args = sizeof(args) / sizeof(args[0]); @@ -19,9 +107,23 @@ usage(int e) { arg_printusage(args, num_args, NULL, "PATH-TO-DER-CSR PRINCIPAL"); fprintf(stderr, - "\n\tExercise CSR authorization plugins for a given CSR for a\n" - "\tgiven principal.\n" - "\n\tExample: %s PKCS10:/tmp/csr.der foo@TEST.H5L.SE\n", + "\tExercise CSR authorization plugins for a given CSR for a\n" + "\tgiven principal.\n\n" + "\tServer-mode (--server) looks for files in the \n" + "\t--authorization-dir DIR directory named:\n" + "\n" + "\t\teku=OID\n" + "\t\tsan_pkinit=PRINCIPAL\n" + "\t\tsan_ms_upn=PRINCIPAL\n" + "\t\tsan_dnsname=DOMAINNAME\n" + "\t\tsan_xmpp=JABBER-ID\n" + "\t\tsan_email=EMAIL\n" + "\n" + "\tClient-mode positional arguments are:\n\n" + "\t\tPATH-TO-DER-CSR PRETEND-CLIENT-PRINCIPAL [...]\n\n" + "\twhere {...} are requested features that must be granted\n" + "\tif the request is only partially authorized.\n\n" + "\tClient example:\n\t\t%s PKCS10:/tmp/csr.der foo@TEST.H5L.SE\n", getprogname()); exit(e); return e; @@ -58,6 +160,177 @@ load_plugins(krb5_context context) #endif } +static char *string_encode(const char *); +static int stat_authz(const char *, const char *); + +static krb5_error_code +authorize(const char *subject, const char *thing) +{ + krb5_error_code ret; + char *s = NULL; + + s = string_encode(subject); + if (s == NULL) + return ENOMEM; + + ret = stat_authz(s, thing); + free(s); + if (ret == ENOENT) + ret = stat_authz(s, "all"); + if (ret == ENOENT) + ret = EACCES; + return ret; +} + +static void +service(void *ctx, + const heim_octet_string *req, + const heim_icred cred, + heim_ipc_complete complete_cb, + heim_sipc_call complete_cb_data) +{ + krb5_error_code ret = 0; + struct rk_strpool *result = NULL; + krb5_data rep; + const char *subject; + char *cmd; + char *next = NULL; + char *res = NULL; + char *tok; + char *s; + int none_granted = 1; + int all_granted = 1; + int first = 1; + + /* + * A krb5_context and log facility for logging would be nice, but this is + * all just for testing. + */ + + (void)ctx; + + cmd = strndup(req->data, req->length); + if (cmd == NULL) + errx(1, "Out of memory"); + + if (strncmp(cmd, "check ", sizeof("check ") - 1) != 0) { + rep.data = "Invalid request command (must be \"check ...\")"; + rep.length = sizeof("Invalid request command (must be \"check ...\")") - 1; + (*complete_cb)(complete_cb_data, EINVAL, &rep); + free(cmd); + return; + } + + s = cmd + sizeof("check ") - 1; + subject = tok = strtok_r(s, " ", &next); + s = NULL; + + while ((tok = strtok_r(s, " ", &next))) { + int ret2; + + ret2 = authorize(subject, tok); + result = rk_strpoolprintf(result, "%s%s:%s", + first ? "" : ",", + tok, + ret2 == 0 ? "granted" : "denied"); + if (ret2 == 0) + none_granted = 0; + else + all_granted = 0; + + if (ret2 != 0 && ret == 0) + ret = ret2; + + first = 0; + } + free(cmd); + + if (ret == 0 && all_granted) { + rk_strpoolfree(result); + + rep.data = "granted"; + rep.length = sizeof("granted") - 1; + (*complete_cb)(complete_cb_data, 0, &rep); + return; + } + + if (none_granted && ignore_flag) { + rk_strpoolfree(result); + + rep.data = "ignore"; + rep.length = sizeof("ignore") - 1; + (*complete_cb)(complete_cb_data, KRB5_PLUGIN_NO_HANDLE, &rep); + return; + } + + s = rk_strpoolcollect(result); /* frees `result' */ + if (s == NULL) { + rep.data = "denied out-of-memory"; + rep.length = sizeof("denied out-of-memory") - 1; + (*complete_cb)(complete_cb_data, KRB5_PLUGIN_NO_HANDLE, &rep); + return; + } + + if (asprintf(&res, "denied %s", s) == -1) + errx(1, "Out of memory"); + if (res == NULL) + errx(1, "Out of memory"); + + rep.data = res; + rep.length = strlen(res); + + (*complete_cb)(complete_cb_data, ret, &rep); + free(res); + free(s); +} + +static char * +make_feature_argument(const char *kind, + hx509_san_type san_type, + const char *value) +{ + const char *san_type_str = NULL; + char *s = NULL; + + if (strcmp(kind, "san") != 0) { + if (asprintf(&s, "%s=%s", kind, value) == -1 || s == NULL) + errx(1, "Out of memory"); + return s; + } + + switch (san_type) { + case HX509_SAN_TYPE_EMAIL: + san_type_str = "email"; + break; + case HX509_SAN_TYPE_DNSNAME: + san_type_str = "dnsname"; + break; + case HX509_SAN_TYPE_DN: + san_type_str = "dn"; + break; + case HX509_SAN_TYPE_REGISTERED_ID: + san_type_str = "registered_id"; + break; + case HX509_SAN_TYPE_XMPP: + san_type_str = "xmpp"; + break; + case HX509_SAN_TYPE_PKINIT: + case HX509_SAN_TYPE_MS_UPN: + san_type_str = "pkinit"; + break; + case HX509_SAN_TYPE_DNSSRV: + san_type_str = "dnssrv"; + break; + default: + warnx("SAN type not supported"); + return ""; + } + + if (asprintf(&s, "san_%s=%s", san_type_str, value) == -1 || s == NULL) + errx(1, "Out of memory"); + return s; +} + int main(int argc, char **argv) { @@ -79,24 +352,143 @@ main(int argc, char **argv) return 0; } - argc -= optidx; - argv += optidx; - - if (argc != 2) - usage(1); - if ((errno = krb5_init_context(&context))) err(1, "Could not initialize krb5_context"); if ((ret = krb5_initlog(context, argv0, &logf)) || (ret = krb5_addlog_dest(context, logf, "0-5/STDERR"))) krb5_err(context, 1, ret, "Could not set up logging to stderr"); load_plugins(context); + + if (server_flag && daemon_flag) + daemon_child_flag = roken_detach_prep(argc, argv, "--daemon-child"); + + argc -= optidx; + argv += optidx; + + if (socket_dir) + setenv("HEIM_IPC_DIR", socket_dir, 1); + + if (server_flag) { + const char *svc; + heim_sipc un; + + rk_pidfile(NULL); + + svc = krb5_config_get_string(context, NULL, + app_string ? app_string : "kdc", + "ipc_csr_authorizer", "service", NULL); + if (svc == NULL) + svc = "org.h5l.csr_authorizer"; + + /* `service' is our request handler; `argv' is its callback data */ + ret = heim_sipc_service_unix(svc, service, NULL, &un); + if (ret) + krb5_err(context, 1, ret, + "Could not setup service on Unix domain socket " + "%s/.heim_%s-socket", socket_dir, svc); + + roken_detach_finish(NULL, daemon_child_flag); + + /* Enter the IPC event loop */ + heim_ipc_main(); + return 0; + } + + /* Client mode */ + if (argc < 2) + usage(1); + + /* Parse the given CSR */ if ((ret = hx509_request_parse(context->hx509ctx, argv[0], &csr))) krb5_err(context, 1, ret, "Could not parse PKCS#10 CSR from %s", argv[0]); + + /* + * Parse the client principal that we'll pretend is an authenticated client + * principal. + */ if ((ret = krb5_parse_name(context, argv[1], &princ))) krb5_err(context, 1, ret, "Could not parse principal %s", argv[1]); - if ((ret = kdc_authorize_csr(context, app_string, csr, princ))) - krb5_err(context, 1, ret, "Authorization failed"); + + /* Call the authorizer */ + ret = kdc_authorize_csr(context, app_string, csr, princ); + + if (ret) { + unsigned n = hx509_request_count_unauthorized(csr); + size_t i, k; + int ret2 = 0; + int good = -1; + + /* + * Check partial approval of SANs. + * + * Iterate over the SANs in the request, and for each check if a) it + * was granted, b) it's on the remainder of our argv[]. + */ + for (i = 0; ret2 == 0; i++) { + hx509_san_type san_type; + char *feature = NULL; + char *san = NULL; + int granted; + + ret2 = hx509_request_get_san(csr, i, &san_type, &san); + if (ret2) + break; + + feature = make_feature_argument("san", san_type, san); + + granted = hx509_request_san_authorized_p(csr, i); + for (k = 2; k < argc; k++) { + if (strcmp(feature, argv[k]) != 0) + continue; + + /* The SAN is on our command line */ + if (granted && good == -1) + good = 1; + else if (!granted) + good = 0; + break; + } + + hx509_xfree(san); + } + + /* Check partial approval of EKUs */ + for (i = 0; ret2 == 0; i++) { + char *feature = NULL; + char *eku = NULL; + int granted; + + ret2 = hx509_request_get_eku(csr, i, &eku); + if (ret2) + break; + + feature = make_feature_argument("eku", 0, eku); + + granted = hx509_request_eku_authorized_p(csr, i); + for (k = 2; k < argc; k++) { + if (strcmp(feature, argv[k]) != 0) + continue; + + /* The SAN is on our command line */ + if (granted && good == -1) + good = 1; + else if (!granted) + good = 0; + break; + } + + hx509_xfree(eku); + } + + if (good != 1) { + krb5_free_principal(context, princ); + _krb5_unload_plugins(context, "kdc"); + hx509_request_free(&csr); + krb5_err(context, 1, ret, + "Authorization failed with %u rejected features", n); + } + } + printf("Authorized!\n"); krb5_free_principal(context, princ); _krb5_unload_plugins(context, "kdc"); @@ -104,3 +496,102 @@ main(int argc, char **argv) hx509_request_free(&csr); return 0; } + +/* + * string_encode_sz() and string_encode() encode a string to be safe for use as + * a file name. They function very much like URL encoders, but '~' also gets + * encoded, and '@', '-', '_', and non-leading '.' do not. + * + * A corresponding decoder is not needed. + */ +static size_t +string_encode_sz(const char *in) +{ + size_t sz = strlen(in); + int first = 1; + + while (*in) { + char c = *(in++); + + switch (c) { + case '@': + case '-': + case '_': + break; + case '.': + if (first) + sz += 2; + break; + default: + if (!isalnum(c)) + sz += 2; + } + first = 0; + } + return sz; +} + +static char * +string_encode(const char *in) +{ + size_t len = strlen(in); + size_t sz = string_encode_sz(in); + size_t i, k; + char *s; + int first = 1; + + if ((s = malloc(sz + 1)) == NULL) + return NULL; + s[sz] = '\0'; + + for (i = k = 0; i < len; i++, first = 0) { + unsigned char c = ((const unsigned char *)in)[i]; + + switch (c) { + case '@': + case '-': + case '_': + s[k++] = c; + break; + case '.': + if (first) { + s[k++] = '%'; + s[k++] = "0123456789abcdef"[(c&0xff)>>4]; + s[k++] = "0123456789abcdef"[(c&0x0f)]; + } else { + s[k++] = c; + } + break; + default: + if (isalnum(c)) { + s[k++] = c; + } else { + s[k++] = '%'; + s[k++] = "0123456789abcdef"[(c&0xff)>>4]; + s[k++] = "0123456789abcdef"[(c&0x0f)]; + } + } + } + return s; +} + +static int +stat_authz(const char *subject, + const char *thing) +{ + struct stat st; + char *p = NULL; + int ret; + + if (authz_dir == NULL) + return KRB5_PLUGIN_NO_HANDLE; + if (thing) + ret = asprintf(&p, "%s/%s/%s", authz_dir, subject, thing); + else + ret = asprintf(&p, "%s/%s", authz_dir, subject); + if (ret == -1 || p == NULL) + return ENOMEM; + ret = stat(p, &st); + free(p); + return ret == 0 ? 0 : errno; +} diff --git a/tests/kdc/Makefile.am b/tests/kdc/Makefile.am index f61a7e853..53b990847 100644 --- a/tests/kdc/Makefile.am +++ b/tests/kdc/Makefile.am @@ -311,7 +311,7 @@ krb5-pkinit-win.conf: krb5-pkinit.conf.in Makefile mv krb5-pkinit-win.conf.tmp krb5-pkinit-win.conf clean: clean-am - rm -rf cc_dir simple_csr_authz + rm -rf cc_dir authz_dir CLEANFILES= \ $(TESTS) \ diff --git a/tests/kdc/check-bx509.in b/tests/kdc/check-bx509.in index 0c61a20d0..516fa9714 100644 --- a/tests/kdc/check-bx509.in +++ b/tests/kdc/check-bx509.in @@ -74,11 +74,18 @@ klistjson="${klist} --json -c $cache" klist="${klist} --hidden -v -c $cache" kgetcred="${kgetcred} -c $cache" kdestroy="${kdestroy} -c $cache ${afs_no_unlog}" +test_csr_authorizer="$test_csr_authorizer -A $objdir/authz_dir -S $objdir" kx509="${kx509} -c $cache" KRB5_CONFIG="${objdir}/krb5-bx509.conf" export KRB5_CONFIG +HEIM_PIDFILE_DIR="${objdir}/" +export HEIM_PIDFILE_DIR + +HEIM_IPC_DIR=$objdir +export HEIM_IPC_DIR + rsa=yes pkinit=no if ${hxtool} info | grep 'rsa: hx509 null RSA' > /dev/null ; then @@ -102,26 +109,26 @@ rm -f current-db* rm -f out-* rm -f mkey.file* rm -f *.pem *.crt *.der -rm -rf simple_csr_authz +rm -rf authz_dir -mkdir -p simple_csr_authz +mkdir -p authz_dir > messages.log -# We'll avoid using a KDC for now. For testing /bx509 we only need keys for -# Negotiate tokens, and we'll use ktutil and kimpersonate to make it possible -# to create and accept those without a KDC. When we test /bnegotiate, however, -# we'll start a KDC. +kdcpid= +bx509pid= +test_csr_authorizer_pid= +trap 'kill -9 ${kdcpid} ${bx509pid} ${test_csr_authorizer_pid}; echo signal killing kdc, bx509d, and test_csr_authorizer; exit 1;' EXIT # csr_grant ext-type value grantee_principal csr_grant() { - mkdir -p "${objdir}/simple_csr_authz/${3}" - touch "${objdir}/simple_csr_authz/${3}/${1}-${2}" + mkdir -p "${objdir}/authz_dir/${3}" + touch "${objdir}/authz_dir/${3}/${1}=${2}" } csr_revoke() { - rm -rf "${objdir}/simple_csr_authz" - mkdir -p "${objdir}/simple_csr_authz" + rm -rf "${objdir}/authz_dir" + mkdir -p "${objdir}/authz_dir" } # get_cert "" curl-opts @@ -254,7 +261,7 @@ $hxtool ca --issue-ca --type=https-negotiate-server \ # XXX Before starting bx509d let us use kdc test programs to check that: # # - the negotiate token validator plugin works -# - the simple CSR authorizer plugin works +# - the authz_dir CSR authorizer plugin works # - the KDC CA tester program works echo "Check gss-token and Negotiate token validator plugin" @@ -265,6 +272,64 @@ token=$(KRB5CCNAME=$cache $gsstoken HTTP@$server) $test_token_validator -a datan.test.h5l.se Negotiate "$token" || { echo "Negotiate token validator failed to validate valid token"; exit 2; } + +echo "Starting CSR authorizer IPC service" +$test_csr_authorizer --server --daemon || + { echo "Failed to start test_csr_authorizer service"; exit 2; } +test_csr_authorizer_pid=`getpid test_csr_authorizer` + +# Make a CSR for foo@$R +$hxtool request-create --subject='' --generate-key=rsa --key-bits=1024 \ + --key=FILE:"${objdir}/k.der" --kerberos=foo@$R \ + ${objdir}/req || + { echo "Failed to make a CSR"; exit 2; } + +echo "Test CSR authorizer IPC service (deny foo@$R to san_pkinit=foo@$R)" +csr_revoke +$test_csr_authorizer PKCS10:${objdir}/req foo@$R && + { echo "CSR authorizer IPC service granted foo@$R"; exit 2; } + +echo "Test CSR authorizer IPC service (grant foo@$R to san_pkinit=foo@$R)" +csr_grant san_pkinit foo@$R foo@${R} +$test_csr_authorizer PKCS10:${objdir}/req foo@$R || + { echo "CSR authorizer IPC service rejected foo@$R"; exit 2; } + +# Make a CSR for bar@$R +$hxtool request-create --subject='' --key-bits=1024 \ + --key=FILE:"${objdir}/k.der" --kerberos=bar@$R \ + ${objdir}/req || + { echo "Failed to make a CSR"; exit 2; } + +echo "Test CSR authorizer IPC service (deny foo@$R to san_pkinit=bar@$R)" +$test_csr_authorizer PKCS10:${objdir}/req foo@$R && + { echo "CSR authorizer IPC service accepted foo@$R"; exit 2; } + +echo "Test CSR authorizer IPC service (grant foo@$R to san_pkinit=bar@$R)" +csr_grant san_pkinit foo@$R bar@${R} +$test_csr_authorizer PKCS10:${objdir}/req foo@$R && + { echo "CSR authorizer IPC service accepted foo@$R"; exit 2; } + +# Make a CSR for foo@$R and bar@$R +$hxtool request-create --subject='' --key-bits=1024 \ + --key=FILE:"${objdir}/k.der" \ + --kerberos=foo@$R --kerberos=bar@$R \ + ${objdir}/req || + { echo "Failed to make a CSR"; exit 2; } + +# Check that the authorizer does mark foo@$R as approved even though it denies +# the overall request because it rejects bar@$R +echo "Test CSR authorizer IPC service (partial authz)" +csr_revoke +csr_grant san_pkinit foo@$R foo@${R} +# Check that the authorizer grants foo@$R +$test_csr_authorizer PKCS10:${objdir}/req foo@$R san_pkinit=foo@$R || + { echo "CSR authorizer IPC service partial approval check fail"; exit 2; } +# Check that the authorizer rejects bar@$R +$test_csr_authorizer PKCS10:${objdir}/req foo@$R san_pkinit=bar@$R && + { echo "CSR authorizer IPC service partial approval check fail"; exit 2; } +$test_csr_authorizer PKCS10:${objdir}/req foo@$R san_pkinit=foo@$R san_pkinit=bar@$R && + { echo "CSR authorizer IPC service partial approval check fail"; exit 2; } + echo "Making a plain CSR" $hxtool request-create --subject='' --generate-key=rsa --key-bits=1024 \ --key=FILE:"${objdir}/k.der" "${objdir}/req" || @@ -297,7 +362,7 @@ $hxtool request-create --subject='' --generate-key=rsa --key-bits=1024 \ $test_kdc_ca -a bx509 foo@${R} PKCS10:${objdir}/req \ PEM-FILE:${objdir}/server.pem && { echo "Trivial offline CA test failed: unauthorized issuance (dNSName)"; exit 2; } -csr_grant dnsname foo.test.h5l.se foo@${R} +csr_grant san_dnsname foo.test.h5l.se foo@${R} csr_grant eku 1.3.6.1.5.5.7.3.1 foo@${R} $test_kdc_ca -a bx509 foo@${R} PKCS10:${objdir}/req \ PEM-FILE:${objdir}/server.pem || @@ -316,7 +381,7 @@ $hxtool request-create --subject='' --generate-key=rsa --key-bits=1024 \ $test_kdc_ca -a bx509 foo@${R} PKCS10:${objdir}/req \ PEM-FILE:${objdir}/email.pem && { echo "Offline CA test failed: unauthorized issuance (dNSName)"; exit 2; } -csr_grant email foo@test.h5l.se foo@${R} +csr_grant san_email foo@test.h5l.se foo@${R} csr_grant eku 1.3.6.1.5.5.7.3.2 foo@${R} $test_kdc_ca -a bx509 foo@${R} PKCS10:${objdir}/req \ PEM-FILE:${objdir}/email.pem || @@ -338,21 +403,33 @@ if ! test -x ${objdir}/../../kdc/bx509d; then fi echo "Creating database" -${kadmin} init \ - --realm-max-ticket-life=1day \ - --realm-max-renewable-life=1month \ - ${R} || exit 1 -${kadmin} add -r --use-defaults foo@${R} || exit 1 -${kadmin} add -r --use-defaults bar@${R} || exit 1 -${kadmin} add -r --use-defaults baz@${R} || exit 1 -${kadmin} modify --pkinit-acl="CN=foo,DC=test,DC=h5l,DC=se" foo@${R} || exit 1 +rm -f $kt $ukt +${kadmin} < /dev/null; then + cat "${cachefile}.json" + echo "Request failed w/o error information" + exit 2; + fi +fi +cat "${cachefile}.json" +if grep ccache "${cachefile}.json"; then echo "Got TGTs with /get-tgts end-point that should have been denied" + exit 2; +fi + +echo "Fetch TGTs (batch, partial authz with IPC authorizer)" +${kadmin} modify --max-ticket-life=10d krbtgt/${R}@${R} +csr_revoke +csr_grant san_pkinit bar@${R} foo@${R} +csr_grant san_pkinit baz@${R} foo@${R} +${kdestroy} +token=$(KRB5CCNAME=$cache2 $gsstoken HTTP@$server) +if ! (set -vx; + curl -vvvo "${cachefile}.json" -Lgsf \ + --resolve ${server}:${bx509port}:127.0.0.1 \ + -H "Authorization: Negotiate $token" \ + "http://${server}:${bx509port}/get-tgts?cname=bar@${R}&cname=raz@${R}&cname=baz@${R}"); then + echo "Failed to get TGTs batch including non-existent principal" exit 2 fi +if which jq >/dev/null; then + set -vx + jq -e . "${cachefile}.json" > /dev/null || + { echo "/get-tgts produced non-JSON"; exit 2; } + jq -es '.[]|select(.name|startswith("raz@"))|(.error_code//empty)' "${cachefile}.json" > /dev/null || + { echo "No error was reported for raz@${R}!"; exit 2; } + jq -es '.[]|select(.name|startswith("raz@"))|(.ccache//"")|(length==0)' "${cachefile}.json" > /dev/null || + { echo "Non-empty ccache included for raz@${R}!"; exit 2; } + + # Check bar@$R's tickets: + jq -r 'select(.name|startswith("bar@")).ccache' "${cachefile}.json" | + $rkbase64 -d -- - > "${cachefile}" + ${kgetcred} -H HTTP/${server}@${R} || + { echo "Fetched TGT didn't work"; exit 2; } + ${klistjson} | jq -e --arg p bar@$R '.principal == $p' > /dev/null || + { echo "/get-tgts produced wrong TGTs"; exit 2; } + + # Check baz@$R's tickets: + jq -r 'select(.name|startswith("baz@")).ccache' "${cachefile}.json" | + $rkbase64 -d -- - > "${cachefile}" + ${kgetcred} -H HTTP/${server}@${R} || + { echo "Fetched TGT didn't work"; exit 2; } + ${klistjson} | jq -e --arg p baz@$R '.principal == $p' > /dev/null || + { echo "/get-tgts produced wrong TGTs"; exit 2; } +fi + +echo "Fetch TGTs (batch, partial authz)" +${kadmin} modify --max-ticket-life=10d krbtgt/${R}@${R} +csr_revoke +csr_grant san_pkinit bar@${R} foo@${R} +${kdestroy} +token=$(KRB5CCNAME=$cache2 $gsstoken HTTP@$server) +if ! (set -vx; + curl -vvvo "${cachefile}.json" -Lgsf \ + --resolve ${server}:${bx509port}:127.0.0.1 \ + -H "Authorization: Negotiate $token" \ + "http://${server}:${bx509port}/get-tgts?cname=not@${R}&cname=bar@${R}&cname=baz@${R}"); then + echo "Failed to get TGTs batch including non-existent principal" + exit 2 +fi +if which jq >/dev/null; then + set -vx + jq -e . "${cachefile}.json" > /dev/null || + { echo "/get-tgts produced non-JSON"; exit 2; } + jq -es '.[]|select(.name|startswith("not@"))|(.error_code//empty)' "${cachefile}.json" > /dev/null || + { echo "No error was reported for not@${R}!"; exit 2; } + jq -es '.[]|select(.name|startswith("not@"))|(.ccache//"")|(length==0)' "${cachefile}.json" > /dev/null || + { echo "Non-empty ccache included for not@${R}!"; exit 2; } + jq -es '.[]|select(.name|startswith("baz@"))|(.error_code//empty)' "${cachefile}.json" > /dev/null || + { echo "No error was reported for baz@${R}!"; exit 2; } + jq -es '.[]|select(.name|startswith("baz@"))|(.ccache//"")|(length==0)' "${cachefile}.json" > /dev/null || + { echo "Non-empty ccache included for baz@${R}!"; exit 2; } + + # Check bar@$R's tickets: + jq -r 'select(.name|startswith("bar@")).ccache' "${cachefile}.json" | + $rkbase64 -d -- - > "${cachefile}" + ${kgetcred} -H HTTP/${server}@${R} || + { echo "Fetched TGT didn't work"; exit 2; } + ${klistjson} | jq -e --arg p bar@$R '.principal == $p' > /dev/null || + { echo "/get-tgts produced wrong TGTs"; exit 2; } +fi echo "Fetch TGTs (batch, authz pass)" ${kadmin} modify --max-ticket-life=10d krbtgt/${R}@${R} -(csr_grant pkinit bar@${R} foo@${R}) -(csr_grant pkinit baz@${R} foo@${R}) +csr_grant san_pkinit bar@${R} foo@${R} +csr_grant san_pkinit baz@${R} foo@${R} ${kdestroy} token=$(KRB5CCNAME=$cache2 $gsstoken HTTP@$server) if ! (set -vx; @@ -726,9 +880,9 @@ fi echo "Fetch TGTs (batch, authz pass, one non-existent principal)" ${kadmin} modify --max-ticket-life=10d krbtgt/${R}@${R} -(csr_grant pkinit bar@${R} foo@${R}) -(csr_grant pkinit baz@${R} foo@${R}) -(csr_grant pkinit not@${R} foo@${R}) +csr_grant san_pkinit bar@${R} foo@${R} +csr_grant san_pkinit baz@${R} foo@${R} +csr_grant san_pkinit not@${R} foo@${R} ${kdestroy} token=$(KRB5CCNAME=$cache2 $gsstoken HTTP@$server) if ! (set -vx; @@ -913,9 +1067,10 @@ else exit 1 fi -echo "killing kdc (${kdcpid}) and bx509d (${bx509pid})" +echo "killing kdc (${kdcpid}) and bx509d (${bx509pid}) and test_csr_authorizer (${test_csr_authorizer_pid})" sh ${leaks_kill} kdc $kdcpid || ec=1 sh ${leaks_kill} bx509d $bx509pid || ec=1 +sh ${leaks_kill} test_csr_authorizer $test_csr_authorizer_pid || ec=1 trap "" EXIT diff --git a/tests/kdc/check-httpkadmind.in b/tests/kdc/check-httpkadmind.in index 816f753d0..9707fc14b 100644 --- a/tests/kdc/check-httpkadmind.in +++ b/tests/kdc/check-httpkadmind.in @@ -97,15 +97,19 @@ KRB5_CONFIG="${objdir}/krb5-httpkadmind.conf" export KRB5_CONFIG KRB5CCNAME=$cache export KRB5CCNAME +HEIM_PIDFILE_DIR=$objdir +export HEIM_PIDFILE_DIR +HEIM_IPC_DIR=$objdir +export HEIM_IPC_DIR rm -f current-db* rm -f out-* rm -f mkey.file* rm -f *.pem *.crt *.der -rm -rf simple_csr_authz +rm -rf authz_dir rm -f extracted_keytab* -mkdir -p simple_csr_authz +mkdir -p authz_dir > messages.log @@ -115,13 +119,13 @@ mkdir -p simple_csr_authz # grant ext-type value grantee_principal grant() { - mkdir -p "${objdir}/simple_csr_authz/${3}" - touch "${objdir}/simple_csr_authz/${3}/${1}-${2}" + mkdir -p "${objdir}/authz_dir/${3}" + touch "${objdir}/authz_dir/${3}/${1}=${2}" } revoke() { - rm -rf "${objdir}/simple_csr_authz" - mkdir -p "${objdir}/simple_csr_authz" + rm -rf "${objdir}/authz_dir" + mkdir -p "${objdir}/authz_dir" } if set -o|grep 'verbose.*on' > /dev/null || @@ -204,15 +208,18 @@ get_keytab_POST_redir() { kdcpid= httpkadmindpid= httpkadmind2pid= +test_csr_authorizer_pid= kadmindpid= kadmind2pid= cleanup() { test -n "$kdcpid" && { echo signal killing kdc; kill -9 "$kdcpid"; } + test -n "$test_csr_authorizer_pid" && + { echo signal killing test_csr_authorizer; kill -9 "$test_csr_authorizer_pid"; } test -n "$httpkadmindpid" && { echo signal killing httpkadmind; kill -9 "$httpkadmindpid"; } test -n "$httpkadmind2pid" && - { echo signal killing httpkadmind; kill -9 "$httpkadmind2pid"; } + { echo signal killing second httpkadmind; kill -9 "$httpkadmind2pid"; } test -n "$kadmindpid" && { echo signal killing kadmind; kill -9 "$kadmindpid"; } test -n "$kadmind2pid" && @@ -224,29 +231,28 @@ rm -f extracted_keytab echo "Creating database" rm -f $kt $ukt -${kadmin} init \ - --realm-max-ticket-life=1day \ - --realm-max-renewable-life=1month \ - ${R} || exit 1 -${kadmin} add -r --use-defaults foo@${R} || exit 1 -${kadmin} add -r --use-defaults httpkadmind/admin@${R} || exit 1 -${kadmin} add -r --use-defaults WELLKNOWN/CSRFTOKEN@${R} || exit 1 -${kadmin} add -r --use-defaults HTTP/localhost@${R} || exit 1 -${kadmin} add -r --use-defaults host/xyz.${domain}@${R} || exit 1 -${kadmin} add -r --use-defaults HTTP/xyz.${domain}@${R} || exit 1 -${kadmin} add_ns --key-rotation-epoch=-1d --key-rotation-period=5m \ - --max-ticket-life=1d --max-renewable-life=5d \ - --attributes= HTTP/ns.${domain}@${R} || exit 1 -${kadmin} add_ns --key-rotation-epoch=-1d --key-rotation-period=5m \ - --max-ticket-life=1d --max-renewable-life=5d \ - --attributes=ok-as-delegate host/.ns2.${domain}@${R} || exit 1 -${kadmin} add -r --use-defaults HTTP/${server}@${R} || exit 1 -${kadmin} ext_keytab -r -k $keytab kadmin/admin@${R} || exit 1 -${kadmin} ext_keytab -r -k $keytab httpkadmind/admin@${R} || exit 1 -${kadmin} ext_keytab -r -k $keytab HTTP/${server}@${R} || exit 1 -${kadmin} ext_keytab -r -k $keytab HTTP/localhost@${R} || exit 1 -${kadmin} add -r --use-defaults HTTP/${otherserver}@${R} || exit 1 -${kadmin} ext_keytab -r -k $ukeytab foo@${R} || exit 1 +${kadmin} </dev/null || { echo "failed to setup kimpersonate credentials"; exit 2; } +echo "Starting test_csr_authorizer" +${test_csr_authorizer} -A $objdir/authz_dir -S $objdir --server --daemon || + { echo "test_csr_authorizer failed to start"; exit 2; } +test_csr_authorizer_pid=`getpid test_csr_authorizer` +ec=0 + echo "Starting httpkadmind" ${httpkadmind} -H $server -H localhost --local -t --daemon || { echo "httpkadmind failed to start"; exit 2; } @@ -291,7 +303,7 @@ hn=xyz.${domain} p=HTTP/$hn echo "Fetching keytab for concrete principal $p" rm -f extracted_keytab* -grant dnsname $hn foo@${R} +grant san_dnsname $hn foo@${R} ${kadmin} ext_keytab -k extracted_keytab $p || { echo "Failed to get a keytab for $p with kadmin"; exit 1; } ${ktutil} -k "${objdir}/extracted_keytab" list --keys > extracted_keytab.kadmin || @@ -307,7 +319,7 @@ hn=foo.ns.${domain} p=HTTP/$hn echo "Fetching keytab for virtual principal $p" rm -f extracted_keytab* -grant dnsname $hn foo@${R} +grant san_dnsname $hn foo@${R} ${kadmin} ext_keytab -k extracted_keytab $p || { echo "Failed to get a keytab for $p with kadmin"; exit 1; } ${ktutil} -k "${objdir}/extracted_keytab" list --keys > extracted_keytab.kadmin || @@ -329,9 +341,9 @@ p2=HTTP/$hn2 p3=HTTP/$hn3 echo "Fetching keytabs for more than one principal" rm -f extracted_keytab* -grant dnsname $hn1 foo@${R} -grant dnsname $hn2 foo@${R} -grant dnsname $hn3 foo@${R} +grant san_dnsname $hn1 foo@${R} +grant san_dnsname $hn2 foo@${R} +grant san_dnsname $hn3 foo@${R} # Note that httpkadmind will first process dNSName q-params, then the spn # q-params. ${kadmin} ext_keytab -k extracted_keytab $p1 || @@ -372,7 +384,7 @@ hn=xyz.${domain} p=host/$hn echo "Fetching keytab for virtual principal $p" rm -f extracted_keytab* -grant dnsname $hn foo@${R} +grant san_dnsname $hn foo@${R} get_keytab "service=host&dNSName=xyz.${domain}" -sf -o "${objdir}/extracted_keytab" && { echo "Got a keytab for $p even though it is a host service!"; exit 1; } get_keytab "spn=host/xyz.${domain}" -sf -o "${objdir}/extracted_keytab" && @@ -383,7 +395,7 @@ hn=xyz.${domain} p=HTTP/$hn echo "Checking key rotation for concrete principal $p" rm -f extracted_keytab* -grant dnsname $hn foo@${R} +grant san_dnsname $hn foo@${R} get_keytab "dNSName=${hn}" -sf -o "${objdir}/extracted_keytab" || { echo "Failed to get a keytab for $p with curl"; exit 1; } ${ktutil} -k "${objdir}/extracted_keytab" list --keys > extracted_keytab.rest1 || @@ -405,7 +417,7 @@ hn=xyz.${domain} p=HTTP/$hn echo "Checking key rotation w/ revocation for concrete principal $p" rm -f extracted_keytab* -grant dnsname $hn foo@${R} +grant san_dnsname $hn foo@${R} get_keytab "dNSName=${hn}" -sf -o "${objdir}/extracted_keytab" || { echo "Failed to get a keytab for $p with curl"; exit 1; } ${ktutil} -k "${objdir}/extracted_keytab" list --keys > extracted_keytab.rest1 || @@ -425,7 +437,7 @@ hn=abc.${domain} p=HTTP/$hn echo "Checking concrete principal creation ($p)" rm -f extracted_keytab -grant dnsname $hn foo@${R} +grant san_dnsname $hn foo@${R} get_keytab "dNSName=${hn}&create=true" -sf -o "${objdir}/extracted_keytab" && { echo "GET succeeded for write operation!"; exit 1; } get_keytab_POST "dNSName=${hn}&create=true" -s -o "${objdir}/extracted_keytab" || @@ -444,7 +456,7 @@ hn=bar.ns.${domain} p=HTTP/$hn echo "Checking materialization of virtual principal ($p)" rm -f extracted_keytab -grant dnsname $hn foo@${R} +grant san_dnsname $hn foo@${R} get_keytab "dNSName=${hn}&materialize=true" -sf -o "${objdir}/extracted_keytab" && { echo "GET succeeded for write operation!"; exit 1; } get_keytab_POST "dNSName=${hn}&materialize=true" -s -o "${objdir}/extracted_keytab" || @@ -471,7 +483,7 @@ p=HTTP/$hn restport=$restport2 echo "Checking principal creation at secondary yields redirect" rm -f extracted_keytab -grant dnsname $hn foo@${R} +grant san_dnsname $hn foo@${R} get_keytab_POST_redir "dNSName=${hn}&create=true" \ -s -o "${objdir}/extracted_keytab" ${ktutil} -k "${objdir}/extracted_keytab" list --keys > extracted_keytab.rest || @@ -559,7 +571,7 @@ hn=xyz.${domain} p=HTTP/$hn echo "Fetching keytab for concrete principal $p using remote HDB" rm -f extracted_keytab* -grant dnsname $hn httpkadmind/admin@${R} +grant san_dnsname $hn httpkadmind/admin@${R} KRB5CCNAME=$admincache ${kadmin} ext_keytab -k extracted_keytab $p || { echo "Failed to get a keytab for $p with kadmin"; exit 1; } ${ktutil} -k "${objdir}/extracted_keytab" list --keys > extracted_keytab.kadmin || @@ -575,7 +587,7 @@ hn=xyz.${domain} p=HTTP/$hn echo "Checking key rotation for concrete principal $p using remote HDB" rm -f extracted_keytab* -grant dnsname $hn foo@${R} +grant san_dnsname $hn foo@${R} get_keytab "dNSName=${hn}" -sf -o "${objdir}/extracted_keytab" || { echo "Failed to get a keytab for $p with curl"; exit 1; } ${ktutil} -k "${objdir}/extracted_keytab" list --keys > extracted_keytab.rest1 || @@ -610,7 +622,7 @@ hn=xyz.${domain} p=HTTP/$hn echo "Fetching keytab for concrete principal $p using local read-only HDB" rm -f extracted_keytab* -grant dnsname $hn httpkadmind/admin@${R} +grant san_dnsname $hn httpkadmind/admin@${R} KRB5CCNAME=$admincache ${kadmin} ext_keytab -k extracted_keytab $p || { echo "Failed to get a keytab for $p with kadmin"; exit 1; } ${ktutil} -k "${objdir}/extracted_keytab" list --keys > extracted_keytab.kadmin || @@ -626,7 +638,7 @@ hn=xyz.${domain} p=HTTP/$hn echo "Checking key rotation for concrete principal $p using local read-only HDB and remote HDB" rm -f extracted_keytab* -grant dnsname $hn foo@${R} +grant san_dnsname $hn foo@${R} get_keytab "dNSName=${hn}" -sf -o "${objdir}/extracted_keytab" || { echo "Failed to get a keytab for $p with curl"; exit 1; } ${ktutil} -k "${objdir}/extracted_keytab" list --keys > extracted_keytab.rest1 || @@ -810,6 +822,7 @@ KRB5CCNAME=$admincache ${kadmin} get $p | grep 'Internal error' messages.log && { echo "Internal errors in log"; exit 1; } +sh ${leaks_kill} test_csr_authorizer $test_csr_authorizer_pid || ec=1 sh ${leaks_kill} httpkadmind $httpkadmindpid || ec=1 sh ${leaks_kill} kadmind $kadmindpid || ec=1 sh ${leaks_kill} kadmind $kadmind2pid || ec=1 diff --git a/tests/kdc/check-pkinit.in b/tests/kdc/check-pkinit.in index 9f90fd040..571a64e9c 100644 --- a/tests/kdc/check-pkinit.in +++ b/tests/kdc/check-pkinit.in @@ -64,6 +64,11 @@ kx509="${kx509} -c $cache" KRB5_CONFIG="${objdir}/krb5-pkinit.conf" export KRB5_CONFIG +HEIM_PIDFILE_DIR=$objdir +export HEIM_PIDFILE_DIR +HEIM_IPC_DIR=$objdir +export HEIM_IPC_DIR + rsa=yes pkinit=no diff --git a/tests/kdc/krb5-bx509.conf.in b/tests/kdc/krb5-bx509.conf.in index 8a9d0bb49..2cd6fef22 100644 --- a/tests/kdc/krb5-bx509.conf.in +++ b/tests/kdc/krb5-bx509.conf.in @@ -29,9 +29,6 @@ # Locate kdc plugins for testing plugin_dir = @objdir@/../../kdc/.libs - # Configure kdc plugins for testing - simple_csr_authorizer_directory = @objdir@/simple_csr_authz - enable-pkinit = true pkinit_identity = PEM-FILE:@objdir@/user-issuer.pem pkinit_anchors = PEM-FILE:@objdir@/pkinit-anchor.pem @@ -86,7 +83,6 @@ db-dir = @objdir@ [bx509] - simple_csr_authorizer_directory = @objdir@/simple_csr_authz realms = { TEST.H5L.SE = { # Default (no cert exts requested) @@ -127,7 +123,6 @@ [get-tgt] no_addresses = true allow_addresses = true - simple_csr_authorizer_directory = @objdir@/simple_csr_authz realms = { TEST.H5L.SE = { # Default (no cert exts requested) diff --git a/tests/kdc/krb5-httpkadmind.conf.in b/tests/kdc/krb5-httpkadmind.conf.in index f887e82c4..fb2fc6a2f 100644 --- a/tests/kdc/krb5-httpkadmind.conf.in +++ b/tests/kdc/krb5-httpkadmind.conf.in @@ -28,9 +28,6 @@ # Locate kdc plugins for testing plugin_dir = @objdir@/../../kdc/.libs - # Configure kdc plugins for testing - simple_csr_authorizer_directory = @objdir@/simple_csr_authz - database = { dbname = @objdir@/current-db realm = TEST.H5L.SE @@ -84,7 +81,6 @@ virtual_hostbased_princ_svcs = HTTP host [ext_keytab] - simple_csr_authorizer_directory = @objdir@/simple_csr_authz new_hostbased_service_principal_attributes = { host = { a-particular-hostname.test.h5l.se = ok-as-delegate,no-auth-data-reqd diff --git a/tests/kdc/krb5-pkinit.conf.in b/tests/kdc/krb5-pkinit.conf.in index fbc21277a..e2d3f3d26 100644 --- a/tests/kdc/krb5-pkinit.conf.in +++ b/tests/kdc/krb5-pkinit.conf.in @@ -25,7 +25,9 @@ plugin_dir = @objdir@/../../kdc/.libs - simple_csr_authorizer_directory = @objdir@/simple_csr_authz + ipc_csr_authorizer = { + optional = true + } enable_kx509 = true require_initial_kca_tickets = false