You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1146 lines
40 KiB
1146 lines
40 KiB
From 5a0b9b43a070c273ae4ee39ee460fa759ff9d934 Mon Sep 17 00:00:00 2001 |
|
From: Matt Rogers <mrogers@redhat.com> |
|
Date: Tue, 28 Feb 2017 15:55:24 -0500 |
|
Subject: [PATCH] Add certauth pluggable interface |
|
|
|
Add the header include/krb5/certauth_plugin.h, defining a pluggable |
|
interface to control authorization of PKINIT client certificates. |
|
|
|
Add the "pkinit_san" and "pkinit_eku" builtin certauth modules and |
|
related PKINIT crypto X.509 helper functions. Add authorize_cert() as |
|
the entry function for certauth plugin module checks called in |
|
pkinit_server_verify_padata(). Modify kdcpreauth_moddata to hold the |
|
list of certauth module handles, and load the modules when the PKINIT |
|
kdcpreauth server plugin is initialized. Change |
|
crypto_retrieve_X509_sans() to return ENOENT when no SAN is found. |
|
|
|
Add test modules in plugins/certauth/test. Create t_certauth.py with |
|
basic certauth tests. Add plugin interface documentation in |
|
doc/plugindev/certauth.rst and doc/admin/krb5_conf.rst. |
|
|
|
[ghudson@mit.edu: simplified code, edited docs] |
|
|
|
ticket: 8561 (new) |
|
(cherry picked from commit b619ce84470519bea65470be3263cd85fba94f57) |
|
--- |
|
doc/admin/conf_files/krb5_conf.rst | 21 ++ |
|
doc/plugindev/certauth.rst | 27 ++ |
|
doc/plugindev/index.rst | 1 + |
|
src/Makefile.in | 1 + |
|
src/configure.in | 1 + |
|
src/include/Makefile.in | 1 + |
|
src/include/k5-int.h | 3 +- |
|
src/include/krb5/certauth_plugin.h | 103 +++++++ |
|
src/lib/krb5/krb/plugin.c | 3 +- |
|
src/plugins/certauth/test/Makefile.in | 20 ++ |
|
src/plugins/certauth/test/certauth_test.exports | 2 + |
|
src/plugins/certauth/test/deps | 14 + |
|
src/plugins/certauth/test/main.c | 209 +++++++++++++ |
|
src/plugins/preauth/pkinit/pkinit_crypto.h | 4 + |
|
src/plugins/preauth/pkinit/pkinit_crypto_openssl.c | 30 ++ |
|
src/plugins/preauth/pkinit/pkinit_srv.c | 335 ++++++++++++++++++--- |
|
src/plugins/preauth/pkinit/pkinit_trace.h | 5 + |
|
src/tests/Makefile.in | 1 + |
|
src/tests/t_certauth.py | 47 +++ |
|
19 files changed, 786 insertions(+), 42 deletions(-) |
|
create mode 100644 doc/plugindev/certauth.rst |
|
create mode 100644 src/include/krb5/certauth_plugin.h |
|
create mode 100644 src/plugins/certauth/test/Makefile.in |
|
create mode 100644 src/plugins/certauth/test/certauth_test.exports |
|
create mode 100644 src/plugins/certauth/test/deps |
|
create mode 100644 src/plugins/certauth/test/main.c |
|
create mode 100644 src/tests/t_certauth.py |
|
|
|
diff --git a/doc/admin/conf_files/krb5_conf.rst b/doc/admin/conf_files/krb5_conf.rst |
|
index 653aad613..c0e4349c0 100644 |
|
--- a/doc/admin/conf_files/krb5_conf.rst |
|
+++ b/doc/admin/conf_files/krb5_conf.rst |
|
@@ -858,6 +858,27 @@ built-in modules exist for this interface: |
|
This module authorizes a principal to a local account if the |
|
principal name maps to the local account name. |
|
|
|
+.. _certauth: |
|
+ |
|
+certauth interface |
|
+################## |
|
+ |
|
+The certauth section (introduced in release 1.16) controls modules for |
|
+the certificate authorization interface, which determines whether a |
|
+certificate is allowed to preauthenticate a user via PKINIT. The |
|
+following built-in modules exist for this interface: |
|
+ |
|
+**pkinit_san** |
|
+ This module authorizes the certificate if it contains a PKINIT |
|
+ Subject Alternative Name for the requested client principal, or a |
|
+ Microsoft UPN SAN matching the principal if **pkinit_allow_upn** |
|
+ is set to true for the realm. |
|
+ |
|
+**pkinit_eku** |
|
+ This module rejects the certificate if it does not contain an |
|
+ Extended Key Usage attribute consistent with the |
|
+ **pkinit_eku_checking** value for the realm. |
|
+ |
|
|
|
PKINIT options |
|
-------------- |
|
diff --git a/doc/plugindev/certauth.rst b/doc/plugindev/certauth.rst |
|
new file mode 100644 |
|
index 000000000..8a7f7c5eb |
|
--- /dev/null |
|
+++ b/doc/plugindev/certauth.rst |
|
@@ -0,0 +1,27 @@ |
|
+.. _certauth_plugin: |
|
+ |
|
+PKINIT certificate authorization interface (certauth) |
|
+===================================================== |
|
+ |
|
+The certauth interface was first introduced in release 1.16. It |
|
+allows customization of the X.509 certificate attribute requirements |
|
+placed on certificates used by PKINIT enabled clients. For a detailed |
|
+description of the certauth interface, see the header file |
|
+``<krb5/certauth_plugin.h>`` |
|
+ |
|
+A certauth module implements the **authorize** method to determine |
|
+whether a client's certificate is authorized to authenticate a client |
|
+principal. **authorize** receives the DER-encoded certificate, the |
|
+requested client principal, and a pointer to the client's |
|
+krb5_db_entry (for modules that link against libkdb5). It returns the |
|
+authorization status and optionally outputs a list of authentication |
|
+indicator strings to be added to the ticket. A module must use its |
|
+own internal or library-provided ASN.1 certificate decoder. |
|
+ |
|
+A module can optionally create and destroy module data with the |
|
+**init** and **fini** methods. Module data objects last for the |
|
+lifetime of the KDC process. |
|
+ |
|
+If a module allocates and returns a list of authentication indicators |
|
+from **authorize**, it must also implement the **free_ind** method |
|
+to free the list. |
|
diff --git a/doc/plugindev/index.rst b/doc/plugindev/index.rst |
|
index 3fb921778..67dbc2790 100644 |
|
--- a/doc/plugindev/index.rst |
|
+++ b/doc/plugindev/index.rst |
|
@@ -31,5 +31,6 @@ Contents |
|
profile.rst |
|
gssapi.rst |
|
internal.rst |
|
+ certauth.rst |
|
|
|
.. TODO: GSSAPI mechanism plugins |
|
diff --git a/src/Makefile.in b/src/Makefile.in |
|
index 2ebf2fb4d..b0249778c 100644 |
|
--- a/src/Makefile.in |
|
+++ b/src/Makefile.in |
|
@@ -17,6 +17,7 @@ SUBDIRS=util include lib \ |
|
plugins/pwqual/test \ |
|
plugins/authdata/greet_server \ |
|
plugins/authdata/greet_client \ |
|
+ plugins/certauth/test \ |
|
plugins/kdb/db2 \ |
|
@ldap_plugin_dir@ \ |
|
plugins/kdb/test \ |
|
diff --git a/src/configure.in b/src/configure.in |
|
index acf3a458b..24f653f0d 100644 |
|
--- a/src/configure.in |
|
+++ b/src/configure.in |
|
@@ -1451,6 +1451,7 @@ dnl ccapi ccapi/lib ccapi/lib/unix ccapi/server ccapi/server/unix ccapi/test |
|
|
|
kdc slave config-files build-tools man doc include |
|
|
|
+ plugins/certauth/test |
|
plugins/hostrealm/test |
|
plugins/localauth/test |
|
plugins/kadm5_hook/test |
|
diff --git a/src/include/Makefile.in b/src/include/Makefile.in |
|
index f5b921833..0239338a1 100644 |
|
--- a/src/include/Makefile.in |
|
+++ b/src/include/Makefile.in |
|
@@ -140,6 +140,7 @@ install-headers-unix install: krb5/krb5.h profile.h |
|
$(INSTALL_DATA) $(srcdir)/krb5.h $(DESTDIR)$(KRB5_INCDIR)$(S)krb5.h |
|
$(INSTALL_DATA) $(srcdir)/kdb.h $(DESTDIR)$(KRB5_INCDIR)$(S)kdb.h |
|
$(INSTALL_DATA) krb5/krb5.h $(DESTDIR)$(KRB5_INCDIR)$(S)krb5$(S)krb5.h |
|
+ $(INSTALL_DATA) $(srcdir)/krb5/certauth_plugin.h $(DESTDIR)$(KRB5_INCDIR)$(S)krb5$(S)certauth_plugin.h |
|
$(INSTALL_DATA) $(srcdir)/krb5/ccselect_plugin.h $(DESTDIR)$(KRB5_INCDIR)$(S)krb5$(S)ccselect_plugin.h |
|
$(INSTALL_DATA) $(srcdir)/krb5/clpreauth_plugin.h $(DESTDIR)$(KRB5_INCDIR)$(S)krb5$(S)clpreauth_plugin.h |
|
$(INSTALL_DATA) $(srcdir)/krb5/hostrealm_plugin.h $(DESTDIR)$(KRB5_INCDIR)$(S)krb5$(S)hostrealm_plugin.h |
|
diff --git a/src/include/k5-int.h b/src/include/k5-int.h |
|
index 173cb0264..cea644d0a 100644 |
|
--- a/src/include/k5-int.h |
|
+++ b/src/include/k5-int.h |
|
@@ -1156,7 +1156,8 @@ struct plugin_interface { |
|
#define PLUGIN_INTERFACE_AUDIT 7 |
|
#define PLUGIN_INTERFACE_TLS 8 |
|
#define PLUGIN_INTERFACE_KDCAUTHDATA 9 |
|
-#define PLUGIN_NUM_INTERFACES 10 |
|
+#define PLUGIN_INTERFACE_CERTAUTH 10 |
|
+#define PLUGIN_NUM_INTERFACES 11 |
|
|
|
/* Retrieve the plugin module of type interface_id and name modname, |
|
* storing the result into module. */ |
|
diff --git a/src/include/krb5/certauth_plugin.h b/src/include/krb5/certauth_plugin.h |
|
new file mode 100644 |
|
index 000000000..f22fc1e84 |
|
--- /dev/null |
|
+++ b/src/include/krb5/certauth_plugin.h |
|
@@ -0,0 +1,103 @@ |
|
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ |
|
+/* include/krb5/certauth_plugin.h - certauth plugin header. */ |
|
+/* |
|
+ * Copyright (C) 2017 by 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: |
|
+ * |
|
+ * * 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. |
|
+ */ |
|
+ |
|
+/* |
|
+ * Certificate authorization plugin interface. The PKINIT server module uses |
|
+ * this interface to check client certificate attributes after the certificate |
|
+ * signature has been verified. |
|
+ */ |
|
+#ifndef KRB5_CERTAUTH_PLUGIN_H |
|
+#define KRB5_CERTAUTH_PLUGIN_H |
|
+ |
|
+#include <krb5/krb5.h> |
|
+#include <krb5/plugin.h> |
|
+ |
|
+/* Abstract module data type. */ |
|
+typedef struct krb5_certauth_moddata_st *krb5_certauth_moddata; |
|
+ |
|
+typedef struct _krb5_db_entry_new krb5_db_entry; |
|
+ |
|
+/* |
|
+ * Optional: Initialize module data. |
|
+ */ |
|
+typedef krb5_error_code |
|
+(*krb5_certauth_init_fn)(krb5_context context, |
|
+ krb5_certauth_moddata *moddata_out); |
|
+ |
|
+/* |
|
+ * Optional: Clean up the module data. |
|
+ */ |
|
+typedef void |
|
+(*krb5_certauth_fini_fn)(krb5_context context, krb5_certauth_moddata moddata); |
|
+ |
|
+/* |
|
+ * Mandatory: |
|
+ * Return 0 if the DER-encoded cert is authorized for PKINIT authentication by |
|
+ * princ; otherwise return one of the following error codes: |
|
+ * - KRB5KDC_ERR_CLIENT_NAME_MISMATCH - incorrect SAN value |
|
+ * - KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE - incorrect EKU |
|
+ * - KRB5KDC_ERR_CERTIFICATE_MISMATCH - other extension error |
|
+ * - KRB5_PLUGIN_NO_HANDLE - the module has no opinion about cert |
|
+ * |
|
+ * - opts is used by built-in modules to receive internal data, and must be |
|
+ * ignored by other modules. |
|
+ * - db_entry receives the client principal database entry, and can be ignored |
|
+ * by modules that do not link with libkdb5. |
|
+ * - *authinds_out optionally returns a null-terminated list of authentication |
|
+ * indicator strings upon KRB5_PLUGIN_NO_HANDLE or accepted authorization. |
|
+ */ |
|
+typedef krb5_error_code |
|
+(*krb5_certauth_authorize_fn)(krb5_context context, |
|
+ krb5_certauth_moddata moddata, |
|
+ const uint8_t *cert, size_t cert_len, |
|
+ krb5_const_principal princ, const void *opts, |
|
+ const krb5_db_entry *db_entry, |
|
+ char ***authinds_out); |
|
+ |
|
+/* |
|
+ * Free indicators allocated by a module. Mandatory if authorize returns |
|
+ * authentication indicators. |
|
+ */ |
|
+typedef void |
|
+(*krb5_certauth_free_indicator_fn)(krb5_context context, |
|
+ krb5_certauth_moddata moddata, |
|
+ char **authinds); |
|
+ |
|
+typedef struct krb5_certauth_vtable_st { |
|
+ char *name; |
|
+ krb5_certauth_init_fn init; |
|
+ krb5_certauth_fini_fn fini; |
|
+ krb5_certauth_authorize_fn authorize; |
|
+ krb5_certauth_free_indicator_fn free_ind; |
|
+} *krb5_certauth_vtable; |
|
+ |
|
+#endif /* KRB5_CERTAUTH_PLUGIN_H */ |
|
diff --git a/src/lib/krb5/krb/plugin.c b/src/lib/krb5/krb/plugin.c |
|
index 7d64b7c7e..17dd6bd30 100644 |
|
--- a/src/lib/krb5/krb/plugin.c |
|
+++ b/src/lib/krb5/krb/plugin.c |
|
@@ -57,7 +57,8 @@ const char *interface_names[] = { |
|
"hostrealm", |
|
"audit", |
|
"tls", |
|
- "kdcauthdata" |
|
+ "kdcauthdata", |
|
+ "certauth" |
|
}; |
|
|
|
/* Return the context's interface structure for id, or NULL if invalid. */ |
|
diff --git a/src/plugins/certauth/test/Makefile.in b/src/plugins/certauth/test/Makefile.in |
|
new file mode 100644 |
|
index 000000000..d3524084c |
|
--- /dev/null |
|
+++ b/src/plugins/certauth/test/Makefile.in |
|
@@ -0,0 +1,20 @@ |
|
+mydir=plugins$(S)certauth$(S)test |
|
+BUILDTOP=$(REL)..$(S)..$(S).. |
|
+ |
|
+LIBBASE=certauth_test |
|
+LIBMAJOR=0 |
|
+LIBMINOR=0 |
|
+RELDIR=../plugins/certauth/test |
|
+SHLIB_EXPDEPS=$(KRB5_BASE_DEPLIBS) |
|
+SHLIB_EXPLIBS=$(KRB5_BASE_LIBS) |
|
+ |
|
+STLIBOBJS=main.o |
|
+ |
|
+SRCS=$(srcdir)/main.c |
|
+ |
|
+all-unix: all-libs |
|
+install-unix: |
|
+clean-unix:: clean-libs clean-libobjs |
|
+ |
|
+@libnover_frag@ |
|
+@libobj_frag@ |
|
diff --git a/src/plugins/certauth/test/certauth_test.exports b/src/plugins/certauth/test/certauth_test.exports |
|
new file mode 100644 |
|
index 000000000..1c8cd24e2 |
|
--- /dev/null |
|
+++ b/src/plugins/certauth/test/certauth_test.exports |
|
@@ -0,0 +1,2 @@ |
|
+certauth_test1_initvt |
|
+certauth_test2_initvt |
|
diff --git a/src/plugins/certauth/test/deps b/src/plugins/certauth/test/deps |
|
new file mode 100644 |
|
index 000000000..2974b3b57 |
|
--- /dev/null |
|
+++ b/src/plugins/certauth/test/deps |
|
@@ -0,0 +1,14 @@ |
|
+# |
|
+# Generated makefile dependencies follow. |
|
+# |
|
+main.so main.po $(OUTPRE)main.$(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/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \ |
|
+ $(top_srcdir)/include/krb5/certauth_plugin.h $(top_srcdir)/include/krb5/plugin.h \ |
|
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ |
|
+ main.c |
|
diff --git a/src/plugins/certauth/test/main.c b/src/plugins/certauth/test/main.c |
|
new file mode 100644 |
|
index 000000000..7ef7377fb |
|
--- /dev/null |
|
+++ b/src/plugins/certauth/test/main.c |
|
@@ -0,0 +1,209 @@ |
|
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ |
|
+/* plugins/certauth/main.c - certauth plugin test modules. */ |
|
+/* |
|
+ * Copyright (C) 2017 by 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: |
|
+ * |
|
+ * * 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. |
|
+ */ |
|
+ |
|
+#include <k5-int.h> |
|
+#include "krb5/certauth_plugin.h" |
|
+ |
|
+struct krb5_certauth_moddata_st { |
|
+ int initialized; |
|
+}; |
|
+ |
|
+/* Test module 1 returns OK with an indicator. */ |
|
+static krb5_error_code |
|
+test1_authorize(krb5_context context, krb5_certauth_moddata moddata, |
|
+ const uint8_t *cert, size_t cert_len, |
|
+ krb5_const_principal princ, const void *opts, |
|
+ const krb5_db_entry *db_entry, char ***authinds_out) |
|
+{ |
|
+ char **ais = NULL; |
|
+ |
|
+ ais = calloc(2, sizeof(*ais)); |
|
+ assert(ais != NULL); |
|
+ ais[0] = strdup("test1"); |
|
+ assert(ais[0] != NULL); |
|
+ *authinds_out = ais; |
|
+ return KRB5_PLUGIN_NO_HANDLE; |
|
+} |
|
+ |
|
+static void |
|
+test_free_ind(krb5_context context, krb5_certauth_moddata moddata, |
|
+ char **authinds) |
|
+{ |
|
+ size_t i; |
|
+ |
|
+ if (authinds == NULL) |
|
+ return; |
|
+ for (i = 0; authinds[i] != NULL; i++) |
|
+ free(authinds[i]); |
|
+ free(authinds); |
|
+} |
|
+ |
|
+/* A basic moddata test. */ |
|
+static krb5_error_code |
|
+test2_init(krb5_context context, krb5_certauth_moddata *moddata_out) |
|
+{ |
|
+ krb5_certauth_moddata mod; |
|
+ |
|
+ mod = calloc(1, sizeof(*mod)); |
|
+ assert(mod != NULL); |
|
+ mod->initialized = 1; |
|
+ *moddata_out = mod; |
|
+ return 0; |
|
+} |
|
+ |
|
+static void |
|
+test2_fini(krb5_context context, krb5_certauth_moddata moddata) |
|
+{ |
|
+ free(moddata); |
|
+} |
|
+ |
|
+/* Return true if cert appears to contain the CN name, based on a search of the |
|
+ * DER encoding. */ |
|
+static krb5_boolean |
|
+has_cn(krb5_context context, const uint8_t *cert, size_t cert_len, |
|
+ const char *name) |
|
+{ |
|
+ krb5_boolean match = FALSE; |
|
+ uint8_t name_len, cntag[5] = "\x06\x03\x55\x04\x03"; |
|
+ const uint8_t *c; |
|
+ struct k5buf buf; |
|
+ size_t c_left; |
|
+ |
|
+ /* Construct a DER search string of the CN AttributeType encoding followed |
|
+ * by a UTF8String encoding containing name as the AttributeValue. */ |
|
+ k5_buf_init_dynamic(&buf); |
|
+ k5_buf_add_len(&buf, cntag, sizeof(cntag)); |
|
+ k5_buf_add(&buf, "\x0C"); |
|
+ assert(strlen(name) < 128); |
|
+ name_len = strlen(name); |
|
+ k5_buf_add_len(&buf, &name_len, 1); |
|
+ k5_buf_add_len(&buf, name, name_len); |
|
+ assert(k5_buf_status(&buf) == 0); |
|
+ |
|
+ /* Check for the CN needle in the certificate haystack. */ |
|
+ c_left = cert_len; |
|
+ c = memchr(cert, *cntag, c_left); |
|
+ while (c != NULL) { |
|
+ c_left = cert_len - (c - cert); |
|
+ if (buf.len > c_left) |
|
+ break; |
|
+ if (memcmp(c, buf.data, buf.len) == 0) { |
|
+ match = TRUE; |
|
+ break; |
|
+ } |
|
+ assert(c_left >= 1); |
|
+ c = memchr(c + 1, *cntag, c_left - 1); |
|
+ } |
|
+ |
|
+ k5_buf_free(&buf); |
|
+ return match; |
|
+} |
|
+ |
|
+/* |
|
+ * Test module 2 returns OK if princ matches the CN part of the subject name, |
|
+ * and returns indicators of the module name and princ. |
|
+ */ |
|
+static krb5_error_code |
|
+test2_authorize(krb5_context context, krb5_certauth_moddata moddata, |
|
+ const uint8_t *cert, size_t cert_len, |
|
+ krb5_const_principal princ, const void *opts, |
|
+ const krb5_db_entry *db_entry, char ***authinds_out) |
|
+{ |
|
+ krb5_error_code ret; |
|
+ char *name = NULL, **ais = NULL; |
|
+ |
|
+ *authinds_out = NULL; |
|
+ |
|
+ assert(moddata != NULL && moddata->initialized); |
|
+ |
|
+ ret = krb5_unparse_name_flags(context, princ, |
|
+ KRB5_PRINCIPAL_UNPARSE_NO_REALM, &name); |
|
+ if (ret) |
|
+ goto cleanup; |
|
+ |
|
+ if (!has_cn(context, cert, cert_len, name)) { |
|
+ ret = KRB5KDC_ERR_CERTIFICATE_MISMATCH; |
|
+ goto cleanup; |
|
+ } |
|
+ |
|
+ /* Create an indicator list with the module name and CN. */ |
|
+ ais = calloc(3, sizeof(*ais)); |
|
+ assert(ais != NULL); |
|
+ ais[0] = strdup("test2"); |
|
+ ais[1] = strdup(name); |
|
+ assert(ais[0] != NULL && ais[1] != NULL); |
|
+ *authinds_out = ais; |
|
+ |
|
+ ais = NULL; |
|
+ |
|
+cleanup: |
|
+ krb5_free_unparsed_name(context, name); |
|
+ return ret; |
|
+} |
|
+ |
|
+krb5_error_code |
|
+certauth_test1_initvt(krb5_context context, int maj_ver, int min_ver, |
|
+ krb5_plugin_vtable vtable); |
|
+krb5_error_code |
|
+certauth_test1_initvt(krb5_context context, int maj_ver, int min_ver, |
|
+ krb5_plugin_vtable vtable) |
|
+{ |
|
+ krb5_certauth_vtable vt; |
|
+ |
|
+ if (maj_ver != 1) |
|
+ return KRB5_PLUGIN_VER_NOTSUPP; |
|
+ vt = (krb5_certauth_vtable)vtable; |
|
+ vt->name = "test1"; |
|
+ vt->authorize = test1_authorize; |
|
+ vt->free_ind = test_free_ind; |
|
+ return 0; |
|
+} |
|
+ |
|
+krb5_error_code |
|
+certauth_test2_initvt(krb5_context context, int maj_ver, int min_ver, |
|
+ krb5_plugin_vtable vtable); |
|
+krb5_error_code |
|
+certauth_test2_initvt(krb5_context context, int maj_ver, int min_ver, |
|
+ krb5_plugin_vtable vtable) |
|
+{ |
|
+ krb5_certauth_vtable vt; |
|
+ |
|
+ if (maj_ver != 1) |
|
+ return KRB5_PLUGIN_VER_NOTSUPP; |
|
+ vt = (krb5_certauth_vtable)vtable; |
|
+ vt->name = "test2"; |
|
+ vt->authorize = test2_authorize; |
|
+ vt->init = test2_init; |
|
+ vt->fini = test2_fini; |
|
+ vt->free_ind = test_free_ind; |
|
+ return 0; |
|
+} |
|
diff --git a/src/plugins/preauth/pkinit/pkinit_crypto.h b/src/plugins/preauth/pkinit/pkinit_crypto.h |
|
index b483affed..49b96b8ee 100644 |
|
--- a/src/plugins/preauth/pkinit/pkinit_crypto.h |
|
+++ b/src/plugins/preauth/pkinit/pkinit_crypto.h |
|
@@ -664,4 +664,8 @@ extern const size_t krb5_pkinit_sha512_oid_len; |
|
*/ |
|
extern krb5_data const * const supported_kdf_alg_ids[]; |
|
|
|
+krb5_error_code |
|
+crypto_encode_der_cert(krb5_context context, pkinit_req_crypto_context reqctx, |
|
+ uint8_t **der_out, size_t *der_len); |
|
+ |
|
#endif /* _PKINIT_CRYPTO_H */ |
|
diff --git a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c |
|
index 8def8c542..a5b010b26 100644 |
|
--- a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c |
|
+++ b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c |
|
@@ -2137,6 +2137,7 @@ crypto_retrieve_X509_sans(krb5_context context, |
|
|
|
if (!(ext = X509_get_ext(cert, l)) || !(ialt = X509V3_EXT_d2i(ext))) { |
|
pkiDebug("%s: found no subject alt name extensions\n", __FUNCTION__); |
|
+ retval = ENOENT; |
|
goto cleanup; |
|
} |
|
num_sans = sk_GENERAL_NAME_num(ialt); |
|
@@ -6176,3 +6177,32 @@ crypto_get_deferred_ids(krb5_context context, |
|
ret = (const pkinit_deferred_id *)deferred; |
|
return ret; |
|
} |
|
+ |
|
+/* Return the received certificate as DER-encoded data. */ |
|
+krb5_error_code |
|
+crypto_encode_der_cert(krb5_context context, pkinit_req_crypto_context reqctx, |
|
+ uint8_t **der_out, size_t *der_len) |
|
+{ |
|
+ int len; |
|
+ unsigned char *der, *p; |
|
+ |
|
+ *der_out = NULL; |
|
+ *der_len = 0; |
|
+ |
|
+ if (reqctx->received_cert == NULL) |
|
+ return EINVAL; |
|
+ p = NULL; |
|
+ len = i2d_X509(reqctx->received_cert, NULL); |
|
+ if (len <= 0) |
|
+ return EINVAL; |
|
+ p = der = malloc(len); |
|
+ if (p == NULL) |
|
+ return ENOMEM; |
|
+ if (i2d_X509(reqctx->received_cert, &p) <= 0) { |
|
+ free(p); |
|
+ return EINVAL; |
|
+ } |
|
+ *der_out = der; |
|
+ *der_len = len; |
|
+ return 0; |
|
+} |
|
diff --git a/src/plugins/preauth/pkinit/pkinit_srv.c b/src/plugins/preauth/pkinit/pkinit_srv.c |
|
index b5638a367..731d14eb8 100644 |
|
--- a/src/plugins/preauth/pkinit/pkinit_srv.c |
|
+++ b/src/plugins/preauth/pkinit/pkinit_srv.c |
|
@@ -31,6 +31,25 @@ |
|
|
|
#include <k5-int.h> |
|
#include "pkinit.h" |
|
+#include "krb5/certauth_plugin.h" |
|
+ |
|
+/* Aliases used by the built-in certauth modules */ |
|
+struct certauth_req_opts { |
|
+ krb5_kdcpreauth_callbacks cb; |
|
+ krb5_kdcpreauth_rock rock; |
|
+ pkinit_kdc_context plgctx; |
|
+ pkinit_kdc_req_context reqctx; |
|
+}; |
|
+ |
|
+typedef struct certauth_module_handle_st { |
|
+ struct krb5_certauth_vtable_st vt; |
|
+ krb5_certauth_moddata moddata; |
|
+} *certauth_handle; |
|
+ |
|
+struct krb5_kdcpreauth_moddata_st { |
|
+ pkinit_kdc_context *realm_contexts; |
|
+ certauth_handle *certauth_modules; |
|
+}; |
|
|
|
static krb5_error_code |
|
pkinit_init_kdc_req_context(krb5_context, pkinit_kdc_req_context *blob); |
|
@@ -51,6 +70,34 @@ pkinit_find_realm_context(krb5_context context, |
|
krb5_kdcpreauth_moddata moddata, |
|
krb5_principal princ); |
|
|
|
+static void |
|
+free_realm_contexts(krb5_context context, pkinit_kdc_context *realm_contexts) |
|
+{ |
|
+ int i; |
|
+ |
|
+ if (realm_contexts == NULL) |
|
+ return; |
|
+ for (i = 0; realm_contexts[i] != NULL; i++) |
|
+ pkinit_server_plugin_fini_realm(context, realm_contexts[i]); |
|
+ pkiDebug("%s: freeing context at %p\n", __FUNCTION__, realm_contexts); |
|
+ free(realm_contexts); |
|
+} |
|
+ |
|
+static void |
|
+free_certauth_handles(krb5_context context, certauth_handle *list) |
|
+{ |
|
+ int i; |
|
+ |
|
+ if (list == NULL) |
|
+ return; |
|
+ for (i = 0; list[i] != NULL; i++) { |
|
+ if (list[i]->vt.fini != NULL) |
|
+ list[i]->vt.fini(context, list[i]->moddata); |
|
+ free(list[i]); |
|
+ } |
|
+ free(list); |
|
+} |
|
+ |
|
static krb5_error_code |
|
pkinit_create_edata(krb5_context context, |
|
pkinit_plg_crypto_context plg_cryptoctx, |
|
@@ -123,7 +170,7 @@ verify_client_san(krb5_context context, |
|
pkinit_kdc_req_context reqctx, |
|
krb5_kdcpreauth_callbacks cb, |
|
krb5_kdcpreauth_rock rock, |
|
- krb5_principal client, |
|
+ krb5_const_principal client, |
|
int *valid_san) |
|
{ |
|
krb5_error_code retval; |
|
@@ -134,12 +181,15 @@ verify_client_san(krb5_context context, |
|
char *client_string = NULL, *san_string; |
|
#endif |
|
|
|
+ *valid_san = 0; |
|
retval = crypto_retrieve_cert_sans(context, plgctx->cryptoctx, |
|
reqctx->cryptoctx, plgctx->idctx, |
|
&princs, |
|
plgctx->opts->allow_upn ? &upns : NULL, |
|
NULL); |
|
- if (retval) { |
|
+ if (retval == ENOENT) { |
|
+ goto out; |
|
+ } else if (retval) { |
|
pkiDebug("%s: error from retrieve_certificate_sans()\n", __FUNCTION__); |
|
retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH; |
|
goto out; |
|
@@ -273,6 +323,73 @@ out: |
|
return retval; |
|
} |
|
|
|
+ |
|
+/* Run the received, verified certificate through certauth modules, to verify |
|
+ * that it is authorized to authenticate as client. */ |
|
+static krb5_error_code |
|
+authorize_cert(krb5_context context, certauth_handle *certauth_modules, |
|
+ pkinit_kdc_context plgctx, pkinit_kdc_req_context reqctx, |
|
+ krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock, |
|
+ krb5_principal client) |
|
+{ |
|
+ krb5_error_code ret; |
|
+ certauth_handle h; |
|
+ struct certauth_req_opts opts; |
|
+ krb5_boolean accepted = FALSE; |
|
+ uint8_t *cert; |
|
+ size_t i, cert_len; |
|
+ void *db_ent = NULL; |
|
+ char **ais = NULL, **ai = NULL; |
|
+ |
|
+ /* Re-encode the received certificate into DER, which is extra work, but |
|
+ * avoids creating an X.509 library dependency in the interface. */ |
|
+ ret = crypto_encode_der_cert(context, reqctx->cryptoctx, &cert, &cert_len); |
|
+ if (ret) |
|
+ goto cleanup; |
|
+ |
|
+ /* Set options for the builtin module. */ |
|
+ opts.plgctx = plgctx; |
|
+ opts.reqctx = reqctx; |
|
+ opts.cb = cb; |
|
+ opts.rock = rock; |
|
+ |
|
+ db_ent = cb->client_entry(context, rock); |
|
+ |
|
+ /* |
|
+ * Check the certificate against each certauth module. For the certificate |
|
+ * to be authorized at least one module must return 0, and no module can an |
|
+ * error code other than KRB5_PLUGIN_NO_HANDLE (pass). Add indicators from |
|
+ * modules that return 0 or pass. |
|
+ */ |
|
+ ret = KRB5_PLUGIN_NO_HANDLE; |
|
+ for (i = 0; certauth_modules != NULL && certauth_modules[i] != NULL; i++) { |
|
+ h = certauth_modules[i]; |
|
+ ret = h->vt.authorize(context, h->moddata, cert, cert_len, client, |
|
+ &opts, db_ent, &ais); |
|
+ if (ret == 0) |
|
+ accepted = TRUE; |
|
+ else if (ret != KRB5_PLUGIN_NO_HANDLE) |
|
+ goto cleanup; |
|
+ |
|
+ if (ais != NULL) { |
|
+ /* Assert authentication indicators from the module. */ |
|
+ for (ai = ais; *ai != NULL; ai++) { |
|
+ ret = cb->add_auth_indicator(context, rock, *ai); |
|
+ if (ret) |
|
+ goto cleanup; |
|
+ } |
|
+ h->vt.free_ind(context, h->moddata, ais); |
|
+ ais = NULL; |
|
+ } |
|
+ } |
|
+ |
|
+ ret = accepted ? 0 : KRB5KDC_ERR_CLIENT_NAME_MISMATCH; |
|
+ |
|
+cleanup: |
|
+ free(cert); |
|
+ return ret; |
|
+} |
|
+ |
|
static void |
|
pkinit_server_verify_padata(krb5_context context, |
|
krb5_data *req_pkt, |
|
@@ -295,7 +412,6 @@ pkinit_server_verify_padata(krb5_context context, |
|
pkinit_kdc_req_context reqctx = NULL; |
|
krb5_checksum cksum = {0, 0, 0, NULL}; |
|
krb5_data *der_req = NULL; |
|
- int valid_eku = 0, valid_san = 0; |
|
krb5_data k5data; |
|
int is_signed = 1; |
|
krb5_pa_data **e_data = NULL; |
|
@@ -388,27 +504,11 @@ pkinit_server_verify_padata(krb5_context context, |
|
goto cleanup; |
|
} |
|
if (is_signed) { |
|
- |
|
- retval = verify_client_san(context, plgctx, reqctx, cb, rock, |
|
- request->client, &valid_san); |
|
- if (retval) |
|
- goto cleanup; |
|
- if (!valid_san) { |
|
- pkiDebug("%s: did not find an acceptable SAN in user " |
|
- "certificate\n", __FUNCTION__); |
|
- retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH; |
|
- goto cleanup; |
|
- } |
|
- retval = verify_client_eku(context, plgctx, reqctx, &valid_eku); |
|
+ retval = authorize_cert(context, moddata->certauth_modules, plgctx, |
|
+ reqctx, cb, rock, request->client); |
|
if (retval) |
|
goto cleanup; |
|
|
|
- if (!valid_eku) { |
|
- pkiDebug("%s: did not find an acceptable EKU in user " |
|
- "certificate\n", __FUNCTION__); |
|
- retval = KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE; |
|
- goto cleanup; |
|
- } |
|
} else { /* !is_signed */ |
|
if (!krb5_principal_compare(context, request->client, |
|
krb5_anonymous_principal())) { |
|
@@ -1245,11 +1345,15 @@ pkinit_find_realm_context(krb5_context context, |
|
krb5_principal princ) |
|
{ |
|
int i; |
|
- pkinit_kdc_context *realm_contexts = (pkinit_kdc_context *)moddata; |
|
+ pkinit_kdc_context *realm_contexts; |
|
|
|
if (moddata == NULL) |
|
return NULL; |
|
|
|
+ realm_contexts = moddata->realm_contexts; |
|
+ if (realm_contexts == NULL) |
|
+ return NULL; |
|
+ |
|
for (i = 0; realm_contexts[i] != NULL; i++) { |
|
pkinit_kdc_context p = realm_contexts[i]; |
|
|
|
@@ -1331,6 +1435,155 @@ errout: |
|
return retval; |
|
} |
|
|
|
+static krb5_error_code |
|
+pkinit_san_authorize(krb5_context context, krb5_certauth_moddata moddata, |
|
+ const uint8_t *cert, size_t cert_len, |
|
+ krb5_const_principal princ, const void *opts, |
|
+ const krb5_db_entry *db_entry, char ***authinds_out) |
|
+{ |
|
+ krb5_error_code ret; |
|
+ int valid_san; |
|
+ const struct certauth_req_opts *req_opts = opts; |
|
+ |
|
+ *authinds_out = NULL; |
|
+ |
|
+ ret = verify_client_san(context, req_opts->plgctx, req_opts->reqctx, |
|
+ req_opts->cb, req_opts->rock, princ, &valid_san); |
|
+ if (ret == ENOENT) |
|
+ return KRB5_PLUGIN_NO_HANDLE; |
|
+ else if (ret) |
|
+ return ret; |
|
+ |
|
+ if (!valid_san) { |
|
+ pkiDebug("%s: did not find an acceptable SAN in user certificate\n", |
|
+ __FUNCTION__); |
|
+ return KRB5KDC_ERR_CLIENT_NAME_MISMATCH; |
|
+ } |
|
+ |
|
+ return 0; |
|
+} |
|
+ |
|
+static krb5_error_code |
|
+pkinit_eku_authorize(krb5_context context, krb5_certauth_moddata moddata, |
|
+ const uint8_t *cert, size_t cert_len, |
|
+ krb5_const_principal princ, const void *opts, |
|
+ const krb5_db_entry *db_entry, char ***authinds_out) |
|
+{ |
|
+ krb5_error_code ret; |
|
+ int valid_eku; |
|
+ const struct certauth_req_opts *req_opts = opts; |
|
+ |
|
+ *authinds_out = NULL; |
|
+ |
|
+ /* Verify the client EKU. */ |
|
+ ret = verify_client_eku(context, req_opts->plgctx, req_opts->reqctx, |
|
+ &valid_eku); |
|
+ if (ret) |
|
+ return ret; |
|
+ |
|
+ if (!valid_eku) { |
|
+ pkiDebug("%s: did not find an acceptable EKU in user certificate\n", |
|
+ __FUNCTION__); |
|
+ return KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE; |
|
+ } |
|
+ |
|
+ return 0; |
|
+} |
|
+ |
|
+static krb5_error_code |
|
+certauth_pkinit_san_initvt(krb5_context context, int maj_ver, int min_ver, |
|
+ krb5_plugin_vtable vtable) |
|
+{ |
|
+ krb5_certauth_vtable vt; |
|
+ |
|
+ if (maj_ver != 1) |
|
+ return KRB5_PLUGIN_VER_NOTSUPP; |
|
+ vt = (krb5_certauth_vtable)vtable; |
|
+ vt->name = "pkinit_san"; |
|
+ vt->authorize = pkinit_san_authorize; |
|
+ return 0; |
|
+} |
|
+ |
|
+static krb5_error_code |
|
+certauth_pkinit_eku_initvt(krb5_context context, int maj_ver, int min_ver, |
|
+ krb5_plugin_vtable vtable) |
|
+{ |
|
+ krb5_certauth_vtable vt; |
|
+ |
|
+ if (maj_ver != 1) |
|
+ return KRB5_PLUGIN_VER_NOTSUPP; |
|
+ vt = (krb5_certauth_vtable)vtable; |
|
+ vt->name = "pkinit_eku"; |
|
+ vt->authorize = pkinit_eku_authorize; |
|
+ return 0; |
|
+} |
|
+ |
|
+static krb5_error_code |
|
+load_certauth_plugins(krb5_context context, certauth_handle **handle_out) |
|
+{ |
|
+ krb5_error_code ret; |
|
+ krb5_plugin_initvt_fn *modules = NULL, *mod; |
|
+ certauth_handle *list = NULL, h; |
|
+ size_t count; |
|
+ |
|
+ /* Register the builtin modules. */ |
|
+ ret = k5_plugin_register(context, PLUGIN_INTERFACE_CERTAUTH, |
|
+ "pkinit_san", certauth_pkinit_san_initvt); |
|
+ if (ret) |
|
+ goto cleanup; |
|
+ |
|
+ ret = k5_plugin_register(context, PLUGIN_INTERFACE_CERTAUTH, |
|
+ "pkinit_eku", certauth_pkinit_eku_initvt); |
|
+ if (ret) |
|
+ goto cleanup; |
|
+ |
|
+ ret = k5_plugin_load_all(context, PLUGIN_INTERFACE_CERTAUTH, &modules); |
|
+ if (ret) |
|
+ goto cleanup; |
|
+ |
|
+ /* Allocate handle list. */ |
|
+ for (count = 0; modules[count]; count++); |
|
+ list = k5calloc(count + 1, sizeof(*list), &ret); |
|
+ if (list == NULL) |
|
+ goto cleanup; |
|
+ |
|
+ /* Initialize each module, ignoring ones that fail. */ |
|
+ count = 0; |
|
+ for (mod = modules; *mod != NULL; mod++) { |
|
+ h = k5calloc(1, sizeof(*h), &ret); |
|
+ if (h == NULL) |
|
+ goto cleanup; |
|
+ |
|
+ ret = (*mod)(context, 1, 1, (krb5_plugin_vtable)&h->vt); |
|
+ if (ret) { |
|
+ TRACE_CERTAUTH_VTINIT_FAIL(context, ret); |
|
+ free(h); |
|
+ continue; |
|
+ } |
|
+ h->moddata = NULL; |
|
+ if (h->vt.init != NULL) { |
|
+ ret = h->vt.init(context, &h->moddata); |
|
+ if (ret) { |
|
+ TRACE_CERTAUTH_INIT_FAIL(context, h->vt.name, ret); |
|
+ free(h); |
|
+ continue; |
|
+ } |
|
+ } |
|
+ list[count++] = h; |
|
+ list[count] = NULL; |
|
+ } |
|
+ list[count] = NULL; |
|
+ |
|
+ ret = 0; |
|
+ *handle_out = list; |
|
+ list = NULL; |
|
+ |
|
+cleanup: |
|
+ k5_plugin_free_modules(context, modules); |
|
+ free_certauth_handles(context, list); |
|
+ return ret; |
|
+} |
|
+ |
|
static int |
|
pkinit_server_plugin_init(krb5_context context, |
|
krb5_kdcpreauth_moddata *moddata_out, |
|
@@ -1338,6 +1591,8 @@ pkinit_server_plugin_init(krb5_context context, |
|
{ |
|
krb5_error_code retval = ENOMEM; |
|
pkinit_kdc_context plgctx, *realm_contexts = NULL; |
|
+ certauth_handle *certauth_modules = NULL; |
|
+ krb5_kdcpreauth_moddata moddata; |
|
size_t i, j; |
|
size_t numrealms; |
|
|
|
@@ -1368,16 +1623,22 @@ pkinit_server_plugin_init(krb5_context context, |
|
goto errout; |
|
} |
|
|
|
- *moddata_out = (krb5_kdcpreauth_moddata)realm_contexts; |
|
- retval = 0; |
|
- pkiDebug("%s: returning context at %p\n", __FUNCTION__, realm_contexts); |
|
+ retval = load_certauth_plugins(context, &certauth_modules); |
|
+ if (retval) |
|
+ goto errout; |
|
+ |
|
+ moddata = k5calloc(1, sizeof(*moddata), &retval); |
|
+ if (moddata == NULL) |
|
+ goto errout; |
|
+ moddata->realm_contexts = realm_contexts; |
|
+ moddata->certauth_modules = certauth_modules; |
|
+ *moddata_out = moddata; |
|
+ pkiDebug("%s: returning context at %p\n", __FUNCTION__, moddata); |
|
+ return 0; |
|
|
|
errout: |
|
- if (retval) { |
|
- pkinit_server_plugin_fini(context, |
|
- (krb5_kdcpreauth_moddata)realm_contexts); |
|
- } |
|
- |
|
+ free_realm_contexts(context, realm_contexts); |
|
+ free_certauth_handles(context, certauth_modules); |
|
return retval; |
|
} |
|
|
|
@@ -1405,17 +1666,11 @@ static void |
|
pkinit_server_plugin_fini(krb5_context context, |
|
krb5_kdcpreauth_moddata moddata) |
|
{ |
|
- pkinit_kdc_context *realm_contexts = (pkinit_kdc_context *)moddata; |
|
- int i; |
|
- |
|
- if (realm_contexts == NULL) |
|
+ if (moddata == NULL) |
|
return; |
|
- |
|
- for (i = 0; realm_contexts[i] != NULL; i++) { |
|
- pkinit_server_plugin_fini_realm(context, realm_contexts[i]); |
|
- } |
|
- pkiDebug("%s: freeing context at %p\n", __FUNCTION__, realm_contexts); |
|
- free(realm_contexts); |
|
+ free_realm_contexts(context, moddata->realm_contexts); |
|
+ free_certauth_handles(context, moddata->certauth_modules); |
|
+ free(moddata); |
|
} |
|
|
|
static krb5_error_code |
|
diff --git a/src/plugins/preauth/pkinit/pkinit_trace.h b/src/plugins/preauth/pkinit/pkinit_trace.h |
|
index b3f5cbb20..458d0961e 100644 |
|
--- a/src/plugins/preauth/pkinit/pkinit_trace.h |
|
+++ b/src/plugins/preauth/pkinit/pkinit_trace.h |
|
@@ -91,4 +91,9 @@ |
|
#define TRACE_PKINIT_OPENSSL_ERROR(c, msg) \ |
|
TRACE(c, "PKINIT OpenSSL error: {str}", msg) |
|
|
|
+#define TRACE_CERTAUTH_VTINIT_FAIL(c, ret) \ |
|
+ TRACE(c, "certauth module failed to init vtable: {kerr}", ret) |
|
+#define TRACE_CERTAUTH_INIT_FAIL(c, name, ret) \ |
|
+ TRACE(c, "certauth module {str} failed to init: {kerr}", name, ret) |
|
+ |
|
#endif /* PKINIT_TRACE_H */ |
|
diff --git a/src/tests/Makefile.in b/src/tests/Makefile.in |
|
index b55469146..0e93d6b59 100644 |
|
--- a/src/tests/Makefile.in |
|
+++ b/src/tests/Makefile.in |
|
@@ -167,6 +167,7 @@ check-pytests: localauth plugorder rdreq responder s2p s4u2proxy unlockiter |
|
$(RUNPYTEST) $(srcdir)/t_preauth.py $(PYTESTFLAGS) |
|
$(RUNPYTEST) $(srcdir)/t_princflags.py $(PYTESTFLAGS) |
|
$(RUNPYTEST) $(srcdir)/t_tabdump.py $(PYTESTFLAGS) |
|
+ $(RUNPYTEST) $(srcdir)/t_certauth.py $(PYTESTFLAGS) |
|
|
|
clean: |
|
$(RM) adata etinfo forward gcred hist hooks hrealm icred kdbtest |
|
diff --git a/src/tests/t_certauth.py b/src/tests/t_certauth.py |
|
new file mode 100644 |
|
index 000000000..e64a57b0d |
|
--- /dev/null |
|
+++ b/src/tests/t_certauth.py |
|
@@ -0,0 +1,47 @@ |
|
+#!/usr/bin/python |
|
+from k5test import * |
|
+ |
|
+# Skip this test if pkinit wasn't built. |
|
+if not os.path.exists(os.path.join(plugins, 'preauth', 'pkinit.so')): |
|
+ skip_rest('certauth tests', 'PKINIT module not built') |
|
+ |
|
+certs = os.path.join(srctop, 'tests', 'dejagnu', 'pkinit-certs') |
|
+ca_pem = os.path.join(certs, 'ca.pem') |
|
+kdc_pem = os.path.join(certs, 'kdc.pem') |
|
+privkey_pem = os.path.join(certs, 'privkey.pem') |
|
+user_pem = os.path.join(certs, 'user.pem') |
|
+ |
|
+modpath = os.path.join(buildtop, 'plugins', 'certauth', 'test', |
|
+ 'certauth_test.so') |
|
+pkinit_krb5_conf = {'realms': {'$realm': { |
|
+ 'pkinit_anchors': 'FILE:%s' % ca_pem}}, |
|
+ 'plugins': {'certauth': {'module': ['test1:' + modpath, |
|
+ 'test2:' + modpath], |
|
+ 'enable_only': ['test1', 'test2']}}} |
|
+pkinit_kdc_conf = {'realms': {'$realm': { |
|
+ 'default_principal_flags': '+preauth', |
|
+ 'pkinit_eku_checking': 'none', |
|
+ 'pkinit_identity': 'FILE:%s,%s' % (kdc_pem, privkey_pem), |
|
+ 'pkinit_indicator': ['indpkinit1', 'indpkinit2']}}} |
|
+ |
|
+file_identity = 'FILE:%s,%s' % (user_pem, privkey_pem) |
|
+ |
|
+realm = K5Realm(krb5_conf=pkinit_krb5_conf, kdc_conf=pkinit_kdc_conf, |
|
+ get_creds=False) |
|
+ |
|
+# Let the test module match user to CN=user, with indicators. |
|
+realm.kinit(realm.user_princ, |
|
+ flags=['-X', 'X509_user_identity=%s' % file_identity]) |
|
+realm.klist(realm.user_princ) |
|
+realm.run([kvno, realm.host_princ]) |
|
+realm.run(['./adata', realm.host_princ], |
|
+ expected_msg='+97: [test1, test2, user, indpkinit1, indpkinit2]') |
|
+ |
|
+# Let the test module mismatch with user2 to CN=user. |
|
+realm.addprinc("user2@KRBTEST.COM") |
|
+out = realm.kinit("user2@KRBTEST.COM", |
|
+ flags=['-X', 'X509_user_identity=%s' % file_identity], |
|
+ expected_code=1, |
|
+ expected_msg='kinit: Certificate mismatch') |
|
+ |
|
+success("certauth tests")
|
|
|