summaryrefslogtreecommitdiff
path: root/src/plugins
diff options
context:
space:
mode:
authorCy Schubert <cy@FreeBSD.org>2018-04-03 19:36:00 +0000
committerCy Schubert <cy@FreeBSD.org>2018-04-03 19:36:00 +0000
commitb0e4d68d5124581ae353493d69bea352de4cff8a (patch)
tree43300ec43e83eccd367fd76fdfdefba2dcd7d8f4 /src/plugins
parent33a9b234e7087f573ef08cd7318c6497ba08b439 (diff)
Notes
Diffstat (limited to 'src/plugins')
-rwxr-xr-xsrc/plugins/audit/kdc_j_encode.c29
-rw-r--r--src/plugins/certauth/test/Makefile.in20
-rw-r--r--src/plugins/certauth/test/certauth_test.exports2
-rw-r--r--src/plugins/certauth/test/deps14
-rw-r--r--src/plugins/certauth/test/main.c211
-rw-r--r--src/plugins/kadm5_auth/test/Makefile.in20
-rw-r--r--src/plugins/kadm5_auth/test/deps22
-rw-r--r--src/plugins/kadm5_auth/test/kadm5_auth_test.exports2
-rw-r--r--src/plugins/kadm5_auth/test/main.c305
-rw-r--r--src/plugins/kdb/db2/db2_exp.c5
-rw-r--r--src/plugins/kdb/db2/kdb_db2.c13
-rw-r--r--src/plugins/kdb/db2/kdb_db2.h5
-rw-r--r--src/plugins/kdb/db2/libdb2/hash/hash.c19
-rw-r--r--src/plugins/kdb/db2/lockout.c8
-rw-r--r--src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.c6
-rw-r--r--src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.h16
-rw-r--r--src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap_conn.c1
-rw-r--r--src/plugins/kdb/ldap/libkdb_ldap/kerberos.openldap.ldif68
-rw-r--r--src/plugins/kdb/ldap/libkdb_ldap/ldap_handle.c68
-rw-r--r--src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.c98
-rw-r--r--src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c2
-rw-r--r--src/plugins/kdb/ldap/libkdb_ldap/ldap_tkt_policy.c2
-rw-r--r--src/plugins/kdb/ldap/libkdb_ldap/lockout.c8
-rw-r--r--src/plugins/kdcpolicy/test/Makefile.in20
-rw-r--r--src/plugins/kdcpolicy/test/deps14
-rw-r--r--src/plugins/kdcpolicy/test/kdcpolicy_test.exports1
-rw-r--r--src/plugins/kdcpolicy/test/main.c111
-rw-r--r--src/plugins/preauth/otp/main.c3
-rw-r--r--src/plugins/preauth/pkinit/Makefile.in8
-rw-r--r--src/plugins/preauth/pkinit/deps11
-rw-r--r--src/plugins/preauth/pkinit/pkinit.h10
-rw-r--r--src/plugins/preauth/pkinit/pkinit_clnt.c7
-rw-r--r--src/plugins/preauth/pkinit/pkinit_crypto.h88
-rw-r--r--src/plugins/preauth/pkinit/pkinit_crypto_nss.c5800
-rw-r--r--src/plugins/preauth/pkinit/pkinit_crypto_openssl.c838
-rw-r--r--src/plugins/preauth/pkinit/pkinit_crypto_openssl.h19
-rw-r--r--src/plugins/preauth/pkinit/pkinit_identity.c32
-rw-r--r--src/plugins/preauth/pkinit/pkinit_matching.c177
-rw-r--r--src/plugins/preauth/pkinit/pkinit_srv.c448
-rw-r--r--src/plugins/preauth/pkinit/pkinit_trace.h78
-rw-r--r--src/plugins/preauth/test/Makefile.in4
-rw-r--r--src/plugins/preauth/test/cltest.c86
-rw-r--r--src/plugins/preauth/test/common.c61
-rw-r--r--src/plugins/preauth/test/common.h41
-rw-r--r--src/plugins/preauth/test/deps14
-rw-r--r--src/plugins/preauth/test/kdctest.c96
46 files changed, 2075 insertions, 6836 deletions
diff --git a/src/plugins/audit/kdc_j_encode.c b/src/plugins/audit/kdc_j_encode.c
index e24f4d851066..265e95bc4eab 100755
--- a/src/plugins/audit/kdc_j_encode.c
+++ b/src/plugins/audit/kdc_j_encode.c
@@ -861,22 +861,19 @@ tkt_to_value(krb5_ticket *tkt, k5_json_object obj,
ret = int32_to_value(part2->session->enctype, tmp, AU_SESS_ETYPE);
if (ret)
goto error;
- if (&part2->times) {
- ret = int32_to_value(part2->times.starttime, tmp, AU_START);
- if (ret)
- goto error;
- ret = int32_to_value(part2->times.endtime, tmp, AU_END);
- if (ret)
- goto error;
- ret = int32_to_value(part2->times.renew_till, tmp, AU_RENEW_TILL);
- if (ret)
- goto error;
- ret = int32_to_value(part2->times.authtime, tmp, AU_AUTHTIME);
- if (ret)
- goto error;
- }
- if (&part2->transited && &part2->transited.tr_contents &&
- part2->transited.tr_contents.length > 0) {
+ ret = int32_to_value(part2->times.starttime, tmp, AU_START);
+ if (ret)
+ goto error;
+ ret = int32_to_value(part2->times.endtime, tmp, AU_END);
+ if (ret)
+ goto error;
+ ret = int32_to_value(part2->times.renew_till, tmp, AU_RENEW_TILL);
+ if (ret)
+ goto error;
+ ret = int32_to_value(part2->times.authtime, tmp, AU_AUTHTIME);
+ if (ret)
+ goto error;
+ if (part2->transited.tr_contents.length > 0) {
ret = data_to_value(&part2->transited.tr_contents,
tmp, AU_TR_CONTENTS);
if (ret)
diff --git a/src/plugins/certauth/test/Makefile.in b/src/plugins/certauth/test/Makefile.in
new file mode 100644
index 000000000000..d3524084c120
--- /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 000000000000..1c8cd24e2b93
--- /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 000000000000..2974b3b57e2a
--- /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 000000000000..77641230c402
--- /dev/null
+++ b/src/plugins/certauth/test/main.c
@@ -0,0 +1,211 @@
+/* -*- 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 struct _krb5_db_entry_new *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 struct _krb5_db_entry_new *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/kadm5_auth/test/Makefile.in b/src/plugins/kadm5_auth/test/Makefile.in
new file mode 100644
index 000000000000..825c4aea8b82
--- /dev/null
+++ b/src/plugins/kadm5_auth/test/Makefile.in
@@ -0,0 +1,20 @@
+mydir=plugins$(S)kadm5_auth$(S)test
+BUILDTOP=$(REL)..$(S)..$(S)..
+
+LIBBASE=kadm5_auth_test
+LIBMAJOR=0
+LIBMINOR=0
+RELDIR=../plugins/kadm5_auth/test
+SHLIB_EXPDEPS=$(KDB5_DEPLIBS) $(KRB5_BASE_DEPLIBS)
+SHLIB_EXPLIBS=$(KDB5_LIBS) $(KRB5_BASE_LIBS) $(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/kadm5_auth/test/deps b/src/plugins/kadm5_auth/test/deps
new file mode 100644
index 000000000000..a2b74c21d1c1
--- /dev/null
+++ b/src/plugins/kadm5_auth/test/deps
@@ -0,0 +1,22 @@
+#
+# Generated makefile dependencies follow.
+#
+main.so main.po $(OUTPRE)main.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \
+ $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/chpass_util_strings.h \
+ $(BUILDTOP)/include/kadm5/kadm_err.h $(BUILDTOP)/include/krb5/krb5.h \
+ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
+ $(COM_ERR_DEPS) $(top_srcdir)/include/gssrpc/auth.h \
+ $(top_srcdir)/include/gssrpc/auth_gss.h $(top_srcdir)/include/gssrpc/auth_unix.h \
+ $(top_srcdir)/include/gssrpc/clnt.h $(top_srcdir)/include/gssrpc/rename.h \
+ $(top_srcdir)/include/gssrpc/rpc.h $(top_srcdir)/include/gssrpc/rpc_msg.h \
+ $(top_srcdir)/include/gssrpc/svc.h $(top_srcdir)/include/gssrpc/svc_auth.h \
+ $(top_srcdir)/include/gssrpc/xdr.h $(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/kdb.h $(top_srcdir)/include/krb5.h \
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/kadm5_auth_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/kadm5_auth/test/kadm5_auth_test.exports b/src/plugins/kadm5_auth/test/kadm5_auth_test.exports
new file mode 100644
index 000000000000..31319af2a9d8
--- /dev/null
+++ b/src/plugins/kadm5_auth/test/kadm5_auth_test.exports
@@ -0,0 +1,2 @@
+kadm5_auth_welcomer_initvt
+kadm5_auth_bouncer_initvt
diff --git a/src/plugins/kadm5_auth/test/main.c b/src/plugins/kadm5_auth/test/main.c
new file mode 100644
index 000000000000..6899f22e9edb
--- /dev/null
+++ b/src/plugins/kadm5_auth/test/main.c
@@ -0,0 +1,305 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* plugins/kadm5_auth/test/main.c - test modules for kadm5_auth interface */
+/*
+ * Copyright (C) 2017 by the Massachusetts Institute of Technology.
+ * 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.
+ */
+
+/*
+ * This file implements two testing kadm5_auth modules, the welcomer and the
+ * bouncer. The welcomer implements permissive behavior, while the bouncer
+ * implements restrictive behavior.
+ *
+ * Module data objects and restrictions are adequately tested by the acl
+ * module, so we do not test them here. Focus instead on the ability to
+ * examine principal and policy objects and to perform DB operations.
+ */
+
+#include "k5-int.h"
+#include <kadm5/admin.h>
+#include <krb5/kadm5_auth_plugin.h>
+
+krb5_error_code
+kadm5_auth_welcomer_initvt(krb5_context context, int maj_ver, int min_ver,
+ krb5_plugin_vtable vtable);
+krb5_error_code
+kadm5_auth_bouncer_initvt(krb5_context context, int maj_ver, int min_ver,
+ krb5_plugin_vtable vtable);
+
+/* The welcomer authorizes all getprinc operations, since kadmin uses them as a
+ * precursor to modprinc. */
+static krb5_error_code
+welcomer_getprinc(krb5_context context, kadm5_auth_moddata data,
+ krb5_const_principal client, krb5_const_principal target)
+{
+ return 0;
+}
+
+/* The welcomer authorizes addprinc operations which set a policy "VIP". */
+static krb5_error_code
+welcomer_addprinc(krb5_context context, kadm5_auth_moddata data,
+ krb5_const_principal client, krb5_const_principal target,
+ const struct _kadm5_principal_ent_t *ent, long mask,
+ struct kadm5_auth_restrictions **rs_out)
+{
+ if ((mask & KADM5_POLICY) && strcmp(ent->policy, "VIP") == 0)
+ return 0;
+ return KRB5_PLUGIN_NO_HANDLE;
+}
+
+/* The bouncer denies addprinc operations which include a maximum lifetime. */
+static krb5_error_code
+bouncer_addprinc(krb5_context context, kadm5_auth_moddata data,
+ krb5_const_principal client, krb5_const_principal target,
+ const struct _kadm5_principal_ent_t *ent, long mask,
+ struct kadm5_auth_restrictions **rs_out)
+{
+ return (mask & KADM5_MAX_LIFE) ? EPERM : KRB5_PLUGIN_NO_HANDLE;
+}
+
+/* The welcomer authorizes modprinc operations which only set maxrenewlife. */
+static krb5_error_code
+welcomer_modprinc(krb5_context context, kadm5_auth_moddata data,
+ krb5_const_principal client, krb5_const_principal target,
+ const struct _kadm5_principal_ent_t *ent, long mask,
+ struct kadm5_auth_restrictions **rs_out)
+{
+ return (mask == KADM5_MAX_RLIFE) ? 0 : KRB5_PLUGIN_NO_HANDLE;
+}
+
+/* The bouncer denies modprinc operations if the target principal has an even
+ * number of components. */
+static krb5_error_code
+bouncer_modprinc(krb5_context context, kadm5_auth_moddata data,
+ krb5_const_principal client, krb5_const_principal target,
+ const struct _kadm5_principal_ent_t *ent, long mask,
+ struct kadm5_auth_restrictions **rs_out)
+{
+ return (target->length % 2 == 0) ? EPERM : KRB5_PLUGIN_NO_HANDLE;
+}
+
+/* The welcomer authorizes setstr operations for the attribute "note". */
+static krb5_error_code
+welcomer_setstr(krb5_context context, kadm5_auth_moddata data,
+ krb5_const_principal client, krb5_const_principal target,
+ const char *key, const char *value)
+{
+ return (strcmp(key, "note") == 0) ? 0 : KRB5_PLUGIN_NO_HANDLE;
+}
+
+/* The bouncer denies setstr operations if the value is more than 10 bytes. */
+static krb5_error_code
+bouncer_setstr(krb5_context context, kadm5_auth_moddata data,
+ krb5_const_principal client, krb5_const_principal target,
+ const char *key, const char *value)
+{
+ return (strlen(value) > 10) ? EPERM : KRB5_PLUGIN_NO_HANDLE;
+}
+
+/* The welcomer authorizes delprinc operations if the target principal starts
+ * with "d". */
+static krb5_error_code
+welcomer_delprinc(krb5_context context, kadm5_auth_moddata data,
+ krb5_const_principal client, krb5_const_principal target)
+{
+ if (target->length > 0 && target->data[0].length > 0 &&
+ *target->data[0].data == 'd')
+ return 0;
+ return KRB5_PLUGIN_NO_HANDLE;
+}
+
+/* The bouncer denies delprinc operations if the target principal has the
+ * "nodelete" string attribute. */
+static krb5_error_code
+bouncer_delprinc(krb5_context context, kadm5_auth_moddata data,
+ krb5_const_principal client, krb5_const_principal target)
+{
+ krb5_error_code ret;
+ krb5_db_entry *ent;
+ char *val = NULL;
+
+ if (krb5_db_get_principal(context, target, 0, &ent) != 0)
+ return EPERM;
+ ret = krb5_dbe_get_string(context, ent, "nodelete", &val);
+ krb5_db_free_principal(context, ent);
+ ret = (ret != 0 || val != NULL) ? EPERM : KRB5_PLUGIN_NO_HANDLE;
+ krb5_dbe_free_string(context, val);
+ return ret;
+}
+
+/* The welcomer authorizes rename operations if the first components of the
+ * principals have the same length. */
+static krb5_error_code
+welcomer_renprinc(krb5_context context, kadm5_auth_moddata data,
+ krb5_const_principal client, krb5_const_principal src,
+ krb5_const_principal dest)
+{
+ if (src->length > 0 && dest->length > 0 &&
+ src->data[0].length == dest->data[0].length)
+ return 0;
+ return KRB5_PLUGIN_NO_HANDLE;
+}
+
+/* The bouncer denies rename operations if the source principal starts with
+ * "a". */
+static krb5_error_code
+bouncer_renprinc(krb5_context context, kadm5_auth_moddata data,
+ krb5_const_principal client, krb5_const_principal src,
+ krb5_const_principal dest)
+{
+ if (src->length > 0 && src->data[0].length > 0 &&
+ *src->data[0].data == 'a')
+ return EPERM;
+ return KRB5_PLUGIN_NO_HANDLE;
+}
+
+/* The welcomer authorizes addpol operations which set a minlength of 3. */
+static krb5_error_code
+welcomer_addpol(krb5_context context, kadm5_auth_moddata data,
+ krb5_const_principal client, const char *policy,
+ const struct _kadm5_policy_ent_t *ent, long mask)
+{
+ if ((mask & KADM5_PW_MIN_LENGTH) && ent->pw_min_length == 3)
+ return 0;
+ return KRB5_PLUGIN_NO_HANDLE;
+}
+
+/* The bouncer denies addpol operations if the name is 3 bytes or less. */
+static krb5_error_code
+bouncer_addpol(krb5_context context, kadm5_auth_moddata data,
+ krb5_const_principal client, const char *policy,
+ const struct _kadm5_policy_ent_t *ent, long mask)
+{
+ return (strlen(policy) <= 3) ? EPERM : KRB5_PLUGIN_NO_HANDLE;
+}
+
+/* The welcomer authorizes modpol operations which only change min_life. */
+static krb5_error_code
+welcomer_modpol(krb5_context context, kadm5_auth_moddata data,
+ krb5_const_principal client, const char *policy,
+ const struct _kadm5_policy_ent_t *ent, long mask)
+{
+ return (mask == KADM5_PW_MIN_LIFE) ? 0 : KRB5_PLUGIN_NO_HANDLE;
+}
+
+/* The bouncer denies modpol operations which set pw_min_life above 10. */
+static krb5_error_code
+bouncer_modpol(krb5_context context, kadm5_auth_moddata data,
+ krb5_const_principal client, const char *policy,
+ const struct _kadm5_policy_ent_t *ent, long mask)
+{
+ if ((mask & KADM5_PW_MIN_LIFE) && ent->pw_min_life > 10)
+ return EPERM;
+ return KRB5_PLUGIN_NO_HANDLE;
+}
+
+/* The welcomer authorizes getpol operations if the policy and client principal
+ * policy have the same length. */
+static krb5_error_code
+welcomer_getpol(krb5_context context, kadm5_auth_moddata data,
+ krb5_const_principal client, const char *policy,
+ const char *client_policy)
+{
+ if (client_policy != NULL && strlen(policy) == strlen(client_policy))
+ return 0;
+ return KRB5_PLUGIN_NO_HANDLE;
+}
+
+/* The bouncer denies getpol operations if the policy name begins with 'x'. */
+static krb5_error_code
+bouncer_getpol(krb5_context context, kadm5_auth_moddata data,
+ krb5_const_principal client, const char *policy,
+ const char *client_policy)
+{
+ return (*policy == 'x') ? EPERM : KRB5_PLUGIN_NO_HANDLE;
+}
+
+/* The welcomer counts end calls by incrementing the "ends" string attribute on
+ * the "opcount" principal, if it exists. */
+static void
+welcomer_end(krb5_context context, kadm5_auth_moddata data)
+{
+ krb5_principal princ = NULL;
+ krb5_db_entry *ent = NULL;
+ char *val = NULL, buf[10];
+
+ if (krb5_parse_name(context, "opcount", &princ) != 0)
+ goto cleanup;
+ if (krb5_db_get_principal(context, princ, 0, &ent) != 0)
+ goto cleanup;
+ if (krb5_dbe_get_string(context, ent, "ends", &val) != 0 || val == NULL)
+ goto cleanup;
+ snprintf(buf, sizeof(buf), "%d", atoi(val) + 1);
+ if (krb5_dbe_set_string(context, ent, "ends", buf) != 0)
+ goto cleanup;
+ ent->mask = KADM5_TL_DATA;
+ krb5_db_put_principal(context, ent);
+
+cleanup:
+ krb5_dbe_free_string(context, val);
+ krb5_db_free_principal(context, ent);
+ krb5_free_principal(context, princ);
+}
+
+krb5_error_code
+kadm5_auth_welcomer_initvt(krb5_context context, int maj_ver, int min_ver,
+ krb5_plugin_vtable vtable)
+{
+ kadm5_auth_vtable vt = (kadm5_auth_vtable)vtable;
+
+ vt->name = "welcomer";
+ vt->addprinc = welcomer_addprinc;
+ vt->modprinc = welcomer_modprinc;
+ vt->setstr = welcomer_setstr;
+ vt->delprinc = welcomer_delprinc;
+ vt->renprinc = welcomer_renprinc;
+ vt->getprinc = welcomer_getprinc;
+ vt->addpol = welcomer_addpol;
+ vt->modpol = welcomer_modpol;
+ vt->getpol = welcomer_getpol;
+ vt->end = welcomer_end;
+ return 0;
+}
+
+krb5_error_code
+kadm5_auth_bouncer_initvt(krb5_context context, int maj_ver, int min_ver,
+ krb5_plugin_vtable vtable)
+{
+ kadm5_auth_vtable vt = (kadm5_auth_vtable)vtable;
+
+ vt->name = "bouncer";
+ vt->addprinc = bouncer_addprinc;
+ vt->modprinc = bouncer_modprinc;
+ vt->setstr = bouncer_setstr;
+ vt->delprinc = bouncer_delprinc;
+ vt->renprinc = bouncer_renprinc;
+ vt->addpol = bouncer_addpol;
+ vt->modpol = bouncer_modpol;
+ vt->getpol = bouncer_getpol;
+ return 0;
+}
diff --git a/src/plugins/kdb/db2/db2_exp.c b/src/plugins/kdb/db2/db2_exp.c
index 1a41481f9fae..4d905db77487 100644
--- a/src/plugins/kdb/db2/db2_exp.c
+++ b/src/plugins/kdb/db2/db2_exp.c
@@ -167,9 +167,12 @@ WRAP_K (krb5_db2_check_policy_as,
WRAP_VOID (krb5_db2_audit_as_req,
(krb5_context kcontext, krb5_kdc_req *request,
+ const krb5_address *local_addr,
+ const krb5_address *remote_addr,
krb5_db_entry *client, krb5_db_entry *server,
krb5_timestamp authtime, krb5_error_code error_code),
- (kcontext, request, client, server, authtime, error_code));
+ (kcontext, request, local_addr, remote_addr, client, server,
+ authtime, error_code));
static krb5_error_code
hack_init (void)
diff --git a/src/plugins/kdb/db2/kdb_db2.c b/src/plugins/kdb/db2/kdb_db2.c
index 4c4036eb4714..d23587a59790 100644
--- a/src/plugins/kdb/db2/kdb_db2.c
+++ b/src/plugins/kdb/db2/kdb_db2.c
@@ -1314,13 +1314,6 @@ krb5_db2_delete_policy(krb5_context context, char *policy)
return osa_adb_destroy_policy(dbc->policy_db, policy);
}
-void
-krb5_db2_free_policy(krb5_context context, osa_policy_ent_t entry)
-{
- osa_free_policy_ent(entry);
-}
-
-
/*
* Merge non-replicated attributes from src into dst, setting
* changed to non-zero if dst was changed.
@@ -1558,8 +1551,10 @@ krb5_db2_check_policy_as(krb5_context kcontext, krb5_kdc_req *request,
void
krb5_db2_audit_as_req(krb5_context kcontext, krb5_kdc_req *request,
- krb5_db_entry *client, krb5_db_entry *server,
- krb5_timestamp authtime, krb5_error_code error_code)
+ const krb5_address *local_addr,
+ const krb5_address *remote_addr, krb5_db_entry *client,
+ krb5_db_entry *server, krb5_timestamp authtime,
+ krb5_error_code error_code)
{
(void) krb5_db2_lockout_audit(kcontext, client, authtime, error_code);
}
diff --git a/src/plugins/kdb/db2/kdb_db2.h b/src/plugins/kdb/db2/kdb_db2.h
index b1b50c8286d6..349244dd92d7 100644
--- a/src/plugins/kdb/db2/kdb_db2.h
+++ b/src/plugins/kdb/db2/kdb_db2.h
@@ -134,7 +134,10 @@ krb5_db2_check_policy_as(krb5_context kcontext, krb5_kdc_req *request,
void
krb5_db2_audit_as_req(krb5_context kcontext, krb5_kdc_req *request,
+ const krb5_address *local_addr,
+ const krb5_address *remote_addr,
krb5_db_entry *client, krb5_db_entry *server,
- krb5_timestamp authtime, krb5_error_code error_code);
+ krb5_timestamp authtime,
+ krb5_error_code error_code);
#endif /* KRB5_KDB_DB2_H */
diff --git a/src/plugins/kdb/db2/libdb2/hash/hash.c b/src/plugins/kdb/db2/libdb2/hash/hash.c
index 76f5d470932e..862dbb164080 100644
--- a/src/plugins/kdb/db2/libdb2/hash/hash.c
+++ b/src/plugins/kdb/db2/libdb2/hash/hash.c
@@ -103,26 +103,15 @@ __kdb2_hash_open(file, flags, mode, info, dflags)
DB *dbp;
DBT mpool_key;
HTAB *hashp;
- int32_t bpages, csize, new_table, save_errno, specified_file;
+ int32_t bpages, csize, new_table, save_errno;
- if ((flags & O_ACCMODE) == O_WRONLY) {
+ if (!file || (flags & O_ACCMODE) == O_WRONLY) {
errno = EINVAL;
return (NULL);
}
if (!(hashp = (HTAB *)calloc(1, sizeof(HTAB))))
return (NULL);
hashp->fp = -1;
-
- /* set this now, before file goes away... */
- specified_file = (file != NULL);
- if (!file) {
- file = tmpnam(NULL);
- /* store the file name so that we can unlink it later */
- hashp->fname = file;
-#ifdef DEBUG
- fprintf(stderr, "Using file name %s.\n", file);
-#endif
- }
/*
* Even if user wants write only, we need to be able to read
* the actual file, so we need to open it read/write. But, the
@@ -130,7 +119,7 @@ __kdb2_hash_open(file, flags, mode, info, dflags)
* we can check accesses.
*/
hashp->flags = flags;
- hashp->save_file = specified_file && (hashp->flags & O_RDWR);
+ hashp->save_file = hashp->flags & O_RDWR;
new_table = 0;
if (!file || (flags & O_TRUNC) ||
@@ -542,8 +531,6 @@ hdestroy(hashp)
/* we need to chmod the file to allow it to be deleted... */
chmod(hashp->fname, 0700);
unlink(hashp->fname);
- /* destroy the temporary name */
- tmpnam(NULL);
}
free(hashp);
diff --git a/src/plugins/kdb/db2/lockout.c b/src/plugins/kdb/db2/lockout.c
index 7d151b55b35a..3a4f41821837 100644
--- a/src/plugins/kdb/db2/lockout.c
+++ b/src/plugins/kdb/db2/lockout.c
@@ -100,7 +100,7 @@ locked_check_p(krb5_context context,
/* If the entry was unlocked since the last failure, it's not locked. */
if (krb5_dbe_lookup_last_admin_unlock(context, entry, &unlock_time) == 0 &&
- entry->last_failed <= unlock_time)
+ !ts_after(entry->last_failed, unlock_time))
return FALSE;
if (max_fail == 0 || entry->fail_auth_count < max_fail)
@@ -109,7 +109,7 @@ locked_check_p(krb5_context context,
if (lockout_duration == 0)
return TRUE; /* principal permanently locked */
- return (stamp < entry->last_failed + lockout_duration);
+ return ts_after(ts_incr(entry->last_failed, lockout_duration), stamp);
}
krb5_error_code
@@ -200,13 +200,13 @@ krb5_db2_lockout_audit(krb5_context context,
status == KRB5KRB_AP_ERR_BAD_INTEGRITY)) {
if (krb5_dbe_lookup_last_admin_unlock(context, entry,
&unlock_time) == 0 &&
- entry->last_failed <= unlock_time) {
+ !ts_after(entry->last_failed, unlock_time)) {
/* Reset fail_auth_count after administrative unlock. */
entry->fail_auth_count = 0;
}
if (failcnt_interval != 0 &&
- stamp > entry->last_failed + failcnt_interval) {
+ ts_after(stamp, ts_incr(entry->last_failed, failcnt_interval))) {
/* Reset fail_auth_count after failcnt_interval. */
entry->fail_auth_count = 0;
}
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.c b/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.c
index 7ba8075cb849..4fbf898965c2 100644
--- a/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.c
+++ b/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.c
@@ -277,8 +277,10 @@ krb5_ldap_check_policy_as(krb5_context kcontext, krb5_kdc_req *request,
void
krb5_ldap_audit_as_req(krb5_context kcontext, krb5_kdc_req *request,
- krb5_db_entry *client, krb5_db_entry *server,
- krb5_timestamp authtime, krb5_error_code error_code)
+ const krb5_address *local_addr,
+ const krb5_address *remote_addr, krb5_db_entry *client,
+ krb5_db_entry *server, krb5_timestamp authtime,
+ krb5_error_code error_code)
{
(void) krb5_ldap_lockout_audit(kcontext, client, authtime, error_code);
}
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.h b/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.h
index 06b477537d5a..535a1f309e29 100644
--- a/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.h
+++ b/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.h
@@ -171,7 +171,6 @@ typedef struct _krb5_ldap_server_info krb5_ldap_server_info;
typedef struct _krb5_ldap_server_handle {
int msgid;
LDAP *ldap_handle;
- krb5_boolean server_info_update_pending;
krb5_ldap_server_info *server_info;
struct _krb5_ldap_server_handle *next;
} krb5_ldap_server_handle;
@@ -282,8 +281,10 @@ krb5_ldap_check_policy_as(krb5_context kcontext, krb5_kdc_req *request,
void
krb5_ldap_audit_as_req(krb5_context kcontext, krb5_kdc_req *request,
- krb5_db_entry *client, krb5_db_entry *server,
- krb5_timestamp authtime, krb5_error_code error_code);
+ const krb5_address *local_addr,
+ const krb5_address *remote_addr, krb5_db_entry *client,
+ krb5_db_entry *server, krb5_timestamp authtime,
+ krb5_error_code error_code);
krb5_error_code
krb5_ldap_check_allowed_to_delegate(krb5_context context,
@@ -300,15 +301,6 @@ krb5_ldap_lock( krb5_context, int );
krb5_error_code
krb5_ldap_unlock( krb5_context );
-#ifndef HAVE_LDAP_INITIALIZE
-int
-ldap_initialize(LDAP **, char *);
-#endif
-#ifndef HAVE_LDAP_UNBIND_EXT_S
-int
-ldap_unbind_ext_s(LDAP *, LDAPControl **, LDAPControl **);
-#endif
-
/* lockout.c */
krb5_error_code
krb5_ldap_lockout_check_policy(krb5_context context,
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap_conn.c b/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap_conn.c
index d904c9933b2e..cee4b7b8d30e 100644
--- a/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap_conn.c
+++ b/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap_conn.c
@@ -193,7 +193,6 @@ initialize_server(krb5_ldap_context *ldap_context, krb5_ldap_server_info *info)
return ret;
}
- server->server_info_update_pending = FALSE;
server->next = info->ldap_server_handles;
info->ldap_server_handles = server;
info->num_conns++;
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/kerberos.openldap.ldif b/src/plugins/kdb/ldap/libkdb_ldap/kerberos.openldap.ldif
new file mode 100644
index 000000000000..830277d738ca
--- /dev/null
+++ b/src/plugins/kdb/ldap/libkdb_ldap/kerberos.openldap.ldif
@@ -0,0 +1,68 @@
+# This LDIF version of the Kerberos schema can be loaded into an
+# OpenLDAP database. It was originally converted semi-automatically
+# from kerberos.schema using slaptest.
+
+dn: cn=kerberos,cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: kerberos
+olcAttributeTypes: ( 2.16.840.1.113719.1.301.4.1.1 NAME 'krbPrincipalName' EQUALITY caseExactIA5Match SUBSTR caseExactSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: ( 1.2.840.113554.1.4.1.6.1 NAME 'krbCanonicalName' EQUALITY caseExactIA5Match SUBSTR caseExactSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: ( 2.16.840.1.113719.1.301.4.3.1 NAME 'krbPrincipalType' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+olcAttributeTypes: ( 2.16.840.1.113719.1.301.4.5.1 NAME 'krbUPEnabled' DESC 'Boolean' SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
+olcAttributeTypes: ( 2.16.840.1.113719.1.301.4.6.1 NAME 'krbPrincipalExpiration' EQUALITY generalizedTimeMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-VALUE )
+olcAttributeTypes: ( 2.16.840.1.113719.1.301.4.8.1 NAME 'krbTicketFlags' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+olcAttributeTypes: ( 2.16.840.1.113719.1.301.4.9.1 NAME 'krbMaxTicketLife' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+olcAttributeTypes: ( 2.16.840.1.113719.1.301.4.10.1 NAME 'krbMaxRenewableAge' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+olcAttributeTypes: ( 2.16.840.1.113719.1.301.4.14.1 NAME 'krbRealmReferences' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+olcAttributeTypes: ( 2.16.840.1.113719.1.301.4.15.1 NAME 'krbLdapServers' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+olcAttributeTypes: ( 2.16.840.1.113719.1.301.4.17.1 NAME 'krbKdcServers' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+olcAttributeTypes: ( 2.16.840.1.113719.1.301.4.18.1 NAME 'krbPwdServers' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+olcAttributeTypes: ( 2.16.840.1.113719.1.301.4.24.1 NAME 'krbHostServer' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: ( 2.16.840.1.113719.1.301.4.25.1 NAME 'krbSearchScope' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+olcAttributeTypes: ( 2.16.840.1.113719.1.301.4.26.1 NAME 'krbPrincipalReferences' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+olcAttributeTypes: ( 2.16.840.1.113719.1.301.4.28.1 NAME 'krbPrincNamingAttr' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
+olcAttributeTypes: ( 2.16.840.1.113719.1.301.4.29.1 NAME 'krbAdmServers' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+olcAttributeTypes: ( 2.16.840.1.113719.1.301.4.30.1 NAME 'krbMaxPwdLife' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+olcAttributeTypes: ( 2.16.840.1.113719.1.301.4.31.1 NAME 'krbMinPwdLife' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+olcAttributeTypes: ( 2.16.840.1.113719.1.301.4.32.1 NAME 'krbPwdMinDiffChars' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+olcAttributeTypes: ( 2.16.840.1.113719.1.301.4.33.1 NAME 'krbPwdMinLength' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+olcAttributeTypes: ( 2.16.840.1.113719.1.301.4.34.1 NAME 'krbPwdHistoryLength' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+olcAttributeTypes: ( 1.3.6.1.4.1.5322.21.2.1 NAME 'krbPwdMaxFailure' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+olcAttributeTypes: ( 1.3.6.1.4.1.5322.21.2.2 NAME 'krbPwdFailureCountInterval' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+olcAttributeTypes: ( 1.3.6.1.4.1.5322.21.2.3 NAME 'krbPwdLockoutDuration' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+olcAttributeTypes: ( 1.2.840.113554.1.4.1.6.2 NAME 'krbPwdAttributes' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+olcAttributeTypes: ( 1.2.840.113554.1.4.1.6.3 NAME 'krbPwdMaxLife' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+olcAttributeTypes: ( 1.2.840.113554.1.4.1.6.4 NAME 'krbPwdMaxRenewableLife' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+olcAttributeTypes: ( 1.2.840.113554.1.4.1.6.5 NAME 'krbPwdAllowedKeysalts' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: ( 2.16.840.1.113719.1.301.4.36.1 NAME 'krbPwdPolicyReference' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE )
+olcAttributeTypes: ( 2.16.840.1.113719.1.301.4.37.1 NAME 'krbPasswordExpiration' EQUALITY generalizedTimeMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-VALUE )
+olcAttributeTypes: ( 2.16.840.1.113719.1.301.4.39.1 NAME 'krbPrincipalKey' EQUALITY octetStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
+olcAttributeTypes: ( 2.16.840.1.113719.1.301.4.40.1 NAME 'krbTicketPolicyReference' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE )
+olcAttributeTypes: ( 2.16.840.1.113719.1.301.4.41.1 NAME 'krbSubTrees' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+olcAttributeTypes: ( 2.16.840.1.113719.1.301.4.42.1 NAME 'krbDefaultEncSaltTypes' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+olcAttributeTypes: ( 2.16.840.1.113719.1.301.4.43.1 NAME 'krbSupportedEncSaltTypes' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+olcAttributeTypes: ( 2.16.840.1.113719.1.301.4.44.1 NAME 'krbPwdHistory' EQUALITY octetStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
+olcAttributeTypes: ( 2.16.840.1.113719.1.301.4.45.1 NAME 'krbLastPwdChange' EQUALITY generalizedTimeMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-VALUE )
+olcAttributeTypes: ( 1.3.6.1.4.1.5322.21.2.5 NAME 'krbLastAdminUnlock' EQUALITY generalizedTimeMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-VALUE )
+olcAttributeTypes: ( 2.16.840.1.113719.1.301.4.46.1 NAME 'krbMKey' EQUALITY octetStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
+olcAttributeTypes: ( 2.16.840.1.113719.1.301.4.47.1 NAME 'krbPrincipalAliases' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: ( 2.16.840.1.113719.1.301.4.48.1 NAME 'krbLastSuccessfulAuth' EQUALITY generalizedTimeMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-VALUE )
+olcAttributeTypes: ( 2.16.840.1.113719.1.301.4.49.1 NAME 'krbLastFailedAuth' EQUALITY generalizedTimeMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-VALUE )
+olcAttributeTypes: ( 2.16.840.1.113719.1.301.4.50.1 NAME 'krbLoginFailedCount' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+olcAttributeTypes: ( 2.16.840.1.113719.1.301.4.51.1 NAME 'krbExtraData' EQUALITY octetStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
+olcAttributeTypes: ( 2.16.840.1.113719.1.301.4.52.1 NAME 'krbObjectReferences' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+olcAttributeTypes: ( 2.16.840.1.113719.1.301.4.53.1 NAME 'krbPrincContainerRef' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+olcAttributeTypes: ( 2.16.840.1.113730.3.8.15.2.1 NAME 'krbPrincipalAuthInd' EQUALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+olcAttributeTypes: ( 1.3.6.1.4.1.5322.21.2.4 NAME 'krbAllowedToDelegateTo' EQUALITY caseExactIA5Match SUBSTR caseExactSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcObjectClasses: ( 2.16.840.1.113719.1.301.6.1.1 NAME 'krbContainer' SUP top STRUCTURAL MUST cn )
+olcObjectClasses: ( 2.16.840.1.113719.1.301.6.2.1 NAME 'krbRealmContainer' SUP top STRUCTURAL MUST cn MAY ( krbMKey $ krbUPEnabled $ krbSubTrees $ krbSearchScope $ krbLdapServers $ krbSupportedEncSaltTypes $ krbDefaultEncSaltTypes $ krbTicketPolicyReference $ krbKdcServers $ krbPwdServers $ krbAdmServers $ krbPrincNamingAttr $ krbPwdPolicyReference $ krbPrincContainerRef ) )
+olcObjectClasses: ( 2.16.840.1.113719.1.301.6.3.1 NAME 'krbService' SUP top ABSTRACT MUST cn MAY ( krbHostServer $ krbRealmReferences ) )
+olcObjectClasses: ( 2.16.840.1.113719.1.301.6.4.1 NAME 'krbKdcService' SUP krbService STRUCTURAL )
+olcObjectClasses: ( 2.16.840.1.113719.1.301.6.5.1 NAME 'krbPwdService' SUP krbService STRUCTURAL )
+olcObjectClasses: ( 2.16.840.1.113719.1.301.6.8.1 NAME 'krbPrincipalAux' SUP top AUXILIARY MAY ( krbPrincipalName $ krbCanonicalName $ krbUPEnabled $ krbPrincipalKey $ krbTicketPolicyReference $ krbPrincipalExpiration $ krbPasswordExpiration $ krbPwdPolicyReference $ krbPrincipalType $ krbPwdHistory $ krbLastPwdChange $ krbLastAdminUnlock $ krbPrincipalAliases $ krbLastSuccessfulAuth $ krbLastFailedAuth $ krbLoginFailedCount $ krbExtraData $ krbAllowedToDelegateTo $ krbPrincipalAuthInd ) )
+olcObjectClasses: ( 2.16.840.1.113719.1.301.6.9.1 NAME 'krbPrincipal' SUP top STRUCTURAL MUST krbPrincipalName MAY krbObjectReferences )
+olcObjectClasses: ( 2.16.840.1.113719.1.301.6.11.1 NAME 'krbPrincRefAux' SUP top AUXILIARY MAY krbPrincipalReferences )
+olcObjectClasses: ( 2.16.840.1.113719.1.301.6.13.1 NAME 'krbAdmService' SUP krbService STRUCTURAL )
+olcObjectClasses: ( 2.16.840.1.113719.1.301.6.14.1 NAME 'krbPwdPolicy' SUP top STRUCTURAL MUST cn MAY ( krbMaxPwdLife $ krbMinPwdLife $ krbPwdMinDiffChars $ krbPwdMinLength $ krbPwdHistoryLength $ krbPwdMaxFailure $ krbPwdFailureCountInterval $ krbPwdLockoutDuration $ krbPwdAttributes $ krbPwdMaxLife $ krbPwdMaxRenewableLife $ krbPwdAllowedKeysalts ) )
+olcObjectClasses: ( 2.16.840.1.113719.1.301.6.16.1 NAME 'krbTicketPolicyAux' SUP top AUXILIARY MAY ( krbTicketFlags $ krbMaxTicketLife $ krbMaxRenewableAge ) )
+olcObjectClasses: ( 2.16.840.1.113719.1.301.6.17.1 NAME 'krbTicketPolicy' SUP top STRUCTURAL MUST cn )
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/ldap_handle.c b/src/plugins/kdb/ldap/libkdb_ldap/ldap_handle.c
index 77d8f810dbad..2f5d3d9e03d2 100644
--- a/src/plugins/kdb/ldap/libkdb_ldap/ldap_handle.c
+++ b/src/plugins/kdb/ldap/libkdb_ldap/ldap_handle.c
@@ -30,62 +30,6 @@
#include "ldap_main.h"
-
-#ifdef ASYNC_BIND
-
-/*
- * Update the server info structure. In case of an asynchronous bind,
- * this function is called to check the bind status. A flag
- * server_info_upate_pending is refered before calling this function.
- * This function sets the server_status to either ON or OFF and
- * sets the server_info_udpate_pending to OFF.
- * Do not lock the mutex here. The caller should lock it
- */
-
-static krb5_error_code
-krb5_update_server_info(krb5_ldap_server_handle *ldap_server_handle,
- krb5_ldap_server_info *server_info)
-{
- krb5_error_code st=0;
- struct timeval ztime={0, 0};
- LDAPMessage *result=NULL;
-
- if (ldap_server_handle == NULL || server_info == NULL)
- return -1;
-
- while (st == 0) {
- st = ldap_result(ldap_server_handle->ldap_handle, ldap_server_handle->msgid,
- LDAP_MSG_ALL, &ztime, &result);
- switch (st) {
- case -1:
- server_info->server_status = OFF;
- time(&server_info->downtime);
- break;
-
- case 0:
- continue;
- break;
-
- case LDAP_RES_BIND:
- if ((st=ldap_result2error(ldap_server_handle->ldap_handle, result, 1)) == LDAP_SUCCESS) {
- server_info->server_status = ON;
- } else {
- server_info->server_status = OFF;
- time(&server_info->downtime);
- }
- ldap_msgfree(result);
- break;
- default:
- ldap_msgfree(result);
- continue;
- break;
- }
- }
- ldap_server_handle->server_info_update_pending = FALSE;
- return 0;
-}
-#endif
-
/*
* Return ldap server handle from the pool. If the pool is exhausted return NULL.
* Do not lock the mutex, caller should lock it
@@ -105,18 +49,6 @@ krb5_get_ldap_handle(krb5_ldap_context *ldap_context)
ldap_server_handle = ldap_server_info->ldap_server_handles;
ldap_server_info->ldap_server_handles = ldap_server_handle->next;
break;
-#ifdef ASYNC_BIND
- if (ldap_server_handle->server_info_update_pending == TRUE) {
- krb5_update_server_info(context, ldap_server_handle,
- ldap_server_info);
- }
-
- if (ldap_server_info->server_status == ON) {
- ldap_server_info->ldap_server_handles = ldap_server_handle->next;
- break;
- } else
- ldap_server_handle = NULL;
-#endif
}
}
++cnt;
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.c b/src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.c
index 32efc4f54ad0..5b9d1e9fa67c 100644
--- a/src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.c
+++ b/src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.c
@@ -1231,6 +1231,8 @@ krb5_ldap_policydn_to_name(krb5_context context, const char *policy_dn,
kdb5_dal_handle *dal_handle;
krb5_ldap_context *ldap_context;
const char *realmdn;
+ char *rdn;
+ LDAPDN dn;
*name_out = NULL;
SETUP_CONTEXT();
@@ -1248,46 +1250,22 @@ krb5_ldap_policydn_to_name(krb5_context context, const char *policy_dn,
if (policy_dn[plen] != ',' || strcmp(realmdn, policy_dn + plen + 1) != 0)
return EINVAL;
-#if defined HAVE_LDAP_STR2DN
- {
- char *rdn;
- LDAPDN dn;
-
- rdn = k5memdup0(policy_dn, plen, &ret);
- if (rdn == NULL)
- return ret;
- ret = ldap_str2dn(rdn, &dn, LDAP_DN_FORMAT_LDAPV3 | LDAP_DN_PEDANTIC);
- free(rdn);
- if (ret)
- return EINVAL;
- if (dn[0] == NULL || dn[1] != NULL ||
- dn[0][0]->la_attr.bv_len != 2 ||
- strncasecmp(dn[0][0]->la_attr.bv_val, "cn", 2) != 0) {
- ret = EINVAL;
- } else {
- *name_out = k5memdup0(dn[0][0]->la_value.bv_val,
- dn[0][0]->la_value.bv_len, &ret);
- }
- ldap_dnfree(dn);
+ rdn = k5memdup0(policy_dn, plen, &ret);
+ if (rdn == NULL)
return ret;
+ ret = ldap_str2dn(rdn, &dn, LDAP_DN_FORMAT_LDAPV3 | LDAP_DN_PEDANTIC);
+ free(rdn);
+ if (ret)
+ return EINVAL;
+ if (dn[0] == NULL || dn[1] != NULL || dn[0][0]->la_attr.bv_len != 2 ||
+ strncasecmp(dn[0][0]->la_attr.bv_val, "cn", 2) != 0) {
+ ret = EINVAL;
+ } else {
+ *name_out = k5memdup0(dn[0][0]->la_value.bv_val,
+ dn[0][0]->la_value.bv_len, &ret);
}
-#elif defined HAVE_LDAP_EXPLODE_DN
- {
- char **parsed_dn;
-
- /* 1 = return DN components without type prefix */
- parsed_dn = ldap_explode_dn(policy_dn, 1);
- if (parsed_dn == NULL)
- return EINVAL;
- *name_out = strdup(parsed_dn[0]);
- if (*name_out == NULL)
- return ENOMEM;
- ldap_value_free(parsed_dn);
- return 0;
- }
-#else
- return EINVAL;
-#endif
+ ldap_dnfree(dn);
+ return ret;
}
/* Compute the policy DN for the given policy name. */
@@ -1699,47 +1677,3 @@ cleanup:
free_princ_ent_contents(&princ_ent);
return ret;
}
-
-/* Solaris libldap does not provide the following functions which are in
- * OpenLDAP. */
-#ifndef HAVE_LDAP_INITIALIZE
-int
-ldap_initialize(LDAP **ldp, char *url)
-{
- int rc = 0;
- LDAP *ld = NULL;
- LDAPURLDesc *ludp = NULL;
-
- /*
- * For now, we don't use any DN that may be provided. And on Solaris
- * (based on Mozilla's LDAP client code), we need the _nodn form to parse
- * "ldap://host" without a trailing slash.
- *
- * Also, this version won't handle an input string which contains multiple
- * URLs, unlike the OpenLDAP ldap_initialize. See
- * https://bugzilla.mozilla.org/show_bug.cgi?id=353336#c1 .
- */
-#ifdef HAVE_LDAP_URL_PARSE_NODN
- rc = ldap_url_parse_nodn(url, &ludp);
-#else
- rc = ldap_url_parse(url, &ludp);
-#endif
- if (rc == 0) {
- ld = ldap_init(ludp->lud_host, ludp->lud_port);
- if (ld != NULL)
- *ldp = ld;
- else
- rc = KRB5_KDB_ACCESS_ERROR;
- ldap_free_urldesc(ludp);
- }
- return rc;
-}
-#endif /* HAVE_LDAP_INITIALIZE */
-
-#ifndef HAVE_LDAP_UNBIND_EXT_S
-int
-ldap_unbind_ext_s(LDAP *ld, LDAPControl **sctrls, LDAPControl **cctrls)
-{
- return ldap_unbind_ext(ld, sctrls, cctrls);
-}
-#endif /* HAVE_LDAP_UNBIND_EXT_S */
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c b/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c
index 7ba53f959ce4..88a17049503e 100644
--- a/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c
+++ b/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c
@@ -1734,7 +1734,7 @@ getstringtime(krb5_timestamp epochtime)
{
struct tm tme;
char *strtime=NULL;
- time_t posixtime = epochtime;
+ time_t posixtime = ts2tt(epochtime);
strtime = calloc (50, 1);
if (strtime == NULL)
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/ldap_tkt_policy.c b/src/plugins/kdb/ldap/libkdb_ldap/ldap_tkt_policy.c
index f5c6ab8cd376..4193b4adccae 100644
--- a/src/plugins/kdb/ldap/libkdb_ldap/ldap_tkt_policy.c
+++ b/src/plugins/kdb/ldap/libkdb_ldap/ldap_tkt_policy.c
@@ -431,7 +431,7 @@ krb5_ldap_list(krb5_context context, char ***list, char *objectclass,
{
char *filter=NULL, *dn=NULL;
krb5_error_code st=0, tempst=0;
- int i=0, count=0, filterlen=0;
+ int count=0, filterlen=0;
LDAP *ld=NULL;
LDAPMessage *result=NULL,*ent=NULL;
kdb5_dal_handle *dal_handle=NULL;
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/lockout.c b/src/plugins/kdb/ldap/libkdb_ldap/lockout.c
index 0fc56c2fe7bd..1088ecc5ad0b 100644
--- a/src/plugins/kdb/ldap/libkdb_ldap/lockout.c
+++ b/src/plugins/kdb/ldap/libkdb_ldap/lockout.c
@@ -93,7 +93,7 @@ locked_check_p(krb5_context context,
/* If the entry was unlocked since the last failure, it's not locked. */
if (krb5_dbe_lookup_last_admin_unlock(context, entry, &unlock_time) == 0 &&
- entry->last_failed <= unlock_time)
+ !ts_after(entry->last_failed, unlock_time))
return FALSE;
if (max_fail == 0 || entry->fail_auth_count < max_fail)
@@ -102,7 +102,7 @@ locked_check_p(krb5_context context,
if (lockout_duration == 0)
return TRUE; /* principal permanently locked */
- return (stamp < entry->last_failed + lockout_duration);
+ return ts_after(ts_incr(entry->last_failed, lockout_duration), stamp);
}
krb5_error_code
@@ -196,14 +196,14 @@ krb5_ldap_lockout_audit(krb5_context context,
status == KRB5KRB_AP_ERR_BAD_INTEGRITY)) {
if (krb5_dbe_lookup_last_admin_unlock(context, entry,
&unlock_time) == 0 &&
- entry->last_failed <= unlock_time) {
+ !ts_after(entry->last_failed, unlock_time)) {
/* Reset fail_auth_count after administrative unlock. */
entry->fail_auth_count = 0;
entry->mask |= KADM5_FAIL_AUTH_COUNT;
}
if (failcnt_interval != 0 &&
- stamp > entry->last_failed + failcnt_interval) {
+ ts_after(stamp, ts_incr(entry->last_failed, failcnt_interval))) {
/* Reset fail_auth_count after failcnt_interval */
entry->fail_auth_count = 0;
entry->mask |= KADM5_FAIL_AUTH_COUNT;
diff --git a/src/plugins/kdcpolicy/test/Makefile.in b/src/plugins/kdcpolicy/test/Makefile.in
new file mode 100644
index 000000000000..ea3484e13e64
--- /dev/null
+++ b/src/plugins/kdcpolicy/test/Makefile.in
@@ -0,0 +1,20 @@
+mydir=plugins$(S)kdcpolicy$(S)test
+BUILDTOP=$(REL)..$(S)..$(S)..
+
+LIBBASE=kdcpolicy_test
+LIBMAJOR=0
+LIBMINOR=0
+RELDIR=../plugins/kdcpolicy/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/kdcpolicy/test/deps b/src/plugins/kdcpolicy/test/deps
new file mode 100644
index 000000000000..4ecf533f3ff2
--- /dev/null
+++ b/src/plugins/kdcpolicy/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/kdb.h $(top_srcdir)/include/krb5.h \
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/kdcpolicy_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/kdcpolicy/test/kdcpolicy_test.exports b/src/plugins/kdcpolicy/test/kdcpolicy_test.exports
new file mode 100644
index 000000000000..9682ec74f75c
--- /dev/null
+++ b/src/plugins/kdcpolicy/test/kdcpolicy_test.exports
@@ -0,0 +1 @@
+kdcpolicy_test_initvt
diff --git a/src/plugins/kdcpolicy/test/main.c b/src/plugins/kdcpolicy/test/main.c
new file mode 100644
index 000000000000..86c808958dc5
--- /dev/null
+++ b/src/plugins/kdcpolicy/test/main.c
@@ -0,0 +1,111 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* include/krb5/kdcpolicy_plugin.h - KDC policy plugin interface */
+/*
+ * 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 "kdb.h"
+#include <krb5/kdcpolicy_plugin.h>
+
+static krb5_error_code
+output_from_indicator(const char *const *auth_indicators, int divisor,
+ krb5_deltat *lifetime_out,
+ krb5_deltat *renew_lifetime_out,
+ const char **status)
+{
+ if (auth_indicators[0] == NULL) {
+ *status = NULL;
+ return 0;
+ }
+
+ if (strcmp(auth_indicators[0], "ONE_HOUR") == 0) {
+ *lifetime_out = 3600 / divisor;
+ *renew_lifetime_out = *lifetime_out * 2;
+ return 0;
+ } else if (strcmp(auth_indicators[0], "SEVEN_HOURS") == 0) {
+ *lifetime_out = 7 * 3600 / divisor;
+ *renew_lifetime_out = *lifetime_out * 2;
+ return 0;
+ }
+
+ *status = "LOCAL_POLICY";
+ return KRB5KDC_ERR_POLICY;
+}
+
+static krb5_error_code
+test_check_as(krb5_context context, krb5_kdcpolicy_moddata moddata,
+ const krb5_kdc_req *request, const krb5_db_entry *client,
+ const krb5_db_entry *server, const char *const *auth_indicators,
+ const char **status, krb5_deltat *lifetime_out,
+ krb5_deltat *renew_lifetime_out)
+{
+ if (request->client != NULL && request->client->length >= 1 &&
+ data_eq_string(request->client->data[0], "fail")) {
+ *status = "LOCAL_POLICY";
+ return KRB5KDC_ERR_POLICY;
+ }
+ return output_from_indicator(auth_indicators, 1, lifetime_out,
+ renew_lifetime_out, status);
+}
+
+static krb5_error_code
+test_check_tgs(krb5_context context, krb5_kdcpolicy_moddata moddata,
+ const krb5_kdc_req *request, const krb5_db_entry *server,
+ const krb5_ticket *ticket, const char *const *auth_indicators,
+ const char **status, krb5_deltat *lifetime_out,
+ krb5_deltat *renew_lifetime_out)
+{
+ if (request->server != NULL && request->server->length >= 1 &&
+ data_eq_string(request->server->data[0], "fail")) {
+ *status = "LOCAL_POLICY";
+ return KRB5KDC_ERR_POLICY;
+ }
+ return output_from_indicator(auth_indicators, 2, lifetime_out,
+ renew_lifetime_out, status);
+}
+
+krb5_error_code
+kdcpolicy_test_initvt(krb5_context context, int maj_ver, int min_ver,
+ krb5_plugin_vtable vtable);
+krb5_error_code
+kdcpolicy_test_initvt(krb5_context context, int maj_ver, int min_ver,
+ krb5_plugin_vtable vtable)
+{
+ krb5_kdcpolicy_vtable vt;
+
+ if (maj_ver != 1)
+ return KRB5_PLUGIN_VER_NOTSUPP;
+
+ vt = (krb5_kdcpolicy_vtable)vtable;
+ vt->name = "test";
+ vt->check_as = test_check_as;
+ vt->check_tgs = test_check_tgs;
+ return 0;
+}
diff --git a/src/plugins/preauth/otp/main.c b/src/plugins/preauth/otp/main.c
index 2649e9a90d40..a1b681682405 100644
--- a/src/plugins/preauth/otp/main.c
+++ b/src/plugins/preauth/otp/main.c
@@ -331,7 +331,8 @@ otp_verify(krb5_context context, krb5_data *req_pkt, krb5_kdc_req *request,
/* Send the request. */
otp_state_verify((otp_state *)moddata, cb->event_context(context, rock),
- request->client, config, req, on_response, rs);
+ cb->client_name(context, rock), config, req, on_response,
+ rs);
cb->free_string(context, rock, config);
k5_free_pa_otp_req(context, req);
diff --git a/src/plugins/preauth/pkinit/Makefile.in b/src/plugins/preauth/pkinit/Makefile.in
index 3bb88d8e922f..d8b9398180ef 100644
--- a/src/plugins/preauth/pkinit/Makefile.in
+++ b/src/plugins/preauth/pkinit/Makefile.in
@@ -1,7 +1,6 @@
mydir=plugins$(S)preauth$(S)pkinit
BUILDTOP=$(REL)..$(S)..$(S)..
MODULE_INSTALL_DIR = $(KRB5_PA_MODULE_DIR)
-LOCALINCLUDES = $(PKINIT_CRYPTO_IMPL_CFLAGS)
LIBBASE=pkinit
LIBMAJOR=0
@@ -11,8 +10,7 @@ RELDIR=../plugins/preauth/pkinit
SHLIB_EXPDEPS = \
$(TOPLIBD)/libk5crypto$(SHLIBEXT) \
$(TOPLIBD)/libkrb5$(SHLIBEXT)
-SHLIB_EXPLIBS= -lkrb5 -lcom_err -lk5crypto $(PKINIT_CRYPTO_IMPL_LIBS) $(DL_LIB) $(SUPPORT_LIB) $(LIBS)
-DEFINES=-DPKINIT_DYNOBJEXT=\""$(PKINIT_DYNOBJEXT)"\"
+SHLIB_EXPLIBS= -lkrb5 -lcom_err -lk5crypto -lcrypto $(DL_LIB) $(SUPPORT_LIB) $(LIBS)
STLIBOBJS= \
pkinit_accessor.o \
@@ -23,7 +21,7 @@ STLIBOBJS= \
pkinit_profile.o \
pkinit_identity.o \
pkinit_matching.o \
- pkinit_crypto_$(PKINIT_CRYPTO_IMPL).o
+ pkinit_crypto_openssl.o
SRCS= \
$(srcdir)/pkinit_accessor.c \
@@ -35,7 +33,7 @@ SRCS= \
$(srcdir)/pkinit_profile.c \
$(srcdir)/pkinit_identity.c \
$(srcdir)/pkinit_matching.c \
- $(srcdir)/pkinit_crypto_$(PKINIT_CRYPTO_IMPL).c
+ $(srcdir)/pkinit_crypto_openssl.c
all-unix: all-liblinks
install-unix: install-libs
diff --git a/src/plugins/preauth/pkinit/deps b/src/plugins/preauth/pkinit/deps
index 75276b66491c..57116db1618c 100644
--- a/src/plugins/preauth/pkinit/deps
+++ b/src/plugins/preauth/pkinit/deps
@@ -20,11 +20,12 @@ pkinit_srv.so pkinit_srv.po $(OUTPRE)pkinit_srv.$(OBJEXT): \
$(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/clpreauth_plugin.h \
- $(top_srcdir)/include/krb5/kdcpreauth_plugin.h $(top_srcdir)/include/krb5/plugin.h \
- $(top_srcdir)/include/krb5/preauth_plugin.h $(top_srcdir)/include/port-sockets.h \
- $(top_srcdir)/include/socket-utils.h pkcs11.h pkinit.h \
- pkinit_accessor.h pkinit_crypto.h pkinit_srv.c pkinit_trace.h
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/certauth_plugin.h \
+ $(top_srcdir)/include/krb5/clpreauth_plugin.h $(top_srcdir)/include/krb5/kdcpreauth_plugin.h \
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/krb5/preauth_plugin.h \
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
+ pkcs11.h pkinit.h pkinit_accessor.h pkinit_crypto.h \
+ pkinit_srv.c pkinit_trace.h
pkinit_lib.so pkinit_lib.po $(OUTPRE)pkinit_lib.$(OBJEXT): \
$(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
$(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-int-pkinit.h \
diff --git a/src/plugins/preauth/pkinit/pkinit.h b/src/plugins/preauth/pkinit/pkinit.h
index 876db94c32f8..f3de9ad7a15d 100644
--- a/src/plugins/preauth/pkinit/pkinit.h
+++ b/src/plugins/preauth/pkinit/pkinit.h
@@ -73,6 +73,7 @@
#define KRB5_CONF_PKINIT_IDENTITIES "pkinit_identities"
#define KRB5_CONF_PKINIT_IDENTITY "pkinit_identity"
#define KRB5_CONF_PKINIT_KDC_HOSTNAME "pkinit_kdc_hostname"
+/* pkinit_kdc_ocsp has been removed */
#define KRB5_CONF_PKINIT_KDC_OCSP "pkinit_kdc_ocsp"
#define KRB5_CONF_PKINIT_POOL "pkinit_pool"
#define KRB5_CONF_PKINIT_REQUIRE_CRL_CHECKING "pkinit_require_crl_checking"
@@ -173,7 +174,6 @@ typedef struct _pkinit_identity_opts {
char **anchors;
char **intermediates;
char **crls;
- char *ocsp;
int idtype;
char *cert_filename;
char *key_filename;
@@ -209,6 +209,7 @@ struct _pkinit_req_context {
pkinit_identity_opts *idopts;
int do_identity_matching;
krb5_preauthtype pa_type;
+ int rfc4556_kdc;
int rfc6112_kdc;
int identity_initialized;
int identity_prompted;
@@ -292,6 +293,13 @@ krb5_error_code pkinit_cert_matching
pkinit_identity_crypto_context id_cryptoctx,
krb5_principal princ);
+krb5_error_code pkinit_client_cert_match
+ (krb5_context context,
+ pkinit_plg_crypto_context plgctx,
+ pkinit_req_crypto_context reqctx,
+ const char *match_rule,
+ krb5_boolean *matched);
+
/*
* Client's list of identities for which it needs PINs or passwords
*/
diff --git a/src/plugins/preauth/pkinit/pkinit_clnt.c b/src/plugins/preauth/pkinit/pkinit_clnt.c
index e73ad53e99df..f1bc6b21dc47 100644
--- a/src/plugins/preauth/pkinit/pkinit_clnt.c
+++ b/src/plugins/preauth/pkinit/pkinit_clnt.c
@@ -1175,15 +1175,22 @@ pkinit_client_process(krb5_context context, krb5_clpreauth_moddata moddata,
reqctx->rfc6112_kdc = 1;
return 0;
case KRB5_PADATA_PK_AS_REQ:
+ reqctx->rfc4556_kdc = 1;
pkiDebug("processing KRB5_PADATA_PK_AS_REQ\n");
processing_request = 1;
break;
case KRB5_PADATA_PK_AS_REP:
+ reqctx->rfc4556_kdc = 1;
pkiDebug("processing KRB5_PADATA_PK_AS_REP\n");
break;
case KRB5_PADATA_PK_AS_REP_OLD:
case KRB5_PADATA_PK_AS_REQ_OLD:
+ /* Don't fall back to draft9 code if the KDC supports RFC 4556. */
+ if (reqctx->rfc4556_kdc) {
+ TRACE_PKINIT_CLIENT_NO_DRAFT9(context);
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
if (in_padata->length == 0) {
pkiDebug("processing KRB5_PADATA_PK_AS_REQ_OLD\n");
in_padata->pa_type = KRB5_PADATA_PK_AS_REQ_OLD;
diff --git a/src/plugins/preauth/pkinit/pkinit_crypto.h b/src/plugins/preauth/pkinit/pkinit_crypto.h
index b483affed6f7..2d3733bbc8dd 100644
--- a/src/plugins/preauth/pkinit/pkinit_crypto.h
+++ b/src/plugins/preauth/pkinit/pkinit_crypto.h
@@ -59,9 +59,6 @@ enum cms_msg_types {
#define IDTYPE_PKCS11 3
#define IDTYPE_ENVVAR 4
#define IDTYPE_PKCS12 5
-#ifdef PKINIT_CRYPTO_IMPL_NSS
-#define IDTYPE_NSS 6
-#endif
/*
* ca/crl types
@@ -96,7 +93,6 @@ typedef struct _pkinit_cert_iter_info *pkinit_cert_iter_handle;
#define PKINIT_ITER_NO_MORE 0x11111111 /* XXX */
typedef struct _pkinit_cert_matching_data {
- pkinit_cert_handle ch; /* cert handle for this certificate */
char *subject_dn; /* rfc2253-style subject name string */
char *issuer_dn; /* rfc2253-style issuer name string */
unsigned int ku_bits; /* key usage information */
@@ -458,68 +454,38 @@ krb5_error_code crypto_free_cert_info
/*
- * Get number of certificates available after crypto_load_certs()
+ * Get a null-terminated list of certificate matching data objects for the
+ * certificates loaded in id_cryptoctx.
*/
-krb5_error_code crypto_cert_get_count
- (krb5_context context, /* IN */
- pkinit_plg_crypto_context plg_cryptoctx, /* IN */
- pkinit_req_crypto_context req_cryptoctx, /* IN */
- pkinit_identity_crypto_context id_cryptoctx, /* IN */
- int *cert_count); /* OUT */
-
-/*
- * Begin iteration over the certs loaded in crypto_load_certs()
- */
-krb5_error_code crypto_cert_iteration_begin
- (krb5_context context, /* IN */
- pkinit_plg_crypto_context plg_cryptoctx, /* IN */
- pkinit_req_crypto_context req_cryptoctx, /* IN */
- pkinit_identity_crypto_context id_cryptoctx, /* IN */
- pkinit_cert_iter_handle *iter_handle); /* OUT */
-
-/*
- * End iteration over the certs loaded in crypto_load_certs()
- */
-krb5_error_code crypto_cert_iteration_end
- (krb5_context context, /* IN */
- pkinit_cert_iter_handle iter_handle); /* IN */
-
-/*
- * Get next certificate handle
- */
-krb5_error_code crypto_cert_iteration_next
- (krb5_context context, /* IN */
- pkinit_cert_iter_handle iter_handle, /* IN */
- pkinit_cert_handle *cert_handle); /* OUT */
-
-/*
- * Release cert handle
- */
-krb5_error_code crypto_cert_release
- (krb5_context context, /* IN */
- pkinit_cert_handle cert_handle); /* IN */
+krb5_error_code
+crypto_cert_get_matching_data(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ pkinit_cert_matching_data ***md_out);
/*
- * Get certificate matching information
+ * Free a matching data object.
*/
-krb5_error_code crypto_cert_get_matching_data
- (krb5_context context, /* IN */
- pkinit_cert_handle cert_handle, /* IN */
- pkinit_cert_matching_data **ret_data); /* OUT */
+void
+crypto_cert_free_matching_data(krb5_context context,
+ pkinit_cert_matching_data *md);
/*
- * Free certificate information
+ * Free a list of matching data objects.
*/
-krb5_error_code crypto_cert_free_matching_data
- (krb5_context context, /* IN */
- pkinit_cert_matching_data *data); /* IN */
+void
+crypto_cert_free_matching_data_list(krb5_context context,
+ pkinit_cert_matching_data **matchdata);
/*
- * Make the given certificate "the chosen one"
+ * Choose one of the certificates loaded in idctx to use for PKINIT client
+ * operations. cred_index must be an index into the array of matching objects
+ * returned by crypto_cert_get_matching_data().
*/
-krb5_error_code crypto_cert_select
- (krb5_context context, /* IN */
- pkinit_cert_matching_data *data); /* IN */
+krb5_error_code
+crypto_cert_select(krb5_context context, pkinit_identity_crypto_context idctx,
+ size_t cred_index);
/*
* Select the default certificate as "the chosen one"
@@ -664,4 +630,14 @@ 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);
+
+krb5_error_code
+crypto_req_cert_matching_data(krb5_context context,
+ pkinit_plg_crypto_context plgctx,
+ pkinit_req_crypto_context reqctx,
+ pkinit_cert_matching_data **md_out);
+
#endif /* _PKINIT_CRYPTO_H */
diff --git a/src/plugins/preauth/pkinit/pkinit_crypto_nss.c b/src/plugins/preauth/pkinit/pkinit_crypto_nss.c
deleted file mode 100644
index c849f8736508..000000000000
--- a/src/plugins/preauth/pkinit/pkinit_crypto_nss.c
+++ /dev/null
@@ -1,5800 +0,0 @@
-/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-/*
- * Copyright (c) 2006,2007,2010,2011 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.
- *
- * * Neither the name of Red Hat, Inc., 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 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 OWNER
- * 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-platform.h"
-#include "k5-buf.h"
-#include "k5-utf8.h"
-#include "krb5.h"
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <dirent.h>
-#include <errno.h>
-
-#include <prerror.h>
-#include <prmem.h>
-#include <prprf.h>
-#include <nss.h>
-#include <cert.h>
-#include <certdb.h>
-#include <ciferfam.h>
-#include <cms.h>
-#include <keyhi.h>
-#include <nssb64.h>
-#include <ocsp.h>
-#include <p12plcy.h>
-#include <p12.h>
-#include <pk11pub.h>
-#include <pkcs12.h>
-#include <secerr.h>
-#include <secmodt.h>
-#include <secmod.h>
-#include <secoidt.h>
-#include <secoid.h>
-
-/* Avoid including our local copy of "pkcs11.h" from one of the local headers,
- * since the definitions we want to use are going to be the ones that NSS
- * provides. */
-
-#define PKCS11_H
-#include "pkinit.h"
-#include "pkinit_crypto.h"
-
-/* We should probably avoid using the default location for certificate trusts,
- * unless we can be sure that the list of trusted roots isn't being shared
- * with general-purpose SSL/TLS configuration, even though we're leaning on
- * SSL/TLS trust settings. */
-#define DEFAULT_CONFIGDIR "/etc/pki/nssdb"
-
-/* #define DEBUG_DER "/usr/lib64/nss/unsupported-tools/derdump" */
-/* #define DEBUG_SENSITIVE */
-
-/* Define to create a temporary on-disk database when we need to import PKCS12
- * identities. */
-#define PKCS12_HACK
-
-/* Prefix to mark the nicknames we make up for pkcs12 bundles that don't
- * include a friendly name. */
-#define PKCS12_PREFIX "pkinit-pkcs12"
-
-/* The library name of the NSSPEM module. */
-#define PEM_MODULE "nsspem"
-
-/* Forward declaration. */
-static krb5_error_code cert_retrieve_cert_sans(krb5_context context,
- CERTCertificate *cert,
- krb5_principal **pkinit_sans,
- krb5_principal **upn_sans,
- unsigned char ***kdc_hostname);
-static void crypto_update_signer_identity(krb5_context,
- pkinit_identity_crypto_context);
-
-/* DomainParameters: RFC 2459, 7.3.2. */
-struct domain_parameters {
- SECItem p, g, q, j;
- struct validation_parms *validation_parms;
-};
-
-/* Plugin and request state. */
-struct _pkinit_plg_crypto_context {
- PLArenaPool *pool;
- NSSInitContext *ncontext;
-};
-
-struct _pkinit_req_crypto_context {
- PLArenaPool *pool;
- SECKEYPrivateKey *client_dh_privkey; /* used by clients */
- SECKEYPublicKey *client_dh_pubkey; /* used by clients */
- struct domain_parameters client_dh_params; /* used by KDCs */
- CERTCertificate *peer_cert; /* the other party */
-};
-
-struct _pkinit_identity_crypto_context {
- PLArenaPool *pool;
- const char *identity;
- SECMODModule *pem_module; /* used for FILE: and DIR: */
- struct _pkinit_identity_crypto_module {
- char *name;
- char *spec;
- SECMODModule *module;
- } **id_modules; /* used for PKCS11: */
- struct _pkinit_identity_crypto_userdb {
- char *name;
- PK11SlotInfo *userdb;
- } **id_userdbs; /* used for NSS: */
- struct _pkinit_identity_crypto_p12slot {
- char *p12name;
- PK11SlotInfo *slot;
- } id_p12_slot; /* used for PKCS12: */
- struct _pkinit_identity_crypto_file {
- char *name;
- PK11GenericObject *obj;
- CERTCertificate *cert;
- } **id_objects; /* used with FILE: and DIR: */
- SECItem **id_crls;
- CERTCertList *id_certs, *ca_certs;
- CERTCertificate *id_cert;
- struct {
- krb5_context context;
- krb5_prompter_fct prompter;
- void *prompter_data;
- const char *identity;
- } pwcb_args;
- krb5_boolean defer_id_prompt;
- pkinit_deferred_id *deferred_ids;
- krb5_boolean defer_with_dummy_password;
-};
-
-struct _pkinit_cert_info { /* aka _pkinit_cert_handle */
- PLArenaPool *pool;
- struct _pkinit_identity_crypto_context *id_cryptoctx;
- CERTCertificate *cert;
-};
-
-struct _pkinit_cert_iter_info { /* aka _pkinit_cert_iter_handle */
- PLArenaPool *pool;
- struct _pkinit_identity_crypto_context *id_cryptoctx;
- CERTCertListNode *node;
-};
-
-/* Protocol elements that we need to encode or decode. */
-
-/* DH parameters: draft-ietf-cat-kerberos-pk-init-08.txt, 3.1.2.2. */
-struct dh_parameters {
- SECItem p, g, private_value_length;
-};
-static const SEC_ASN1Template dh_parameters_template[] = {
- {
- SEC_ASN1_SEQUENCE,
- 0,
- NULL,
- sizeof(struct dh_parameters),
- },
- {
- SEC_ASN1_INTEGER,
- offsetof(struct dh_parameters, p),
- &SEC_IntegerTemplate,
- sizeof(SECItem),
- },
- {
- SEC_ASN1_INTEGER,
- offsetof(struct dh_parameters, g),
- &SEC_IntegerTemplate,
- sizeof(SECItem),
- },
- {
- SEC_ASN1_INTEGER | SEC_ASN1_OPTIONAL,
- offsetof(struct dh_parameters, private_value_length),
- &SEC_IntegerTemplate,
- sizeof(SECItem),
- },
- {0, 0, NULL, 0}
-};
-
-/* ValidationParms: RFC 2459, 7.3.2. */
-struct validation_parms {
- SECItem seed, pgen_counter;
-};
-static const SEC_ASN1Template validation_parms_template[] = {
- {
- SEC_ASN1_SEQUENCE,
- 0,
- NULL,
- sizeof(struct validation_parms),
- },
- {
- SEC_ASN1_BIT_STRING,
- offsetof(struct validation_parms, seed),
- &SEC_BitStringTemplate,
- sizeof(SECItem),
- },
- {
- SEC_ASN1_INTEGER,
- offsetof(struct validation_parms, pgen_counter),
- &SEC_IntegerTemplate,
- sizeof(SECItem),
- },
- {0, 0, NULL, 0}
-};
-
-/* DomainParameters: RFC 2459, 7.3.2. */
-struct domain_parameters;
-static const SEC_ASN1Template domain_parameters_template[] = {
- {
- SEC_ASN1_SEQUENCE,
- 0,
- NULL,
- sizeof(struct domain_parameters),
- },
- {
- SEC_ASN1_INTEGER,
- offsetof(struct domain_parameters, p),
- &SEC_IntegerTemplate,
- sizeof(SECItem),
- },
- {
- SEC_ASN1_INTEGER,
- offsetof(struct domain_parameters, g),
- &SEC_IntegerTemplate,
- sizeof(SECItem),
- },
- {
- SEC_ASN1_INTEGER,
- offsetof(struct domain_parameters, q),
- &SEC_IntegerTemplate,
- sizeof(SECItem),
- },
- {
- SEC_ASN1_INTEGER | SEC_ASN1_OPTIONAL,
- offsetof(struct domain_parameters, j),
- &SEC_IntegerTemplate,
- sizeof(SECItem),
- },
- {
- SEC_ASN1_INLINE | SEC_ASN1_POINTER | SEC_ASN1_OPTIONAL,
- offsetof(struct domain_parameters, validation_parms),
- &validation_parms_template,
- sizeof(struct validation_parms *),
- },
- {0, 0, NULL, 0}
-};
-
-/* IssuerAndSerialNumber: RFC 3852, 10.2.4. */
-struct issuer_and_serial_number {
- SECItem issuer;
- SECItem serial;
-};
-static const SEC_ASN1Template issuer_and_serial_number_template[] = {
- {
- SEC_ASN1_SEQUENCE,
- 0,
- NULL,
- sizeof(struct issuer_and_serial_number),
- },
- {
- SEC_ASN1_ANY,
- offsetof(struct issuer_and_serial_number, issuer),
- &SEC_AnyTemplate,
- sizeof(SECItem),
- },
- {
- SEC_ASN1_INTEGER,
- offsetof(struct issuer_and_serial_number, serial),
- &SEC_IntegerTemplate,
- sizeof(SECItem),
- },
- {0, 0, NULL, 0}
-};
-
-/* KerberosString: RFC 4120, 5.2.1. */
-static const SEC_ASN1Template kerberos_string_template[] = {
- {
- SEC_ASN1_GENERAL_STRING,
- 0,
- NULL,
- sizeof(SECItem),
- }
-};
-
-/* Realm: RFC 4120, 5.2.2. */
-struct realm {
- SECItem name;
-};
-static const SEC_ASN1Template realm_template[] = {
- {
- SEC_ASN1_GENERAL_STRING,
- 0,
- NULL,
- sizeof(SECItem),
- }
-};
-
-/* PrincipalName: RFC 4120, 5.2.2. */
-static const SEC_ASN1Template sequence_of_kerberos_string_template[] = {
- {
- SEC_ASN1_SEQUENCE_OF,
- 0,
- &kerberos_string_template,
- 0,
- }
-};
-
-struct principal_name {
- SECItem name_type;
- SECItem **name_string;
-};
-static const SEC_ASN1Template principal_name_template[] = {
- {
- SEC_ASN1_SEQUENCE,
- 0,
- NULL,
- sizeof(struct principal_name),
- },
- {
- SEC_ASN1_CONTEXT_SPECIFIC | 0 | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT,
- offsetof(struct principal_name, name_type),
- &SEC_IntegerTemplate,
- sizeof(SECItem),
- },
- {
- SEC_ASN1_CONTEXT_SPECIFIC | 1 | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT,
- offsetof(struct principal_name, name_string),
- sequence_of_kerberos_string_template,
- sizeof(struct SECItem **),
- },
- {0, 0, NULL, 0},
-};
-
-/* KRB5PrincipalName: RFC 4556, 3.2.2. */
-struct kerberos_principal_name {
- SECItem realm;
- struct principal_name principal_name;
-};
-static const SEC_ASN1Template kerberos_principal_name_template[] = {
- {
- SEC_ASN1_SEQUENCE,
- 0,
- NULL,
- sizeof(struct kerberos_principal_name),
- },
- {
- SEC_ASN1_CONTEXT_SPECIFIC | 0 | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT,
- offsetof(struct kerberos_principal_name, realm),
- &realm_template,
- sizeof(struct realm),
- },
- {
- SEC_ASN1_CONTEXT_SPECIFIC | 1 | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT,
- offsetof(struct kerberos_principal_name, principal_name),
- &principal_name_template,
- sizeof(struct principal_name),
- },
- {0, 0, NULL, 0}
-};
-
-/* ContentInfo: RFC 3852, 3. */
-struct content_info {
- SECItem content_type, content;
-};
-static const SEC_ASN1Template content_info_template[] = {
- {
- SEC_ASN1_SEQUENCE,
- 0,
- NULL,
- sizeof(struct content_info),
- },
- {
- SEC_ASN1_OBJECT_ID,
- offsetof(struct content_info, content_type),
- &SEC_ObjectIDTemplate,
- sizeof(SECItem),
- },
- {
- SEC_ASN1_CONTEXT_SPECIFIC | 0 | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT,
- offsetof(struct content_info, content),
- &SEC_OctetStringTemplate,
- sizeof(SECItem),
- },
- {0, 0, NULL, 0}
-};
-
-/* OIDs. */
-static unsigned char oid_pkinit_key_purpose_client_bytes[] = {
- 0x2b, 0x06, 0x01, 0x05, 0x02, 0x03, 0x04
-};
-static SECItem pkinit_kp_client = {
- siDEROID,
- oid_pkinit_key_purpose_client_bytes,
- 7,
-};
-static unsigned char oid_pkinit_key_purpose_kdc_bytes[] = {
- 0x2b, 0x06, 0x01, 0x05, 0x02, 0x03, 0x05
-};
-static SECItem pkinit_kp_kdc = {
- siDEROID,
- oid_pkinit_key_purpose_kdc_bytes,
- 7,
-};
-static unsigned char oid_ms_sc_login_key_purpose_bytes[] = {
- 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x14, 0x02, 0x02
-};
-static SECItem pkinit_kp_mssclogin = {
- siDEROID,
- oid_ms_sc_login_key_purpose_bytes,
- 10,
-};
-static unsigned char oid_pkinit_name_type_principal_bytes[] = {
- 0x2b, 0x06, 0x01, 0x05, 0x02, 0x02
-};
-static SECItem pkinit_nt_principal = {
- siDEROID,
- oid_pkinit_name_type_principal_bytes,
- 6,
-};
-static unsigned char oid_pkinit_name_type_upn_bytes[] = {
- 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x14, 0x02, 0x03
-};
-static SECItem pkinit_nt_upn = {
- siDEROID,
- oid_pkinit_name_type_upn_bytes,
- 10,
-};
-
-static SECOidTag
-get_pkinit_data_auth_data_tag(void)
-{
- static unsigned char oid_pkinit_auth_data_bytes[] = {
- 0x2b, 0x06, 0x01, 0x05, 0x02, 0x03, 0x01
- };
- static SECOidData oid_pkinit_auth_data = {
- {
- siDEROID,
- oid_pkinit_auth_data_bytes,
- 7,
- },
- SEC_OID_UNKNOWN,
- "PKINIT Client Authentication Data",
- CKM_INVALID_MECHANISM,
- UNSUPPORTED_CERT_EXTENSION,
- };
- if (oid_pkinit_auth_data.offset == SEC_OID_UNKNOWN)
- oid_pkinit_auth_data.offset = SECOID_AddEntry(&oid_pkinit_auth_data);
- return oid_pkinit_auth_data.offset;
-}
-
-static SECOidTag
-get_pkinit_data_auth_data9_tag(void)
-{
- static unsigned char oid_pkinit_auth_data9_bytes[] =
- { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01 };
- static SECOidData oid_pkinit_auth_data9 = {
- {
- siDEROID,
- oid_pkinit_auth_data9_bytes,
- 9,
- },
- SEC_OID_UNKNOWN,
- "PKINIT Client Authentication Data (Draft 9)",
- CKM_INVALID_MECHANISM,
- UNSUPPORTED_CERT_EXTENSION,
- };
- if (oid_pkinit_auth_data9.offset == SEC_OID_UNKNOWN)
- oid_pkinit_auth_data9.offset = SECOID_AddEntry(&oid_pkinit_auth_data9);
- return oid_pkinit_auth_data9.offset;
-}
-
-static SECOidTag
-get_pkinit_data_rkey_data_tag(void)
-{
- static unsigned char oid_pkinit_rkey_data_bytes[] = {
- 0x2b, 0x06, 0x01, 0x05, 0x02, 0x03, 0x03
- };
- static SECOidData oid_pkinit_rkey_data = {
- {
- siDEROID,
- oid_pkinit_rkey_data_bytes,
- 7,
- },
- SEC_OID_UNKNOWN,
- "PKINIT Reply Key Data",
- CKM_INVALID_MECHANISM,
- UNSUPPORTED_CERT_EXTENSION,
- };
- if (oid_pkinit_rkey_data.offset == SEC_OID_UNKNOWN)
- oid_pkinit_rkey_data.offset = SECOID_AddEntry(&oid_pkinit_rkey_data);
- return oid_pkinit_rkey_data.offset;
-}
-
-static SECOidTag
-get_pkinit_data_dhkey_data_tag(void)
-{
- static unsigned char oid_pkinit_dhkey_data_bytes[] = {
- 0x2b, 0x06, 0x01, 0x05, 0x02, 0x03, 0x02
- };
- static SECOidData oid_pkinit_dhkey_data = {
- {
- siDEROID,
- oid_pkinit_dhkey_data_bytes,
- 7,
- },
- SEC_OID_UNKNOWN,
- "PKINIT DH Reply Key Data",
- CKM_INVALID_MECHANISM,
- UNSUPPORTED_CERT_EXTENSION,
- };
- if (oid_pkinit_dhkey_data.offset == SEC_OID_UNKNOWN)
- oid_pkinit_dhkey_data.offset = SECOID_AddEntry(&oid_pkinit_dhkey_data);
- return oid_pkinit_dhkey_data.offset;
-}
-
-static SECItem *
-get_oid_from_tag(SECOidTag tag)
-{
- SECOidData *data;
- data = SECOID_FindOIDByTag(tag);
- if (data != NULL)
- return &data->oid;
- else
- return NULL;
-}
-
-#ifdef DEBUG_DER
-static void
-derdump(unsigned char *data, unsigned int length)
-{
- FILE *p;
-
- p = popen(DEBUG_DER, "w");
- if (p != NULL) {
- fwrite(data, 1, length, p);
- pclose(p);
- }
-}
-#endif
-#ifdef DEBUG_CMS
-static void
-cmsdump(unsigned char *data, unsigned int length)
-{
- FILE *p;
-
- p = popen(DEBUG_CMS, "w");
- if (p != NULL) {
- fwrite(data, 1, length, p);
- pclose(p);
- }
-}
-#endif
-
-/* A password-prompt callback for NSS that calls the libkrb5 callback. */
-static char *
-crypto_pwfn(const char *what, PRBool is_hardware, CK_FLAGS token_flags,
- PRBool retry, void *arg)
-{
- int ret;
- pkinit_identity_crypto_context id;
- krb5_prompt prompt;
- krb5_prompt_type prompt_types[2];
- krb5_data reply;
- char *text, *answer;
- const char *warning, *password;
- size_t text_size;
- void *data;
-
- /* We only want to be called once. */
- if (retry)
- return NULL;
- /* We need our callback arguments. */
- if (arg == NULL)
- return NULL;
- id = arg;
-
- /* If we need to warn about the PIN, figure out the text. */
- if (token_flags & CKF_USER_PIN_LOCKED)
- warning = "PIN locked";
- else if (token_flags & CKF_USER_PIN_FINAL_TRY)
- warning = "PIN final try";
- else if (token_flags & CKF_USER_PIN_COUNT_LOW)
- warning = "PIN count low";
- else
- warning = NULL;
-
- /*
- * If we have the name of an identity here, then we're either supposed to
- * save its name, or attempt to use a password, if one was supplied.
- */
- if (id->pwcb_args.identity != NULL) {
- if (id->defer_id_prompt) {
- /* If we're in the defer-prompts step, just save the identity name
- * and "fail". */
- if (!is_hardware)
- token_flags = 0;
- pkinit_set_deferred_id(&id->deferred_ids, id->pwcb_args.identity,
- token_flags, NULL);
- if (id->defer_with_dummy_password) {
- /* Return a useless result. */
- answer = PR_Malloc(1);
- if (answer != NULL) {
- *answer = '\0';
- return answer;
- }
- }
- } else {
- /* Check if we already have a password for this identity. If so,
- * just return a copy of it. */
- password = pkinit_find_deferred_id(id->deferred_ids,
- id->pwcb_args.identity);
- if (password != NULL) {
- /* The result will be freed with PR_Free, so return a copy. */
- text_size = strlen(password) + 1;
- answer = PR_Malloc(text_size);
- if (answer != NULL) {
- memcpy(answer, password, text_size);
- pkiDebug("%s: returning %ld-char answer\n", __FUNCTION__,
- (long)strlen(answer));
- return answer;
- }
- }
- }
- }
-
- if (id->pwcb_args.prompter == NULL)
- return NULL;
-
- /* Set up the prompt. */
- text_size = strlen(what) + 100;
- text = PORT_ArenaZAlloc(id->pool, text_size);
- if (text == NULL) {
- pkiDebug("out of memory");
- return NULL;
- }
- if (is_hardware) {
- if (warning != NULL)
- snprintf(text, text_size, "%s PIN (%s)", what, warning);
- else
- snprintf(text, text_size, "%s PIN", what);
- } else {
- snprintf(text, text_size, "%s %s", _("Pass phrase for"), what);
- }
- memset(&prompt, 0, sizeof(prompt));
- prompt.prompt = text;
- prompt.hidden = 1;
- prompt.reply = &reply;
- reply.length = 256;
- data = malloc(reply.length);
- reply.data = data;
- what = NULL;
- answer = NULL;
-
- /* Call the prompter callback. */
- prompt_types[0] = KRB5_PROMPT_TYPE_PREAUTH;
- prompt_types[1] = 0;
- (*k5int_set_prompt_types)(id->pwcb_args.context, prompt_types);
- fflush(NULL);
- ret = (*id->pwcb_args.prompter)(id->pwcb_args.context,
- id->pwcb_args.prompter_data,
- what, answer, 1, &prompt);
- (*k5int_set_prompt_types)(id->pwcb_args.context, NULL);
- answer = NULL;
- if ((ret == 0) && (reply.data != NULL)) {
- /* The result will be freed with PR_Free, so return a copy. */
- answer = PR_Malloc(reply.length + 1);
- memcpy(answer, reply.data, reply.length);
- answer[reply.length] = '\0';
- answer[strcspn(answer, "\r\n")] = '\0';
-#ifdef DEBUG_SENSITIVE
- pkiDebug("%s: returning \"%s\"\n", __FUNCTION__, answer);
-#else
- pkiDebug("%s: returning %ld-char answer\n", __FUNCTION__,
- (long) strlen(answer));
-#endif
- }
-
- if (reply.data == data)
- free(reply.data);
-
- return answer;
-}
-
-/* A password-prompt callback for NSS that calls the libkrb5 callback. */
-static char *
-crypto_pwcb(PK11SlotInfo *slot, PRBool retry, void *arg)
-{
- pkinit_identity_crypto_context id;
- const char *what = NULL;
- CK_TOKEN_INFO tinfo;
- CK_FLAGS tflags;
-
- if (PK11_GetTokenInfo(slot, &tinfo) == SECSuccess)
- tflags = tinfo.flags;
- else
- tflags = 0;
- if (arg != NULL) {
- id = arg;
- what = id->pwcb_args.identity;
- }
- return crypto_pwfn((what != NULL) ? what : PK11_GetTokenName(slot),
- PK11_IsHW(slot), tflags, retry, arg);
-}
-
-/*
- * Make sure we're using our callback, and set up the callback data.
- */
-static void *
-crypto_pwcb_prep(pkinit_identity_crypto_context id_cryptoctx,
- const char *identity, krb5_context context)
-{
- PK11_SetPasswordFunc(crypto_pwcb);
- id_cryptoctx->pwcb_args.context = context;
- id_cryptoctx->pwcb_args.identity = identity;
- return id_cryptoctx;
-}
-
-krb5_error_code
-pkinit_init_identity_crypto(pkinit_identity_crypto_context *id_cryptoctx)
-{
- PLArenaPool *pool;
- pkinit_identity_crypto_context id;
-
- pkiDebug("%s\n", __FUNCTION__);
- pool = PORT_NewArena(sizeof(double));
- if (pool == NULL)
- return ENOMEM;
- id = PORT_ArenaZAlloc(pool, sizeof(*id));
- if (id == NULL) {
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
- id->pool = pool;
- id->id_certs = CERT_NewCertList();
- id->ca_certs = CERT_NewCertList();
- if ((id->id_certs != NULL) && (id->ca_certs != NULL)) {
- *id_cryptoctx = id;
- return 0;
- }
- if (id->ca_certs != NULL)
- CERT_DestroyCertList(id->ca_certs);
- if (id->id_certs != NULL)
- CERT_DestroyCertList(id->id_certs);
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
-}
-
-/* Return the slot which we'll use for holding imported PKCS12 certificates
- * and keys. Open the module if we need to, first. */
-static PK11SlotInfo *
-crypto_get_p12_slot(struct _pkinit_identity_crypto_context *id)
-{
- char *configdir, *spec;
- size_t spec_size;
- int attempts;
-
- if (id->id_p12_slot.slot == NULL) {
- configdir = DEFAULT_CONFIGDIR;
-#ifdef PKCS12_HACK
- /* Figure out where to put the temporary userdb. */
- attempts = 0;
- while ((attempts < TMP_MAX) &&
- (spec = tempnam(NULL, "pk12-")) != NULL) {
- if (spec != NULL) {
- if (mkdir(spec, S_IRWXU) == 0) {
- configdir = spec;
- break;
- } else {
- free(spec);
- if (errno != EEXIST)
- break;
- }
- attempts++;
- }
- }
-#endif
- spec_size = strlen("configDir='' flags=readOnly") +
- strlen(configdir) + 1;
- spec = PORT_ArenaZAlloc(id->pool, spec_size);
- if (spec != NULL) {
- if (strcmp(configdir, DEFAULT_CONFIGDIR) != 0)
- snprintf(spec, spec_size, "configDir='%s'", configdir);
- else
- snprintf(spec, spec_size, "configDir='%s' flags=readOnly",
- configdir);
- id->id_p12_slot.slot = SECMOD_OpenUserDB(spec);
- }
-#ifdef PKCS12_HACK
- if (strcmp(configdir, DEFAULT_CONFIGDIR) != 0) {
- DIR *dir;
- struct dirent *ent;
- char *path;
- /* First, initialize the slot. */
- if (id->id_p12_slot.slot != NULL)
- if (PK11_NeedUserInit(id->id_p12_slot.slot))
- PK11_InitPin(id->id_p12_slot.slot, "", "");
- /* Scan the directory, deleting all of the contents. */
- dir = opendir(configdir);
- if (dir == NULL)
- pkiDebug("%s: error removing directory \"%s\": %s\n",
- __FUNCTION__, configdir, strerror(errno));
- else {
- while ((ent = readdir(dir)) != NULL) {
- if ((strcmp(ent->d_name, ".") == 0) ||
- (strcmp(ent->d_name, "..") == 0)) {
- continue;
- }
- if (k5_path_join(configdir, ent->d_name, &path) == 0) {
- remove(path);
- free(path);
- }
- }
- closedir(dir);
- }
- /* Remove the directory itself. */
- rmdir(configdir);
- free(configdir);
- }
- }
-#endif
- return id->id_p12_slot.slot;
-}
-
-/* Close the slot which we've been using for holding imported PKCS12
- * certificates and keys. */
-static void
-crypto_close_p12_slot(struct _pkinit_identity_crypto_context *id)
-{
- PK11_FreeSlot(id->id_p12_slot.slot);
- id->id_p12_slot.slot = NULL;
-}
-
-void
-pkinit_fini_identity_crypto(pkinit_identity_crypto_context id_cryptoctx)
-{
- int i;
-
- pkiDebug("%s\n", __FUNCTION__);
- /* The order of cleanup here is intended to ensure that nothing gets
- * freed before anything that might have a reference to it. */
- if (id_cryptoctx->deferred_ids != NULL)
- pkinit_free_deferred_ids(id_cryptoctx->deferred_ids);
- if (id_cryptoctx->id_cert != NULL)
- CERT_DestroyCertificate(id_cryptoctx->id_cert);
- CERT_DestroyCertList(id_cryptoctx->ca_certs);
- CERT_DestroyCertList(id_cryptoctx->id_certs);
- if (id_cryptoctx->id_objects != NULL)
- for (i = 0; id_cryptoctx->id_objects[i] != NULL; i++) {
- if (id_cryptoctx->id_objects[i]->cert != NULL)
- CERT_DestroyCertificate(id_cryptoctx->id_objects[i]->cert);
- PK11_DestroyGenericObject(id_cryptoctx->id_objects[i]->obj);
- }
- if (id_cryptoctx->id_p12_slot.slot != NULL)
- crypto_close_p12_slot(id_cryptoctx);
- if (id_cryptoctx->id_userdbs != NULL)
- for (i = 0; id_cryptoctx->id_userdbs[i] != NULL; i++)
- PK11_FreeSlot(id_cryptoctx->id_userdbs[i]->userdb);
- if (id_cryptoctx->id_modules != NULL) {
- for (i = 0; id_cryptoctx->id_modules[i] != NULL; i++) {
- if (id_cryptoctx->id_modules[i]->module != NULL)
- SECMOD_DestroyModule(id_cryptoctx->id_modules[i]->module);
- }
- }
- if (id_cryptoctx->id_crls != NULL)
- for (i = 0; id_cryptoctx->id_crls[i] != NULL; i++)
- CERT_UncacheCRL(CERT_GetDefaultCertDB(), id_cryptoctx->id_crls[i]);
- if (id_cryptoctx->pem_module != NULL)
- SECMOD_DestroyModule(id_cryptoctx->pem_module);
- PORT_FreeArena(id_cryptoctx->pool, PR_TRUE);
-}
-
-static SECStatus
-crypto_register_any(SECOidTag tag)
-{
- if (NSS_CMSType_RegisterContentType(tag,
- NULL,
- 0,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL, NULL, PR_TRUE) != SECSuccess)
- return ENOMEM;
- return 0;
-}
-
-krb5_error_code
-pkinit_init_plg_crypto(pkinit_plg_crypto_context *plg_cryptoctx)
-{
- PLArenaPool *pool;
- SECOidTag tag;
-
- pkiDebug("%s\n", __FUNCTION__);
- pool = PORT_NewArena(sizeof(double));
- if (pool != NULL) {
- *plg_cryptoctx = PORT_ArenaZAlloc(pool, sizeof(**plg_cryptoctx));
- if (*plg_cryptoctx != NULL) {
- (*plg_cryptoctx)->pool = pool;
- (*plg_cryptoctx)->ncontext = NSS_InitContext(DEFAULT_CONFIGDIR,
- NULL,
- NULL,
- NULL,
- NULL,
- NSS_INIT_READONLY |
- NSS_INIT_NOCERTDB |
- NSS_INIT_NOMODDB |
- NSS_INIT_FORCEOPEN |
- NSS_INIT_NOROOTINIT |
- NSS_INIT_PK11RELOAD);
- if ((*plg_cryptoctx)->ncontext != NULL) {
- tag = get_pkinit_data_auth_data9_tag();
- if (crypto_register_any(tag) != SECSuccess) {
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
- tag = get_pkinit_data_auth_data_tag();
- if (crypto_register_any(tag) != SECSuccess) {
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
- tag = get_pkinit_data_rkey_data_tag();
- if (crypto_register_any(tag) != SECSuccess) {
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
- tag = get_pkinit_data_dhkey_data_tag();
- if (crypto_register_any(tag) != SECSuccess) {
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
- return 0;
- }
- }
- PORT_FreeArena(pool, PR_TRUE);
- }
- return ENOMEM;
-}
-
-void
-pkinit_fini_plg_crypto(pkinit_plg_crypto_context plg_cryptoctx)
-{
- pkiDebug("%s\n", __FUNCTION__);
- if (plg_cryptoctx == NULL)
- return;
- if (NSS_ShutdownContext(plg_cryptoctx->ncontext) != SECSuccess)
- pkiDebug("%s: error shutting down context\n", __FUNCTION__);
- PORT_FreeArena(plg_cryptoctx->pool, PR_TRUE);
-}
-
-krb5_error_code
-pkinit_init_req_crypto(pkinit_req_crypto_context *req_cryptoctx)
-{
- PLArenaPool *pool;
-
- pkiDebug("%s\n", __FUNCTION__);
- pool = PORT_NewArena(sizeof(double));
- if (pool != NULL) {
- *req_cryptoctx = PORT_ArenaZAlloc(pool, sizeof(**req_cryptoctx));
- if (*req_cryptoctx != NULL) {
- (*req_cryptoctx)->pool = pool;
- return 0;
- }
- PORT_FreeArena(pool, PR_TRUE);
- }
- return ENOMEM;
-}
-
-void
-pkinit_fini_req_crypto(pkinit_req_crypto_context req_cryptoctx)
-{
- pkiDebug("%s\n", __FUNCTION__);
- if (req_cryptoctx->client_dh_privkey != NULL)
- SECKEY_DestroyPrivateKey(req_cryptoctx->client_dh_privkey);
- if (req_cryptoctx->client_dh_pubkey != NULL)
- SECKEY_DestroyPublicKey(req_cryptoctx->client_dh_pubkey);
- if (req_cryptoctx->peer_cert != NULL)
- CERT_DestroyCertificate(req_cryptoctx->peer_cert);
- PORT_FreeArena(req_cryptoctx->pool, PR_TRUE);
-}
-
-/* Duplicate the memory from the SECItem into a malloc()d buffer. */
-static int
-secitem_to_buf_len(SECItem *item, unsigned char **out, unsigned int *len)
-{
- *out = malloc(item->len);
- if (*out == NULL)
- return ENOMEM;
- memcpy(*out, item->data, item->len);
- *len = item->len;
- return 0;
-}
-
-/* Encode the raw buffer as an unsigned integer. If the first byte in the
- * buffer has its high bit set, we need to prepend a zero byte to make sure it
- * isn't treated as a negative value. */
-static int
-secitem_to_dh_pubval(SECItem *item, unsigned char **out, unsigned int *len)
-{
- PLArenaPool *pool;
- SECItem *uval, uinteger;
- int i;
-
- pool = PORT_NewArena(sizeof(double));
- if (pool == NULL)
- return ENOMEM;
-
- if (item->data[0] & 0x80) {
- uval = SECITEM_AllocItem(pool, NULL, item->len + 1);
- if (uval == NULL) {
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
- uval->data[0] = '\0';
- memcpy(uval->data + 1, item->data, item->len);
- } else {
- uval = item;
- }
-
- memset(&uinteger, 0, sizeof(uinteger));
- if (SEC_ASN1EncodeItem(pool, &uinteger, uval,
- SEC_ASN1_GET(SEC_IntegerTemplate)) != &uinteger) {
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
-
- i = secitem_to_buf_len(&uinteger, out, len);
-
- PORT_FreeArena(pool, PR_TRUE);
- return i;
-}
-
-/* Decode a DER unsigned integer, and return just the bits that make up that
- * integer. */
-static int
-secitem_from_dh_pubval(PLArenaPool *pool,
- unsigned char *dh_pubkey, unsigned int dh_pubkey_len,
- SECItem *bits_out)
-{
- SECItem tmp;
-
- tmp.data = dh_pubkey;
- tmp.len = dh_pubkey_len;
- memset(bits_out, 0, sizeof(*bits_out));
- if (SEC_ASN1DecodeItem(pool, bits_out,
- SEC_ASN1_GET(SEC_IntegerTemplate),
- &tmp) != SECSuccess)
- return ENOMEM;
- return 0;
-}
-
-/* Load the contents of a file into a SECitem. If it looks like a PEM-wrapped
- * item, maybe try to undo the base64 encoding. */
-enum secitem_from_file_type {
- secitem_from_file_plain,
- secitem_from_file_decode
-};
-static int
-secitem_from_file(PLArenaPool *pool, const char *filename,
- enum secitem_from_file_type secitem_from_file_type,
- SECItem *item_out)
-{
- SECItem tmp, *decoded;
- struct stat st;
- int fd, i, n;
- const char *encoded, *p;
- char *what, *q;
-
- memset(item_out, 0, sizeof(*item_out));
- fd = open(filename, O_RDONLY);
- if (fd == -1)
- return errno;
- if (fstat(fd, &st) == -1) {
- i = errno;
- close(fd);
- return i;
- }
- memset(&tmp, 0, sizeof(tmp));
- tmp.data = PORT_ArenaZAlloc(pool, st.st_size + 1);
- if (tmp.data == NULL) {
- close(fd);
- return ENOMEM;
- }
- n = 0;
- while (n < st.st_size) {
- i = read(fd, tmp.data + n, st.st_size - n);
- if (i <= 0)
- break;
- n += i;
- }
- close(fd);
- if (n < st.st_size)
- return ENOMEM;
- tmp.data[n] = '\0';
- tmp.len = n;
- encoded = (const char *) tmp.data;
- if ((secitem_from_file_type == secitem_from_file_decode) &&
- (tmp.len > 11) &&
- ((strncmp(encoded, "-----BEGIN ", 11) == 0) ||
- ((encoded = strstr((char *)tmp.data, "\n-----BEGIN")) != NULL))) {
- if (encoded[0] == '\n')
- encoded++;
- /* find the beginning of the next line */
- p = encoded;
- p += strcspn(p, "\r\n");
- p += strspn(p, "\r\n");
- q = NULL;
- what = PORT_ArenaZAlloc(pool, p - (encoded + 2) + 1);
- if (what != NULL) {
- /* construct the matching end-of-item and look for it */
- memcpy(what, "-----END ", 9);
- memcpy(what + 9, encoded + 11, p - (encoded + 11));
- what[p - (encoded + 2)] = '\0';
- q = strstr(p, what);
- }
- if (q != NULL) {
- *q = '\0';
- decoded = NSSBase64_DecodeBuffer(pool, NULL, p, q - p);
- if (decoded != NULL)
- tmp = *decoded;
- }
- }
- *item_out = tmp;
- return 0;
-}
-
-static struct oakley_group
-{
- int identifier;
- int bits; /* shortest prime first, so that a
- * sequential search for a set with a
- * length that exceeds the minimum will
- * find the entry with the shortest
- * suitable prime */
- char name[32];
- char prime[4096]; /* large enough to hold that prime */
- long generator; /* note: oakley_parse_group() assumes that this
- * number fits into a long */
- char subprime[4096]; /* large enough to hold its subprime
- * ((p-1)/2) */
-} oakley_groups[] = {
- {
- 1, 768,
- "Oakley MODP Group 1",
- "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1"
- "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD"
- "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245"
- "E485B576 625E7EC6 F44C42E9 A63A3620 FFFFFFFF FFFFFFFF",
- 2,
- "7FFFFFFF FFFFFFFF E487ED51 10B4611A 62633145 C06E0E68"
- "94812704 4533E63A 0105DF53 1D89CD91 28A5043C C71A026E"
- "F7CA8CD9 E69D218D 98158536 F92F8A1B A7F09AB6 B6A8E122"
- "F242DABB 312F3F63 7A262174 D31D1B10 7FFFFFFF FFFFFFFF",
- },
- {
- 2, 1024,
- "Oakley MODP Group 2",
- "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1"
- "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD"
- "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245"
- "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED"
- "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE65381"
- "FFFFFFFF FFFFFFFF",
- 2,
- "7FFFFFFF FFFFFFFF E487ED51 10B4611A 62633145 C06E0E68"
- "94812704 4533E63A 0105DF53 1D89CD91 28A5043C C71A026E"
- "F7CA8CD9 E69D218D 98158536 F92F8A1B A7F09AB6 B6A8E122"
- "F242DABB 312F3F63 7A262174 D31BF6B5 85FFAE5B 7A035BF6"
- "F71C35FD AD44CFD2 D74F9208 BE258FF3 24943328 F67329C0"
- "FFFFFFFF FFFFFFFF",
- },
- {
- 5, 1536,
- "Oakley MODP Group 5",
- "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1"
- "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD"
- "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245"
- "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED"
- "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D"
- "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F"
- "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D"
- "670C354E 4ABC9804 F1746C08 CA237327 FFFFFFFF FFFFFFFF",
- 2,
- "7FFFFFFF FFFFFFFF E487ED51 10B4611A 62633145 C06E0E68"
- "94812704 4533E63A 0105DF53 1D89CD91 28A5043C C71A026E"
- "F7CA8CD9 E69D218D 98158536 F92F8A1B A7F09AB6 B6A8E122"
- "F242DABB 312F3F63 7A262174 D31BF6B5 85FFAE5B 7A035BF6"
- "F71C35FD AD44CFD2 D74F9208 BE258FF3 24943328 F6722D9E"
- "E1003E5C 50B1DF82 CC6D241B 0E2AE9CD 348B1FD4 7E9267AF"
- "C1B2AE91 EE51D6CB 0E3179AB 1042A95D CF6A9483 B84B4B36"
- "B3861AA7 255E4C02 78BA3604 6511B993 FFFFFFFF FFFFFFFF",
- },
- {
- 14, 2048,
- "Oakley MODP Group 14",
- "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1"
- "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD"
- "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245"
- "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED"
- "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D"
- "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F"
- "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D"
- "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B"
- "E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9"
- "DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510"
- "15728E5A 8AACAA68 FFFFFFFF FFFFFFFF",
- 2,
- "7FFFFFFF FFFFFFFF E487ED51 10B4611A 62633145 C06E0E68"
- "94812704 4533E63A 0105DF53 1D89CD91 28A5043C C71A026E"
- "F7CA8CD9 E69D218D 98158536 F92F8A1B A7F09AB6 B6A8E122"
- "F242DABB 312F3F63 7A262174 D31BF6B5 85FFAE5B 7A035BF6"
- "F71C35FD AD44CFD2 D74F9208 BE258FF3 24943328 F6722D9E"
- "E1003E5C 50B1DF82 CC6D241B 0E2AE9CD 348B1FD4 7E9267AF"
- "C1B2AE91 EE51D6CB 0E3179AB 1042A95D CF6A9483 B84B4B36"
- "B3861AA7 255E4C02 78BA3604 650C10BE 19482F23 171B671D"
- "F1CF3B96 0C074301 CD93C1D1 7603D147 DAE2AEF8 37A62964"
- "EF15E5FB 4AAC0B8C 1CCAA4BE 754AB572 8AE9130C 4C7D0288"
- "0AB9472D 45565534 7FFFFFFF FFFFFFFF",
- },
- {
- 15, 3072,
- "Oakley MODP Group 15",
- "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1"
- "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD"
- "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245"
- "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED"
- "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D"
- "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F"
- "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D"
- "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B"
- "E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9"
- "DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510"
- "15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64"
- "ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7"
- "ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B"
- "F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C"
- "BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31"
- "43DB5BFC E0FD108E 4B82D120 A93AD2CA FFFFFFFF FFFFFFFF",
- 2,
- "7FFFFFFF FFFFFFFF E487ED51 10B4611A 62633145 C06E0E68"
- "94812704 4533E63A 0105DF53 1D89CD91 28A5043C C71A026E"
- "F7CA8CD9 E69D218D 98158536 F92F8A1B A7F09AB6 B6A8E122"
- "F242DABB 312F3F63 7A262174 D31BF6B5 85FFAE5B 7A035BF6"
- "F71C35FD AD44CFD2 D74F9208 BE258FF3 24943328 F6722D9E"
- "E1003E5C 50B1DF82 CC6D241B 0E2AE9CD 348B1FD4 7E9267AF"
- "C1B2AE91 EE51D6CB 0E3179AB 1042A95D CF6A9483 B84B4B36"
- "B3861AA7 255E4C02 78BA3604 650C10BE 19482F23 171B671D"
- "F1CF3B96 0C074301 CD93C1D1 7603D147 DAE2AEF8 37A62964"
- "EF15E5FB 4AAC0B8C 1CCAA4BE 754AB572 8AE9130C 4C7D0288"
- "0AB9472D 45556216 D6998B86 82283D19 D42A90D5 EF8E5D32"
- "767DC282 2C6DF785 457538AB AE83063E D9CB87C2 D370F263"
- "D5FAD746 6D8499EB 8F464A70 2512B0CE E771E913 0D697735"
- "F897FD03 6CC50432 6C3B0139 9F643532 290F958C 0BBD9006"
- "5DF08BAB BD30AEB6 3B84C460 5D6CA371 047127D0 3A72D598"
- "A1EDADFE 707E8847 25C16890 549D6965 7FFFFFFF FFFFFFFF",
- },
- {
- 16, 4096,
- "Oakley MODP Group 16",
- "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1"
- "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD"
- "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245"
- "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED"
- "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D"
- "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F"
- "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D"
- "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B"
- "E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9"
- "DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510"
- "15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64"
- "ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7"
- "ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B"
- "F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C"
- "BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31"
- "43DB5BFC E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7"
- "88719A10 BDBA5B26 99C32718 6AF4E23C 1A946834 B6150BDA"
- "2583E9CA 2AD44CE8 DBBBC2DB 04DE8EF9 2E8EFC14 1FBECAA6"
- "287C5947 4E6BC05D 99B2964F A090C3A2 233BA186 515BE7ED"
- "1F612970 CEE2D7AF B81BDD76 2170481C D0069127 D5B05AA9"
- "93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34063199"
- "FFFFFFFF FFFFFFFF",
- 2,
- "7FFFFFFF FFFFFFFF E487ED51 10B4611A 62633145 C06E0E68"
- "94812704 4533E63A 0105DF53 1D89CD91 28A5043C C71A026E"
- "F7CA8CD9 E69D218D 98158536 F92F8A1B A7F09AB6 B6A8E122"
- "F242DABB 312F3F63 7A262174 D31BF6B5 85FFAE5B 7A035BF6"
- "F71C35FD AD44CFD2 D74F9208 BE258FF3 24943328 F6722D9E"
- "E1003E5C 50B1DF82 CC6D241B 0E2AE9CD 348B1FD4 7E9267AF"
- "C1B2AE91 EE51D6CB 0E3179AB 1042A95D CF6A9483 B84B4B36"
- "B3861AA7 255E4C02 78BA3604 650C10BE 19482F23 171B671D"
- "F1CF3B96 0C074301 CD93C1D1 7603D147 DAE2AEF8 37A62964"
- "EF15E5FB 4AAC0B8C 1CCAA4BE 754AB572 8AE9130C 4C7D0288"
- "0AB9472D 45556216 D6998B86 82283D19 D42A90D5 EF8E5D32"
- "767DC282 2C6DF785 457538AB AE83063E D9CB87C2 D370F263"
- "D5FAD746 6D8499EB 8F464A70 2512B0CE E771E913 0D697735"
- "F897FD03 6CC50432 6C3B0139 9F643532 290F958C 0BBD9006"
- "5DF08BAB BD30AEB6 3B84C460 5D6CA371 047127D0 3A72D598"
- "A1EDADFE 707E8847 25C16890 54908400 8D391E09 53C3F36B"
- "C438CD08 5EDD2D93 4CE1938C 357A711E 0D4A341A 5B0A85ED"
- "12C1F4E5 156A2674 6DDDE16D 826F477C 97477E0A 0FDF6553"
- "143E2CA3 A735E02E CCD94B27 D04861D1 119DD0C3 28ADF3F6"
- "8FB094B8 67716BD7 DC0DEEBB 10B8240E 68034893 EAD82D54"
- "C9DA754C 46C7EEE0 C37FDBEE 48536047 A6FA1AE4 9A0318CC"
- "FFFFFFFF FFFFFFFF",
- }
-};
-
-/* Convert a string of hexadecimal characters to a binary integer. */
-static SECItem *
-hex_to_secitem(const char *hex, SECItem *item)
-{
- int count, i;
- unsigned int j;
- unsigned char c, acc;
-
- j = 0;
- c = hex[0];
- /* If the high bit would be set, prepend a zero byte to keep the result
- * from being negative. */
- if ((c == '8') ||
- (c == '9') ||
- ((c >= 'a') && (c <= 'f')) || ((c >= 'A') && (c <= 'F'))) {
- item->data[j] = 0;
- j++;
- }
- count = 0;
- acc = 0;
- for (i = 0; hex[i] != '\0'; i++) {
- if ((count % 2) == 0)
- acc = 0;
- c = hex[i];
- if ((c >= '0') && (c <= '9'))
- acc = (acc << 4) | (c - '0');
- else if ((c >= 'a') && (c <= 'f'))
- acc = (acc << 4) | (c - 'a' + 10);
- else if ((c >= 'A') && (c <= 'F'))
- acc = (acc << 4) | (c - 'A' + 10);
- else
- continue;
- count++;
- if ((count % 2) == 0) {
- item->data[j] = acc & 0xff;
- acc = 0;
- j++;
- }
- if (j >= item->len) {
- /* overrun */
- return NULL;
- break;
- }
- }
- if (hex[i] != '\0') /* unused bytes? */
- return NULL;
- item->len = j;
- return item;
-}
-
-static int
-oakley_parse_group(PLArenaPool *pool, struct oakley_group *group,
- struct domain_parameters **domain_params_out)
-{
- unsigned int bytes;
- struct domain_parameters *params;
- SECItem *t;
-
- params = PORT_ArenaZAlloc(pool, sizeof(*params));
- if (params == NULL)
- return ENOMEM;
-
- /* Allocate more memory than we'll probably need. */
- bytes = group->bits;
-
- /* Encode the prime (p). */
- t = SECITEM_AllocItem(pool, NULL, bytes);
- if (t == NULL)
- return ENOMEM;
- if (hex_to_secitem(group->prime, t) != t)
- return ENOMEM;
- params->p = *t;
- /* Encode the generator. */
- if (SEC_ASN1EncodeInteger(pool, &params->g,
- group->generator) != &params->g)
- return ENOMEM;
- /* Encode the subprime. */
- t = SECITEM_AllocItem(pool, NULL, bytes);
- if (t == NULL)
- return ENOMEM;
- if (hex_to_secitem(group->subprime, t) != t)
- return ENOMEM;
- params->q = *t;
- *domain_params_out = params;
- return 0;
-}
-
-static struct domain_parameters *
-oakley_get_group(PLArenaPool *pool, int minimum_prime_size)
-{
- unsigned int i;
- struct domain_parameters *params;
-
- params = PORT_ArenaZAlloc(pool, sizeof(*params));
- if (params == NULL)
- return NULL;
- for (i = 0; i < sizeof(oakley_groups) / sizeof(oakley_groups[0]); i++)
- if (oakley_groups[i].bits >= minimum_prime_size)
- if (oakley_parse_group(pool, &oakley_groups[i], &params) == 0)
- return params;
- return NULL;
-}
-
-/* Create DH parameters to be sent to the KDC. On success, dh_params should
- * contain an encoded DomainParameters structure (per RFC3280, the "parameters"
- * in an AlgorithmIdentifier), and dh_pubkey should contain the public value
- * we're prepared to send to the KDC, encoded as an integer (per RFC3280, the
- * "subjectPublicKey" field of a SubjectPublicKeyInfo -- the integer is wrapped
- * up into a bitstring elsewhere). */
-krb5_error_code
-client_create_dh(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context id_cryptoctx,
- int dh_size_bits,
- unsigned char **dh_params,
- unsigned int *dh_params_len,
- unsigned char **dh_pubkey, unsigned int *dh_pubkey_len)
-{
- PLArenaPool *pool;
- PK11SlotInfo *slot;
- SECKEYPrivateKey *priv;
- SECKEYPublicKey *pub;
- SECKEYDHParams dh_param;
- struct domain_parameters *params;
- SECItem encoded;
-
- pool = PORT_NewArena(sizeof(double));
- if (pool == NULL)
- return ENOMEM;
- memset(&params, 0, sizeof(params));
-
- /* Find suitable domain parameters. */
- params = oakley_get_group(pool, dh_size_bits);
- if (params == NULL) {
- pkiDebug("%s: error finding suitable parameters\n", __FUNCTION__);
- return ENOENT;
- }
-
- /* Set up to generate the public key. */
- memset(&dh_param, 0, sizeof(dh_param));
- dh_param.arena = pool;
- dh_param.prime = params->p;
- dh_param.base = params->g;
-
- /* Generate a public value and a private key. */
- slot = PK11_GetBestSlot(CKM_DH_PKCS_KEY_PAIR_GEN,
- crypto_pwcb_prep(id_cryptoctx, NULL, context));
- if (slot == NULL) {
- PORT_FreeArena(pool, PR_TRUE);
- pkiDebug("%s: error selecting slot\n", __FUNCTION__);
- return ENOMEM;
- }
- pub = NULL;
- priv = PK11_GenerateKeyPair(slot, CKM_DH_PKCS_KEY_PAIR_GEN,
- &dh_param, &pub, PR_FALSE, PR_FALSE,
- crypto_pwcb_prep(id_cryptoctx, NULL, context));
-
- /* Finish building the return values. */
- memset(&encoded, 0, sizeof(encoded));
- if (SEC_ASN1EncodeItem(pool, &encoded, params,
- domain_parameters_template) != &encoded) {
- PK11_FreeSlot(slot);
- PORT_FreeArena(pool, PR_TRUE);
- pkiDebug("%s: error encoding parameters\n", __FUNCTION__);
- return ENOMEM;
- }
-
- /* Export the return values. */
- if (secitem_to_buf_len(&encoded, dh_params, dh_params_len) != 0) {
- PK11_FreeSlot(slot);
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
- if (secitem_to_dh_pubval(&pub->u.dh.publicValue, dh_pubkey,
- dh_pubkey_len) != 0) {
- free(*dh_params);
- *dh_params = NULL;
- PK11_FreeSlot(slot);
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
-
- /* Save our private and public keys for reuse later. */
- if (req_cryptoctx->client_dh_privkey != NULL)
- SECKEY_DestroyPrivateKey(req_cryptoctx->client_dh_privkey);
- req_cryptoctx->client_dh_privkey = priv;
- if (req_cryptoctx->client_dh_pubkey != NULL)
- SECKEY_DestroyPublicKey(req_cryptoctx->client_dh_pubkey);
- req_cryptoctx->client_dh_pubkey = pub;
-
- PK11_FreeSlot(slot);
- PORT_FreeArena(pool, PR_TRUE);
- return 0;
-}
-
-/* Combine the KDC's public key value with our copy of the parameters and our
- * secret key to generate the session key. */
-krb5_error_code
-client_process_dh(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context id_cryptoctx,
- unsigned char *dh_pubkey,
- unsigned int dh_pubkey_len,
- unsigned char **dh_session_key,
- unsigned int *dh_session_key_len)
-{
- PLArenaPool *pool;
- PK11SlotInfo *slot;
- SECKEYPublicKey *pub, pub2;
- PK11SymKey *sym;
- SECItem *bits;
-
- pool = PORT_NewArena(sizeof(double));
- if (pool == NULL)
- return ENOMEM;
-
- /* Rebuild the KDC's public key using our parameters and the supplied
- * public value (subjectPublicKey). */
- pub = SECKEY_CopyPublicKey(req_cryptoctx->client_dh_pubkey);
- if (pub == NULL) {
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
- pub2 = *pub;
- if (secitem_from_dh_pubval(pool, dh_pubkey, dh_pubkey_len,
- &pub2.u.dh.publicValue) != 0) {
- SECKEY_DestroyPublicKey(pub);
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
-
- /* Generate the shared value using our private key and the KDC's
- * public key. */
- slot = PK11_GetBestSlot(CKM_DH_PKCS_KEY_PAIR_GEN,
- crypto_pwcb_prep(id_cryptoctx, NULL, context));
- if (slot == NULL) {
- SECKEY_DestroyPublicKey(pub);
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
- sym = PK11_PubDerive(req_cryptoctx->client_dh_privkey, &pub2, PR_FALSE,
- NULL, NULL,
- CKM_DH_PKCS_DERIVE,
- CKM_TLS_MASTER_KEY_DERIVE_DH,
- CKA_DERIVE,
- 0, crypto_pwcb_prep(id_cryptoctx, NULL, context));
- if (sym == NULL) {
- PK11_FreeSlot(slot);
- SECKEY_DestroyPublicKey(pub);
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
-
- /* Export the shared value. */
- if ((PK11_ExtractKeyValue(sym) != SECSuccess) ||
- ((bits = PK11_GetKeyData(sym)) == NULL) ||
- (secitem_to_buf_len(bits, dh_session_key, dh_session_key_len) != 0)) {
- PK11_FreeSymKey(sym);
- PK11_FreeSlot(slot);
- SECKEY_DestroyPublicKey(pub);
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
- PK11_FreeSymKey(sym);
- PK11_FreeSlot(slot);
- SECKEY_DestroyPublicKey(pub);
- PORT_FreeArena(pool, PR_TRUE);
- return 0;
-}
-
-/* Given a binary-encoded integer, count the number of bits. */
-static int
-get_integer_bits(SECItem *integer)
-{
- unsigned int i;
- unsigned char c;
- int size = 0;
-
- for (i = 0; i < integer->len; i++) {
- c = integer->data[i];
- if (c != 0) {
- size = (integer->len - i - 1) * 8;
- while (c != 0) {
- c >>= 1;
- size++;
- }
- break;
- }
- }
- return size;
-}
-
-/* Verify that the client-supplied parameters include a prime of sufficient
- * size. */
-krb5_error_code
-server_check_dh(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context id_cryptoctx,
- krb5_data *dh_params, int minbits)
-{
- PLArenaPool *pool;
- SECItem item;
-
- pool = PORT_NewArena(sizeof(double));
- if (pool == NULL)
- return ENOMEM;
-
- item.data = (unsigned char *)dh_params->data;
- item.len = dh_params->length;
- memset(&req_cryptoctx->client_dh_params, 0,
- sizeof(req_cryptoctx->client_dh_params));
- if (SEC_ASN1DecodeItem(req_cryptoctx->pool,
- &req_cryptoctx->client_dh_params,
- domain_parameters_template, &item) != SECSuccess) {
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
-
- if (get_integer_bits(&req_cryptoctx->client_dh_params.p) < minbits) {
- PORT_FreeArena(pool, PR_TRUE);
- return KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
- }
-
- PORT_FreeArena(pool, PR_TRUE);
- return 0;
-}
-
-/* Take apart the client-supplied SubjectPublicKeyInfo, which contains both an
- * encoded DomainParameters structure (per RFC3279), and a public value, and
- * generate our own private key and public value using the supplied parameters.
- * Use our private key and the client's public value to derive the session key,
- * and hand our public value and the session key back to our caller. */
-krb5_error_code
-server_process_dh(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context id_cryptoctx,
- unsigned char *received_pubkey,
- unsigned int received_pub_len,
- unsigned char **dh_pubkey,
- unsigned int *dh_pubkey_len,
- unsigned char **server_key,
- unsigned int *server_key_len)
-{
- PLArenaPool *pool;
- SECKEYPrivateKey *priv;
- SECKEYPublicKey *pub, pub2;
- SECKEYDHParams dh_params;
- PK11SymKey *sym;
- SECItem pubval, *bits;
- PK11SlotInfo *slot;
-
- pool = PORT_NewArena(sizeof(double));
- if (pool == NULL)
- return ENOMEM;
-
- /* Store the client's public value. */
- pubval.data = received_pubkey;
- pubval.len = received_pub_len;
-
- /* Set up DH parameters the using client's domain parameters. */
- memset(&dh_params, 0, sizeof(dh_params));
- dh_params.arena = pool;
- dh_params.prime = req_cryptoctx->client_dh_params.p;
- dh_params.base = req_cryptoctx->client_dh_params.g;
-
- /* Generate a public value and a private key using the parameters. */
- slot = PK11_GetBestSlot(CKM_DH_PKCS_KEY_PAIR_GEN,
- crypto_pwcb_prep(id_cryptoctx, NULL, context));
- if (slot == NULL) {
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
- pub = NULL;
- priv = PK11_GenerateKeyPair(slot, CKM_DH_PKCS_KEY_PAIR_GEN,
- &dh_params, &pub, PR_FALSE, PR_FALSE,
- crypto_pwcb_prep(id_cryptoctx, NULL, context));
- if (priv == NULL) {
- PK11_FreeSlot(slot);
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
-
- /* Build the client's public key using the client's parameters and
- * public value. */
- pub2 = *pub;
- if (SEC_ASN1DecodeItem(pool, &pub2.u.dh.publicValue,
- SEC_ASN1_GET(SEC_IntegerTemplate),
- &pubval) != SECSuccess) {
- SECKEY_DestroyPrivateKey(priv);
- SECKEY_DestroyPublicKey(pub);
- PK11_FreeSlot(slot);
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
-
- /* Generate the shared value using our private key and the client's
- * public key. */
- sym = PK11_PubDerive(priv, &pub2, PR_FALSE,
- NULL, NULL,
- CKM_DH_PKCS_DERIVE,
- CKM_TLS_MASTER_KEY_DERIVE_DH,
- CKA_DERIVE,
- 0, crypto_pwcb_prep(id_cryptoctx, NULL, context));
- if (sym == NULL) {
- SECKEY_DestroyPrivateKey(priv);
- SECKEY_DestroyPublicKey(pub);
- PK11_FreeSlot(slot);
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
-
- /* Export the shared value for our use and our public value for
- * transmission back to the client. */
- *server_key = NULL;
- *dh_pubkey = NULL;
- if ((PK11_ExtractKeyValue(sym) != SECSuccess) ||
- ((bits = PK11_GetKeyData(sym)) == NULL) ||
- (secitem_to_buf_len(bits, server_key, server_key_len) != 0) ||
- (secitem_to_dh_pubval(&pub->u.dh.publicValue,
- dh_pubkey, dh_pubkey_len) != 0)) {
- free(*server_key);
- free(*dh_pubkey);
- PK11_FreeSymKey(sym);
- SECKEY_DestroyPrivateKey(priv);
- SECKEY_DestroyPublicKey(pub);
- PK11_FreeSlot(slot);
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
- PK11_FreeSymKey(sym);
- SECKEY_DestroyPrivateKey(priv);
- SECKEY_DestroyPublicKey(pub);
- PK11_FreeSlot(slot);
- PORT_FreeArena(pool, PR_TRUE);
- return 0;
-}
-
-/* Create the issuer-and-serial portion of an external principal identifier for
- * a KDC's cert that we already have. */
-krb5_error_code
-create_issuerAndSerial(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context id_cryptoctx,
- unsigned char **kdcId_buf, unsigned int *kdcId_len)
-{
- PLArenaPool *pool;
- struct issuer_and_serial_number isn;
- SECItem item;
-
- /* Check if we have a peer cert. If we don't have one, that's okay. */
- if (req_cryptoctx->peer_cert == NULL)
- return 0;
-
- /* Scratch arena. */
- pool = PORT_NewArena(sizeof(double));
- if (pool == NULL)
- return ENOMEM;
-
- /* Encode the peer's issuer/serial. */
- isn.issuer = req_cryptoctx->peer_cert->derIssuer;
- isn.serial = req_cryptoctx->peer_cert->serialNumber;
- memset(&item, 0, sizeof(item));
- if (SEC_ASN1EncodeItem(id_cryptoctx->id_cert->arena, &item, &isn,
- issuer_and_serial_number_template) != &item) {
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
-
- /* Export the value. */
- if (secitem_to_buf_len(&item, kdcId_buf, kdcId_len) != 0) {
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
-
- PORT_FreeArena(pool, PR_TRUE);
- return 0;
-}
-
-/* Populate a list of AlgorithmIdentifier structures with the OIDs of the key
- * wrap algorithms that we support. */
-static void
-free_n_algorithm_identifiers(krb5_algorithm_identifier **ids, int i)
-{
- while (i >= 0) {
- free(ids[i]->algorithm.data);
- free(ids[i]);
- i--;
- }
- free(ids);
-}
-
-krb5_error_code
-create_krb5_supportedCMSTypes(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context id_cryptoctx,
- krb5_algorithm_identifier ***supportedCMSTypes)
-{
- SECOidData *oid;
- SECOidTag oids[] = {
- SEC_OID_CMS_3DES_KEY_WRAP, /* no parameters */
- SEC_OID_AES_128_KEY_WRAP, /* no parameters */
- SEC_OID_AES_192_KEY_WRAP, /* no parameters */
- SEC_OID_AES_256_KEY_WRAP, /* no parameters */
- /* RC2 key wrap requires parameters, so skip it */
- };
- krb5_algorithm_identifier **ids, *id;
- unsigned int i;
-
- ids = malloc(sizeof(id) * ((sizeof(oids) / sizeof(oids[0])) + 1));
- if (ids == NULL)
- return ENOMEM;
-
- for (i = 0; i < (sizeof(oids) / sizeof(oids[0])); i++) {
- id = malloc(sizeof(*id));
- if (id == NULL) {
- free_n_algorithm_identifiers(ids, i - 1);
- return ENOMEM;
- }
- memset(id, 0, sizeof(*id));
- ids[i] = id;
- oid = SECOID_FindOIDByTag(oids[i]);
- if (secitem_to_buf_len(&oid->oid,
- (unsigned char **)&id->algorithm.data,
- &id->algorithm.length) != 0) {
- free(ids[i]);
- free_n_algorithm_identifiers(ids, i - 1);
- return ENOMEM;
- }
- }
- ids[i] = NULL;
- *supportedCMSTypes = ids;
- return 0;
-}
-
-/* Populate a list of trusted certifiers with the list of the root certificates
- * that we trust. */
-static void
-free_n_principal_identifiers(krb5_external_principal_identifier **ids, int i)
-{
- while (i >= 0) {
- free(ids[i]->subjectKeyIdentifier.data);
- free(ids[i]->issuerAndSerialNumber.data);
- free(ids[i]->subjectName.data);
- free(ids[i]);
- i--;
- }
- free(ids);
-}
-
-krb5_error_code
-create_krb5_trustedCertifiers(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context id_cryptoctx,
- krb5_external_principal_identifier ***
- trustedCertifiers)
-{
- CERTCertListNode *node;
- krb5_external_principal_identifier **ids, *id;
- unsigned int i, n;
-
- *trustedCertifiers = NULL;
-
- /* Count the root certs. */
- n = 0;
- if (!CERT_LIST_EMPTY(id_cryptoctx->ca_certs)) {
- for (n = 0, node = CERT_LIST_HEAD(id_cryptoctx->ca_certs);
- (node != NULL) &&
- (node->cert != NULL) &&
- !CERT_LIST_END(node, id_cryptoctx->ca_certs);
- node = CERT_LIST_NEXT(node)) {
- n++;
- }
- }
-
- /* Build the result list. */
- if (n > 0) {
- ids = malloc((n + 1) * sizeof(id));
- if (ids == NULL)
- return ENOMEM;
- node = CERT_LIST_HEAD(id_cryptoctx->ca_certs);
- for (i = 0; i < n; i++) {
- id = malloc(sizeof(*id));
- if (id == NULL) {
- free_n_principal_identifiers(ids, i - 1);
- return ENOMEM;
- }
- memset(id, 0, sizeof(*id));
- /* Use the certificate's subject key ID iff it's
- * actually in the certificate. Allocate the memory
- * from the heap because it'll be freed by other parts
- * of the pkinit module. */
- if ((node->cert->keyIDGenerated ?
- secitem_to_buf_len(&node->cert->derSubject,
- (unsigned char **)
- &id->subjectName.data,
- &id->subjectName.length) :
- secitem_to_buf_len(&node->cert->subjectKeyID,
- (unsigned char **)
- &id->subjectKeyIdentifier.data,
- &id->subjectKeyIdentifier.length)) != 0) {
- /* Free the earlier items. */
- free(ids[i]);
- free_n_principal_identifiers(ids, i - 1);
- return ENOMEM;
- }
- ids[i] = id;
- node = CERT_LIST_NEXT(node);
- }
- ids[i] = NULL;
- *trustedCertifiers = ids;
- }
- return 0;
-}
-
-/* Add a certificate to a list if it isn't already in the list. Since the list
- * would take ownership of the cert if we added it to the list, if it's already
- * in the list, delete this reference to it. */
-static SECStatus
-cert_maybe_add_to_list(CERTCertList *list, CERTCertificate *cert)
-{
- CERTCertListNode *node;
-
- for (node = CERT_LIST_HEAD(list);
- (node != NULL) &&
- (node->cert != NULL) &&
- !CERT_LIST_END(node, list);
- node = CERT_LIST_NEXT(node)) {
- if (SECITEM_ItemsAreEqual(&node->cert->derCert, &cert->derCert)) {
- /* Don't add the duplicate. */
- CERT_DestroyCertificate(cert);
- return SECSuccess;
- }
- }
- return CERT_AddCertToListTail(list, cert);
-}
-
-/* Load CA certificates from the slot. */
-static SECStatus
-cert_load_ca_certs_from_slot(krb5_context context,
- pkinit_identity_crypto_context id,
- PK11SlotInfo *slot,
- const char *identity)
-{
- CERTCertificate *cert;
- CERTCertList *list;
- CERTCertListNode *node;
- CERTCertTrust trust;
- SECStatus status;
-
- /* Log in if the slot requires it. */
- PK11_TokenRefresh(slot);
- if (!PK11_IsLoggedIn(slot, crypto_pwcb_prep(id, identity, context)) &&
- PK11_NeedLogin(slot)) {
- pkiDebug("%s: logging in to token \"%s\"\n",
- __FUNCTION__, PK11_GetTokenName(slot));
- if (PK11_Authenticate(slot, PR_TRUE,
- crypto_pwcb_prep(id, identity,
- context)) != SECSuccess) {
- pkiDebug("%s: error logging into \"%s\": %s, skipping\n",
- __FUNCTION__, PK11_GetTokenName(slot),
- PORT_ErrorToName(PORT_GetError()));
- return SECFailure;
- }
- }
- /* Get the list of certs from the slot. */
- list = PK11_ListCertsInSlot(slot);
- if (list == NULL) {
- pkiDebug("%s: nothing found in token \"%s\"\n",
- __FUNCTION__, PK11_GetTokenName(slot));
- return SECSuccess;
- }
- if (CERT_LIST_EMPTY(list)) {
- CERT_DestroyCertList(list);
- pkiDebug("%s: nothing found in token \"%s\"\n",
- __FUNCTION__, PK11_GetTokenName(slot));
- return SECSuccess;
- }
- /* Walk the list of certs, and for each one that's a CA, add
- * it to our CA cert list. */
- status = SECSuccess;
- for (node = CERT_LIST_HEAD(list);
- (node != NULL) &&
- (node->cert != NULL) &&
- !CERT_LIST_END(node, list);
- node = CERT_LIST_NEXT(node)) {
-#if 0
- /* Skip it if it's not a root. */
- if (!node->cert->isRoot) {
- continue;
- }
-#endif
- /* Skip it if we don't trust it to issue certificates. */
- if (CERT_GetCertTrust(node->cert, &trust) != SECSuccess)
- continue;
- if ((SEC_GET_TRUST_FLAGS(&trust, trustSSL) &
- (CERTDB_TRUSTED_CA |
- CERTDB_TRUSTED_CLIENT_CA | CERTDB_NS_TRUSTED_CA)) == 0)
- continue;
- /* DestroyCertList frees all of the certs in the list,
- * so we need to create a copy that we can own. */
- cert = CERT_DupCertificate(node->cert);
- /* Add it to the list. */
- if (cert_maybe_add_to_list(id->ca_certs, cert) != SECSuccess)
- status = SECFailure;
- }
- CERT_DestroyCertList(list);
- return status;
-}
-
-/* Load certificates for which we have private keys from the slot. */
-static int
-cert_load_certs_with_keys_from_slot(krb5_context context,
- pkinit_identity_crypto_context
- id_cryptoctx,
- PK11SlotInfo *slot,
- const char *cert_label,
- const char *cert_id,
- const char *identity)
-{
- CERTCertificate *cert;
- CERTCertList *clist;
- CERTCertListNode *cnode;
- SECKEYPrivateKey *key;
- int status;
-
- /* Log in if the slot requires it. */
- PK11_TokenRefresh(slot);
- if (!PK11_IsLoggedIn(slot, crypto_pwcb_prep(id_cryptoctx, identity,
- context)) &&
- PK11_NeedLogin(slot)) {
- pkiDebug("%s: logging in to token \"%s\"\n",
- __FUNCTION__, PK11_GetTokenName(slot));
- if (PK11_Authenticate(slot, PR_TRUE,
- crypto_pwcb_prep(id_cryptoctx, identity,
- context)) != SECSuccess) {
- pkiDebug("%s: error logging into \"%s\": %s, skipping\n",
- __FUNCTION__, PK11_GetTokenName(slot),
- PORT_ErrorToName(PORT_GetError()));
- return id_cryptoctx->defer_id_prompt ? 0 : ENOMEM;
- }
- }
- /* Get the list of certs from the slot. */
- clist = PK11_ListCertsInSlot(slot);
- if (clist == NULL) {
- pkiDebug("%s: nothing found in token \"%s\"\n",
- __FUNCTION__, PK11_GetTokenName(slot));
- return 0;
- }
- if (CERT_LIST_EMPTY(clist)) {
- CERT_DestroyCertList(clist);
- pkiDebug("%s: nothing found in token \"%s\"\n",
- __FUNCTION__, PK11_GetTokenName(slot));
- return 0;
- }
- /* Walk the list of certs, and for each one for which we can
- * find the matching private key, add it and the keys to the
- * lists. */
- status = 0;
- for (cnode = CERT_LIST_HEAD(clist);
- (cnode != NULL) &&
- (cnode->cert != NULL) &&
- !CERT_LIST_END(cnode, clist);
- cnode = CERT_LIST_NEXT(cnode)) {
- if (cnode->cert->nickname != NULL) {
- if ((cert_label != NULL) && (cert_id != NULL)) {
- if ((strcmp(cert_id, cnode->cert->nickname) != 0) &&
- (strcmp(cert_label, cnode->cert->nickname) != 0))
- continue;
- } else if (cert_label != NULL) {
- if (strcmp(cert_label, cnode->cert->nickname) != 0)
- continue;
- } else if (cert_id != NULL) {
- if (strcmp(cert_id, cnode->cert->nickname) != 0)
- continue;
- }
- }
- key = PK11_FindPrivateKeyFromCert(slot, cnode->cert,
- crypto_pwcb_prep(id_cryptoctx,
- identity, context));
- if (key == NULL) {
- pkiDebug("%s: no key for \"%s\", skipping it\n",
- __FUNCTION__,
- cnode->cert->nickname ?
- cnode->cert->nickname : "(no name)");
- continue;
- }
- pkiDebug("%s: found \"%s\" and its matching key\n",
- __FUNCTION__,
- cnode->cert->nickname ? cnode->cert->nickname : "(no name)");
- /* DestroyCertList frees all of the certs in the list,
- * so we need to create a copy that it can own. */
- cert = CERT_DupCertificate(cnode->cert);
- if (cert_maybe_add_to_list(id_cryptoctx->id_certs,
- cert) != SECSuccess)
- status = ENOMEM;
- /* We don't need this reference to the key. */
- SECKEY_DestroyPrivateKey(key);
- }
- CERT_DestroyCertList(clist);
- return status;
-}
-
-/*
- * Reassemble the identity as it was supplied by the user or the library
- * configuration.
- */
-static char *
-reassemble_pkcs11_name(PLArenaPool *pool, pkinit_identity_opts *idopts)
-{
- struct k5buf buf;
- int n = 0;
- char *ret;
-
- k5_buf_init_dynamic(&buf);
- k5_buf_add(&buf, "PKCS11:");
- n = 0;
- if (idopts->p11_module_name != NULL) {
- k5_buf_add_fmt(&buf, "%smodule_name=%s", n++ ? ":" : "",
- idopts->p11_module_name);
- }
- if (idopts->token_label != NULL) {
- k5_buf_add_fmt(&buf, "%stoken=%s", n++ ? ":" : "",
- idopts->token_label);
- }
- if (idopts->cert_label != NULL) {
- k5_buf_add_fmt(&buf, "%scertlabel=%s", n++ ? ":" : "",
- idopts->cert_label);
- }
- if (idopts->cert_id_string != NULL) {
- k5_buf_add_fmt(&buf, "%scertid=%s", n++ ? ":" : "",
- idopts->cert_id_string);
- }
- if (idopts->slotid != PK_NOSLOT) {
- k5_buf_add_fmt(&buf, "%sslotid=%ld", n++ ? ":" : "",
- (long)idopts->slotid);
- }
- if (k5_buf_status(&buf) == 0)
- ret = PORT_ArenaStrdup(pool, buf.data);
- else
- ret = NULL;
- k5_buf_free(&buf);
- return ret;
-}
-
-/*
- * Assemble an identity string that will distinguish this token from any other
- * that is accessible through the same module, even if the user didn't specify
- * a token name.
- */
-static char *
-reassemble_pkcs11_identity(PLArenaPool *pool, pkinit_identity_opts *idopts,
- long slotid, const char *tokenname)
-{
- struct k5buf buf;
- int n = 0;
- char *ret;
-
- k5_buf_init_dynamic(&buf);
- k5_buf_add(&buf, "PKCS11:");
- n = 0;
- if (idopts->p11_module_name != NULL) {
- k5_buf_add_fmt(&buf, "%smodule_name=%s",
- n++ ? ":" : "",
- idopts->p11_module_name);
- }
-
- if (slotid != PK_NOSLOT)
- k5_buf_add_fmt(&buf, "%sslotid=%ld", n++ ? ":" : "", slotid);
-
- if (tokenname != NULL)
- k5_buf_add_fmt(&buf, "%stoken=%s", n++ ? ":" : "", tokenname);
-
- if (k5_buf_status(&buf) == 0)
- ret = PORT_ArenaStrdup(pool, buf.data);
- else
- ret = NULL;
- k5_buf_free(&buf);
-
- return ret;
-}
-
-static SECStatus
-crypto_load_pkcs11(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_opts *idopts,
- pkinit_identity_crypto_context id_cryptoctx)
-{
- struct _pkinit_identity_crypto_module **id_modules, *module;
- PK11SlotInfo *slot;
- CK_TOKEN_INFO tinfo;
- char *spec, *identity;
- size_t spec_size;
- const char *tokenname;
- SECStatus status;
- int i, j;
-
- if (idopts == NULL)
- return SECFailure;
-
- /* If no module is specified, use the default module from pkinit.h. */
- if (idopts->p11_module_name == NULL) {
- idopts->p11_module_name = strdup(PKCS11_MODNAME);
- if (idopts->p11_module_name == NULL)
- return SECFailure;
- }
-
- /* Build the module spec. */
- spec_size = strlen("library=''") + strlen(idopts->p11_module_name) * 2 + 1;
- spec = PORT_ArenaZAlloc(id_cryptoctx->pool, spec_size);
- if (spec == NULL)
- return SECFailure;
- strlcpy(spec, "library=\"", spec_size);
- j = strlen(spec);
- for (i = 0; idopts->p11_module_name[i] != '\0'; i++) {
- if (strchr("\"", idopts->p11_module_name[i]) != NULL)
- spec[j++] = '\\';
- spec[j++] = idopts->p11_module_name[i];
- }
- spec[j++] = '\0';
- strlcat(spec, "\"", spec_size);
-
- /* Count the number of modules we've already loaded. */
- if (id_cryptoctx->id_modules != NULL) {
- for (i = 0; id_cryptoctx->id_modules[i] != NULL; i++)
- continue;
- } else {
- i = 0;
- }
-
- /* Allocate a bigger list. */
- id_modules = PORT_ArenaZAlloc(id_cryptoctx->pool,
- sizeof(id_modules[0]) * (i + 2));
- if (id_modules == NULL)
- return SECFailure;
- for (j = 0; j < i; j++)
- id_modules[j] = id_cryptoctx->id_modules[j];
-
- /* Actually load the module, or just ref an already-loaded copy. */
- module = PORT_ArenaZAlloc(id_cryptoctx->pool, sizeof(*module));
- if (module == NULL)
- return SECFailure;
- module->name = reassemble_pkcs11_name(id_cryptoctx->pool, idopts);
- if (module->name == NULL)
- return SECFailure;
- module->spec = spec;
- for (j = 0; j < i; j++) {
- if (strcmp(module->spec, id_modules[j]->spec) == 0)
- break;
- }
- if (j < i)
- module->module = SECMOD_ReferenceModule(id_modules[j]->module);
- else
- module->module = SECMOD_LoadUserModule(spec, NULL, PR_FALSE);
- if (module->module == NULL) {
- pkiDebug("%s: error loading PKCS11 module \"%s\"",
- __FUNCTION__, idopts->p11_module_name);
- return SECFailure;
- }
- if (!module->module->loaded) {
- pkiDebug("%s: error really loading PKCS11 module \"%s\"",
- __FUNCTION__, idopts->p11_module_name);
- SECMOD_DestroyModule(module->module);
- module->module = NULL;
- return SECFailure;
- }
- SECMOD_UpdateSlotList(module->module);
- pkiDebug("%s: loaded PKCS11 module \"%s\"\n", __FUNCTION__,
- idopts->p11_module_name);
-
- /* Add us to the list and set the new list. */
- id_modules[j++] = module;
- id_modules[j] = NULL;
- id_cryptoctx->id_modules = id_modules;
-
- /* Walk the list of slots in the module. */
- status = SECFailure;
- for (i = 0;
- (i < module->module->slotCount) &&
- ((slot = module->module->slots[i]) != NULL);
- i++) {
- PK11_TokenRefresh(slot);
- if (idopts->slotid != PK_NOSLOT) {
- if (idopts->slotid != PK11_GetSlotID(slot))
- continue;
- }
- tokenname = PK11_GetTokenName(slot);
- if (tokenname == NULL || strlen(tokenname) == 0)
- continue;
- /* If we're looking for a specific token, and this isn't it, go on. */
- if (idopts->token_label != NULL) {
- if (strcmp(idopts->cert_label, tokenname) != 0)
- continue;
- }
- /* Assemble a useful identity string, in case of an incomplete one. */
- identity = reassemble_pkcs11_identity(id_cryptoctx->pool, idopts,
- (long)PK11_GetSlotID(slot),
- tokenname);
- /*
- * Skip past all of the loading-certificates-and-keys logic, pick up
- * the token flags, and call it done for now.
- */
- if (id_cryptoctx->defer_id_prompt) {
- if (!PK11_IsLoggedIn(slot, crypto_pwcb_prep(id_cryptoctx, identity,
- context)) &&
- PK11_NeedLogin(slot)) {
- pkiDebug("%s: reading flags for token \"%s\"\n",
- __FUNCTION__, PK11_GetTokenName(slot));
- if (PK11_GetTokenInfo(slot, &tinfo) == SECSuccess) {
- pkinit_set_deferred_id(&id_cryptoctx->deferred_ids,
- identity, tinfo.flags, NULL);
- }
- }
- return SECSuccess;
- }
- if (!PK11_IsPresent(slot))
- continue;
- /* Load private keys and their certs from this token. */
- if (cert_load_certs_with_keys_from_slot(context, id_cryptoctx,
- slot, idopts->cert_label,
- idopts->cert_id_string,
- identity) == 0)
- status = SECSuccess;
- /* If no label was specified, then we've looked at a token, so we're
- * done. */
- if (idopts->token_label == NULL)
- break;
- }
-
- return status;
-}
-
-/* Return the slot which we'll use for holding PEM items. Open the module if
- * we need to, first. */
-static PK11SlotInfo *
-crypto_get_pem_slot(struct _pkinit_identity_crypto_context *id)
-{
- PK11SlotInfo *slot;
- char *pem_module_name, *spec;
- size_t spec_size;
-
- if (id->pem_module == NULL) {
- pem_module_name = PR_GetLibraryName(NULL, PEM_MODULE);
- if (pem_module_name == NULL) {
- pkiDebug("%s: error determining library name for %s\n",
- __FUNCTION__, PEM_MODULE);
- return NULL;
- }
- spec_size = strlen("library=") + strlen(pem_module_name) + 1;
- spec = malloc(spec_size);
- if (spec == NULL) {
- pkiDebug("%s: out of memory building spec for %s\n",
- __FUNCTION__, pem_module_name);
- PR_FreeLibraryName(pem_module_name);
- return NULL;
- }
- snprintf(spec, spec_size, "library=%s", pem_module_name);
- id->pem_module = SECMOD_LoadUserModule(spec, NULL, PR_FALSE);
- if (id->pem_module == NULL)
- pkiDebug("%s: error loading %s\n", __FUNCTION__, pem_module_name);
- else if (!id->pem_module->loaded)
- pkiDebug("%s: error really loading %s\n", __FUNCTION__,
- pem_module_name);
- else
- SECMOD_UpdateSlotList(id->pem_module);
- free(spec);
- PR_FreeLibraryName(pem_module_name);
- }
- if ((id->pem_module != NULL) && id->pem_module->loaded) {
- if (id->pem_module->slotCount != 0)
- slot = id->pem_module->slots[0];
- else
- slot = NULL;
- if (slot == NULL)
- pkiDebug("%s: no slots in %s?\n", __FUNCTION__, PEM_MODULE);
- } else {
- slot = NULL;
- }
- return slot;
-}
-
-/* Resolve any ambiguities from having a duplicate nickname in the PKCS12
- * bundle and in the database, or the bag not providing a nickname. Note: you
- * might expect "arg" to be a wincx, but it's actually a certificate! (Mozilla
- * bug #321584, fixed in 3.12, documented by #586163, in 3.13.) */
-static SECItem *
-crypto_nickname_c_cb(SECItem *old_nickname, PRBool *cancel, void *arg)
-{
- CERTCertificate *leaf;
- char *old_name, *new_name, *p;
- SECItem *new_nickname, tmp;
- size_t new_name_size;
- int i;
-
- leaf = arg;
- if (old_nickname != NULL)
- pkiDebug("%s: warning: nickname collision on \"%.*s\", "
- "generating a new nickname\n", __FUNCTION__,
- old_nickname->len, old_nickname->data);
- else
- pkiDebug("%s: warning: nickname collision, generating a new "
- "nickname\n", __FUNCTION__);
- new_nickname = NULL;
- if (old_nickname == NULL) {
- old_name = leaf->subjectName;
- new_name_size = strlen(PKCS12_PREFIX ": #1") + strlen(old_name) + 1;
- new_name = PR_Malloc(new_name_size);
- if (new_name != NULL) {
- snprintf(new_name, new_name_size, PKCS12_PREFIX ": %s #1",
- old_name);
- tmp.data = (unsigned char *) new_name;
- tmp.len = strlen(new_name) + 1;
- new_nickname = SECITEM_DupItem(&tmp);
- PR_Free(new_name);
- }
- } else {
- old_name = (char *) old_nickname->data;
- if (strncmp(old_name, PKCS12_PREFIX ": ",
- strlen(PKCS12_PREFIX) + 2) == 0) {
- p = strrchr(old_name, '#');
- i = (p ? atoi(p + 1) : 0) + 1;
- old_name = leaf->subjectName;
- new_name_size = strlen(PKCS12_PREFIX ": #") +
- strlen(old_name) + 3 * sizeof(i) + 1;
- new_name = PR_Malloc(new_name_size);
- } else {
- old_name = leaf->subjectName;
- new_name_size = strlen(PKCS12_PREFIX ": #1") +
- strlen(old_name) + 1;
- new_name = PR_Malloc(new_name_size);
- i = 1;
- }
- if (new_name != NULL) {
- snprintf(new_name, new_name_size, PKCS12_PREFIX ": %s #%d",
- old_name, i);
- tmp.data = (unsigned char *) new_name;
- tmp.len = strlen(new_name) + 1;
- new_nickname = SECITEM_DupItem(&tmp);
- PR_Free(new_name);
- }
- }
- if (new_nickname == NULL) {
- pkiDebug("%s: warning: unable to generate a new nickname\n",
- __FUNCTION__);
- *cancel = PR_TRUE;
- } else {
- pkiDebug("%s: generated new nickname \"%.*s\"\n",
- __FUNCTION__, new_nickname->len, new_nickname->data);
- *cancel = PR_FALSE;
- }
- return new_nickname;
-}
-
-static char *
-reassemble_pkcs12_name(PLArenaPool *pool, const char *filename)
-{
- char *tmp, *ret;
-
- if (asprintf(&tmp, "PKCS12:%s", filename) < 0)
- return NULL;
- ret = PORT_ArenaStrdup(pool, tmp);
- free(tmp);
- return ret;
-}
-
-static SECStatus
-crypto_load_pkcs12(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- const char *name,
- pkinit_identity_crypto_context id_cryptoctx)
-{
- PK11SlotInfo *slot;
- SEC_PKCS12DecoderContext *ctx;
- unsigned char emptypwd[] = { '\0', '\0' };
- SECItem tmp, password;
- PRBool retry;
- int attempt;
- char *identity;
-
- if ((slot = crypto_get_p12_slot(id_cryptoctx)) == NULL) {
- pkiDebug("%s: skipping identity PKCS12 bundle \"%s\": "
- "no slot found\n", __FUNCTION__, name);
- return SECFailure;
- }
- if (secitem_from_file(id_cryptoctx->pool, name,
- secitem_from_file_decode, &tmp) != 0) {
- pkiDebug("%s: skipping identity PKCS12 bundle \"%s\": "
- "error reading from file\n", __FUNCTION__, name);
- return SECFailure;
- }
- /* There's a chance we'll need these. */
- SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_40, PR_TRUE);
- SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_128, PR_TRUE);
- SEC_PKCS12EnableCipher(PKCS12_RC4_40, PR_TRUE);
- SEC_PKCS12EnableCipher(PKCS12_RC4_128, PR_TRUE);
- SEC_PKCS12EnableCipher(PKCS12_DES_56, PR_TRUE);
- SEC_PKCS12EnableCipher(PKCS12_DES_EDE3_168, PR_TRUE);
- /* Pass in the password. */
- memset(&password, 0, sizeof(password));
- password.data = emptypwd;
- password.len = 2;
- attempt = 0;
- ctx = NULL;
- identity = reassemble_pkcs12_name(id_cryptoctx->pool, name);
- if (identity == NULL)
- return SECFailure;
- id_cryptoctx->id_p12_slot.p12name = identity;
- do {
- retry = PR_FALSE;
- ctx = SEC_PKCS12DecoderStart(&password,
- slot,
- crypto_pwcb_prep(id_cryptoctx, identity,
- context),
- NULL, NULL, NULL, NULL, NULL);
- if (ctx == NULL) {
- pkiDebug("%s: skipping identity PKCS12 bundle \"%s\": "
- "error setting up decoder\n", __FUNCTION__, name);
- return SECFailure;
- }
- if (SEC_PKCS12DecoderUpdate(ctx, tmp.data, tmp.len) != SECSuccess) {
- pkiDebug("%s: skipping identity PKCS12 bundle \"%s\": "
- "error passing data to decoder\n", __FUNCTION__, name);
- SEC_PKCS12DecoderFinish(ctx);
- return SECFailure;
- }
- if (SEC_PKCS12DecoderVerify(ctx) != SECSuccess) {
- char *newpass;
- krb5_ucs2 *ucs2;
- unsigned char *ucs2s;
- size_t i, n_ucs2s;
- SECErrorCodes err;
-
- err = PORT_GetError();
- SEC_PKCS12DecoderFinish(ctx);
- switch (err) {
- case SEC_ERROR_BAD_PASSWORD:
- if (id_cryptoctx->defer_id_prompt) {
- pkinit_set_deferred_id(&id_cryptoctx->deferred_ids,
- identity, 0, NULL);
- return SECSuccess;
- }
- pkiDebug("%s: prompting for password for %s\n",
- __FUNCTION__, name);
- newpass = crypto_pwfn(name, PR_FALSE, 0, (attempt > 0),
- id_cryptoctx);
- attempt++;
- if (newpass != NULL) {
- /* convert to 16-bit big-endian */
- if (krb5int_utf8s_to_ucs2les(newpass,
- &ucs2s, &n_ucs2s) == 0) {
- PR_Free(newpass);
- ucs2 = (krb5_ucs2 *) ucs2s;
- for (i = 0; i < n_ucs2s / 2; i++)
- ucs2[i] = SWAP16(ucs2[i]);
- password.data = (void *) ucs2s;
- password.len = n_ucs2s + 2;
- PORT_SetError(0);
- retry = PR_TRUE;
- continue;
- }
- PR_Free(newpass);
- }
- break;
- default:
- break;
- }
- pkiDebug("%s: skipping identity PKCS12 bundle \"%s\": "
- "error verifying data: %d\n", __FUNCTION__,
- name, PORT_GetError());
- return SECFailure;
- }
- } while (retry);
- if (SEC_PKCS12DecoderValidateBags(ctx,
- crypto_nickname_c_cb) != SECSuccess) {
- pkiDebug("%s: skipping identity PKCS12 bundle \"%s\": "
- "error validating bags: %d\n", __FUNCTION__, name,
- PORT_GetError());
- SEC_PKCS12DecoderFinish(ctx);
- if (password.data != emptypwd)
- free(password.data);
- return SECFailure;
- }
- if (SEC_PKCS12DecoderImportBags(ctx) != SECSuccess) {
- pkiDebug("%s: skipping identity PKCS12 bundle \"%s\": "
- "error importing data: %d\n", __FUNCTION__, name,
- PORT_GetError());
- SEC_PKCS12DecoderFinish(ctx);
- if (password.data != emptypwd)
- free(password.data);
- return SECFailure;
- }
- pkiDebug("%s: imported PKCS12 bundle \"%s\"\n", __FUNCTION__, name);
- SEC_PKCS12DecoderFinish(ctx);
- if (password.data != emptypwd)
- free(password.data);
- if (cert_load_certs_with_keys_from_slot(context, id_cryptoctx, slot,
- NULL, NULL, identity) == 0)
- return SECSuccess;
- else
- return SECFailure;
-}
-
-/* Helper to fill out a CK_ATTRIBUTE. */
-static void
-crypto_set_attributes(CK_ATTRIBUTE *attr,
- CK_ATTRIBUTE_TYPE type,
- void *pValue, CK_ULONG ulValueLen)
-{
- memset(attr, 0, sizeof(*attr));
- attr->type = type;
- attr->pValue = pValue;
- attr->ulValueLen = ulValueLen;
-}
-
-static char *
-reassemble_files_name(PLArenaPool *pool, const char *certfile,
- const char *keyfile)
-{
- char *tmp, *ret;
-
- if (keyfile != NULL) {
- if (asprintf(&tmp, "FILE:%s,%s", certfile, keyfile) < 0)
- return NULL;
- } else {
- if (asprintf(&tmp, "FILE:%s", certfile) < 0)
- return NULL;
- }
- ret = PORT_ArenaStrdup(pool, tmp);
- free(tmp);
- return ret;
-}
-
-/* Load keys, certs, and/or CRLs from files. */
-static SECStatus
-crypto_load_files(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- const char *certfile,
- const char *keyfile,
- const char *crlfile,
- PRBool cert_self, PRBool cert_mark_trusted,
- pkinit_identity_crypto_context id_cryptoctx)
-{
- PK11SlotInfo *slot;
- struct _pkinit_identity_crypto_file *cobj, *kobj, **id_objects;
- PRBool permanent;
- SECKEYPrivateKey *key;
- CK_ATTRIBUTE attrs[4];
- CK_BBOOL cktrue = CK_TRUE, cktrust;
- CK_OBJECT_CLASS keyclass = CKO_PRIVATE_KEY, certclass = CKO_CERTIFICATE;
- CERTCertificate *cert;
- SECItem tmp, *crl, **crls;
- SECStatus status;
- int i, j, n_attrs, n_objs, n_crls;
-
- if ((slot = crypto_get_pem_slot(id_cryptoctx)) == NULL) {
- if (certfile != NULL)
- pkiDebug("%s: nsspem module not loaded, not loading file \"%s\"\n",
- __FUNCTION__, certfile);
- if (keyfile != NULL)
- pkiDebug("%s: nsspem module not loaded, not loading file \"%s\"\n",
- __FUNCTION__, keyfile);
- if (crlfile != NULL)
- pkiDebug("%s: nsspem module not loaded, not loading file \"%s\"\n",
- __FUNCTION__, crlfile);
- return SECFailure;
- }
- if ((certfile == NULL) && (crlfile == NULL))
- return SECFailure;
-
- /* Load the certificate first to work around RHBZ#859535. */
- cobj = NULL;
- if (certfile != NULL) {
- n_attrs = 0;
- crypto_set_attributes(&attrs[n_attrs++], CKA_CLASS,
- &certclass, sizeof(certclass));
- crypto_set_attributes(&attrs[n_attrs++], CKA_TOKEN,
- &cktrue, sizeof(cktrue));
- crypto_set_attributes(&attrs[n_attrs++], CKA_LABEL,
- (char *) certfile, strlen(certfile) + 1);
- cktrust = cert_mark_trusted ? CK_TRUE : CK_FALSE;
- crypto_set_attributes(&attrs[n_attrs++], CKA_TRUST,
- &cktrust, sizeof(cktrust));
- permanent = PR_FALSE; /* set lifetime to "session" */
- cobj = PORT_ArenaZAlloc(id_cryptoctx->pool, sizeof(*cobj));
- if (cobj == NULL)
- return SECFailure;
- cobj->name = reassemble_files_name(id_cryptoctx->pool,
- certfile, keyfile);
- if (cobj->name == NULL)
- return SECFailure;
- cobj->obj = PK11_CreateGenericObject(slot, attrs, n_attrs, permanent);
- if (cobj->obj == NULL) {
- pkiDebug("%s: error loading %scertificate \"%s\": %s\n",
- __FUNCTION__, cert_mark_trusted ? "CA " : "", certfile,
- PORT_ErrorToName(PORT_GetError()));
- status = SECFailure;
- } else {
- pkiDebug("%s: loaded %scertificate \"%s\"\n",
- __FUNCTION__, cert_mark_trusted ? "CA " : "", certfile);
- status = SECSuccess;
- /* Add it to the list of objects that we're keeping. */
- if (id_cryptoctx->id_objects != NULL)
- for (i = 0; id_cryptoctx->id_objects[i] != NULL; i++)
- continue;
- else
- i = 0;
- id_objects = PORT_ArenaZAlloc(id_cryptoctx->pool,
- sizeof(id_objects[0]) * (i + 2));
- if (id_objects != NULL) {
- n_objs = i;
- for (i = 0; i < n_objs; i++)
- id_objects[i] = id_cryptoctx->id_objects[i];
- id_objects[i++] = cobj;
- id_objects[i++] = NULL;
- id_cryptoctx->id_objects = id_objects;
- }
- /* Find the certificate that goes with this generic object. */
- memset(&tmp, 0, sizeof(tmp));
- status = PK11_ReadRawAttribute(PK11_TypeGeneric, cobj->obj,
- CKA_VALUE, &tmp);
- if (status == SECSuccess) {
- cobj->cert = CERT_FindCertByDERCert(CERT_GetDefaultCertDB(),
- &tmp);
- SECITEM_FreeItem(&tmp, PR_FALSE);
- } else {
- pkiDebug("%s: error locating certificate \"%s\"\n",
- __FUNCTION__, certfile);
- }
- /* Save a reference to the right list. */
- if (cobj->cert != NULL) {
- cert = CERT_DupCertificate(cobj->cert);
- if (cert == NULL)
- return SECFailure;
- if (cert_self) {
- /* Add to the identity list. */
- if (cert_maybe_add_to_list(id_cryptoctx->id_certs,
- cert) != SECSuccess)
- status = SECFailure;
- } else if (cert_mark_trusted) {
- /* Add to the CA list. */
- if (cert_maybe_add_to_list(id_cryptoctx->ca_certs,
- cert) != SECSuccess)
- status = SECFailure;
- } else {
- /* Don't just lose the reference. */
- CERT_DestroyCertificate(cert);
- }
- }
- }
- }
-
- /* Now load what should be the corresponding private key. */
- kobj = NULL;
- if (status == SECSuccess && keyfile != NULL) {
- n_attrs = 0;
- crypto_set_attributes(&attrs[n_attrs++], CKA_CLASS,
- &keyclass, sizeof(keyclass));
- crypto_set_attributes(&attrs[n_attrs++], CKA_TOKEN,
- &cktrue, sizeof(cktrue));
- crypto_set_attributes(&attrs[n_attrs++], CKA_LABEL,
- (char *)keyfile, strlen(keyfile) + 1);
- permanent = PR_FALSE; /* set lifetime to "session" */
- kobj = PORT_ArenaZAlloc(id_cryptoctx->pool, sizeof(*kobj));
- if (kobj == NULL)
- return SECFailure;
- kobj->obj = PK11_CreateGenericObject(slot, attrs, n_attrs, permanent);
- if (kobj->obj == NULL) {
- pkiDebug("%s: error loading key \"%s\": %s\n", __FUNCTION__,
- keyfile, PORT_ErrorToName(PORT_GetError()));
- status = SECFailure;
- } else {
- pkiDebug("%s: loaded key \"%s\"\n", __FUNCTION__, keyfile);
- status = SECSuccess;
- /* Add it to the list of objects that we're keeping. */
- if (id_cryptoctx->id_objects != NULL) {
- for (i = 0; id_cryptoctx->id_objects[i] != NULL; i++)
- continue;
- } else {
- i = 0;
- }
- id_objects = PORT_ArenaZAlloc(id_cryptoctx->pool,
- sizeof(id_objects[0]) * (i + 2));
- if (id_objects != NULL) {
- n_objs = i;
- for (i = 0; i < n_objs; i++)
- id_objects[i] = id_cryptoctx->id_objects[i];
- id_objects[i++] = kobj;
- id_objects[i++] = NULL;
- id_cryptoctx->id_objects = id_objects;
- }
- }
-
- /* "Log in" (provide an encryption password) if the PEM slot now
- * requires it. */
- PK11_TokenRefresh(slot);
-
- /*
- * Unlike most tokens, this one won't self-destruct if we throw wrong
- * passwords at it, but it will cause the module to clear the
- * needs-login flag so that we can continue importing PEM items.
- */
- if (!PK11_IsLoggedIn(slot, crypto_pwcb_prep(id_cryptoctx, cobj->name,
- context)) &&
- PK11_NeedLogin(slot)) {
- pkiDebug("%s: logging in to token \"%s\"\n",
- __FUNCTION__, PK11_GetTokenName(slot));
- if (PK11_Authenticate(slot, PR_TRUE,
- crypto_pwcb_prep(id_cryptoctx, cobj->name,
- context)) != SECSuccess) {
- pkiDebug("%s: error logging into \"%s\": %s, skipping\n",
- __FUNCTION__, PK11_GetTokenName(slot),
- PORT_ErrorToName(PORT_GetError()));
- status = SECFailure;
- PK11_DestroyGenericObject(kobj->obj);
- kobj->obj = NULL;
- }
- }
-
- /* If we loaded a key and a certificate, see if they match. */
- if (cobj != NULL && cobj->cert != NULL && kobj->obj != NULL) {
- key = PK11_FindPrivateKeyFromCert(slot, cobj->cert,
- crypto_pwcb_prep(id_cryptoctx,
- cobj->name,
- context));
- if (key == NULL) {
- pkiDebug("%s: no private key found for \"%s\"(%s), "
- "even though we just loaded that key?\n",
- __FUNCTION__,
- cobj->cert->nickname ?
- cobj->cert->nickname : "(no name)",
- certfile);
- status = SECFailure;
- } else {
- /* We don't need this reference to the key. */
- SECKEY_DestroyPrivateKey(key);
- }
- }
- }
-
- /* If we succeeded to this point, or more likely didn't do anything
- * yet, cache a CRL. */
- if ((status == SECSuccess) && (crlfile != NULL)) {
- memset(&tmp, 0, sizeof(tmp));
- if (secitem_from_file(id_cryptoctx->pool, crlfile,
- secitem_from_file_decode, &tmp) == 0) {
- crl = SECITEM_ArenaDupItem(id_cryptoctx->pool, &tmp);
- /* Count the CRLs. */
- if (id_cryptoctx->id_crls != NULL) {
- for (i = 0; id_cryptoctx->id_crls[i] != NULL; i++)
- continue;
- } else {
- i = 0;
- }
- n_crls = i;
- /* Allocate a bigger list. */
- crls = PORT_ArenaZAlloc(id_cryptoctx->pool,
- sizeof(crls[0]) * (n_crls + 2));
- for (j = 0; j < n_crls; j++)
- crls[j] = id_cryptoctx->id_crls[j];
- if (crl != NULL) {
- status = CERT_CacheCRL(CERT_GetDefaultCertDB(), crl);
- if (status == SECSuccess) {
- crls[j++] = crl;
- pkiDebug("%s: cached CRL from \"%s\"\n",
- __FUNCTION__, crlfile);
- } else
- pkiDebug("%s: error loading CRL from \"%s\": %d\n",
- __FUNCTION__, crlfile, PORT_GetError());
- }
- crls[j++] = NULL;
- id_cryptoctx->id_crls = crls;
- } else
- status = SECFailure;
- }
- return status;
-}
-
-static SECStatus
-crypto_load_dir(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- const char *dirname,
- PRBool cert_self, PRBool cert_mark_trusted, PRBool load_crl,
- pkinit_identity_crypto_context id_cryptoctx)
-{
- SECStatus status;
- DIR *dir;
- struct dirent *ent;
- char *key, *certcrl;
- const char *suffix = load_crl ? ".crl" : ".crt";
- int i;
-
- if (crypto_get_pem_slot(id_cryptoctx) == NULL) {
- pkiDebug("%s: nsspem module not loaded, "
- "not loading directory \"%s\"\n", __FUNCTION__, dirname);
- return SECFailure;
- }
- if (dirname == NULL)
- return SECFailure;
- dir = opendir(dirname);
- if (dir == NULL) {
- pkiDebug("%s: error loading directory \"%s\": %s\n",
- __FUNCTION__, dirname, strerror(errno));
- return SECFailure;
- }
- status = SECFailure;
- pkiDebug("%s: scanning directory \"%s\"\n", __FUNCTION__, dirname);
- while ((ent = readdir(dir)) != NULL) {
- i = strlen(ent->d_name);
- /* Skip over anything that isn't named "<something>.crt" or
- * "<something>.crl", whichever we want at the moment. */
- if ((i < 5) || (strcmp(ent->d_name + i - 4, suffix) != 0)) {
- pkiDebug("%s: skipping candidate \"%s/%s\"\n",
- __FUNCTION__, dirname, ent->d_name);
- continue;
- }
- /* Construct a path to the file. */
- certcrl = NULL;
- if (k5_path_join(dirname, ent->d_name, &certcrl) != 0) {
- pkiDebug("%s: error building pathname \"%s %s\"\n",
- __FUNCTION__, dirname, ent->d_name);
- continue;
- }
- key = NULL;
- if (!load_crl && cert_self) { /* No key. */
- /* Construct the matching key name. */
- if (k5_path_join(dirname, ent->d_name, &key) != 0) {
- pkiDebug("%s: error building pathname \"%s %s\"\n",
- __FUNCTION__, dirname, ent->d_name);
- free(certcrl);
- continue;
- }
- i = strlen(key);
- memcpy(key + i - 4, ".key", 5);
- }
- /* Try loading the key and file as a pair. */
- if (crypto_load_files(context,
- plg_cryptoctx,
- req_cryptoctx,
- load_crl ? NULL : certcrl,
- key,
- load_crl ? certcrl : NULL,
- cert_self, cert_mark_trusted,
- id_cryptoctx) == SECSuccess)
- status = SECSuccess;
- free(certcrl);
- free(key);
- }
- closedir(dir);
- return status;
-}
-
-static char *
-reassemble_nssdb_name(PLArenaPool *pool, const char *dbdir)
-{
- char *tmp, *ret;
-
- if (asprintf(&tmp, "NSS:%s", dbdir) < 0)
- return NULL;
- ret = PORT_ArenaStrdup(pool, tmp);
- free(tmp);
- return ret;
-}
-
-/* Load up a certificate database. */
-static krb5_error_code
-crypto_load_nssdb(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- const char *configdir,
- pkinit_identity_crypto_context id_cryptoctx)
-{
- struct _pkinit_identity_crypto_userdb *userdb, **id_userdbs;
- char *p;
- size_t spec_size;
- int i, j;
-
- if (configdir == NULL)
- return ENOENT;
-
- /* Build the spec. */
- spec_size = strlen("configDir='' flags=readOnly") +
- strlen(configdir) * 2 + 1;
- p = PORT_ArenaZAlloc(id_cryptoctx->pool, spec_size);
- if (p == NULL)
- return ENOMEM;
- strlcpy(p, "configDir='", spec_size);
- j = strlen(p);
- for (i = 0; configdir[i] != '\0'; i++) {
- if (configdir[i] == '\'')
- p[j++] = '\\'; /* Is this the right way to do
- * escaping? */
- p[j++] = configdir[i];
- }
- p[j++] = '\0';
- strlcat(p, "' flags=readOnly", spec_size);
-
- /* Count the number of modules we've already loaded. */
- if (id_cryptoctx->id_userdbs != NULL) {
- for (i = 0; id_cryptoctx->id_userdbs[i] != NULL; i++)
- continue;
- } else
- i = 0;
-
- /* Allocate a bigger list. */
- id_userdbs = PORT_ArenaZAlloc(id_cryptoctx->pool,
- sizeof(id_userdbs[0]) * (i + 2));
- for (j = 0; j < i; j++)
- id_userdbs[j] = id_cryptoctx->id_userdbs[j];
-
- /* Actually load the module. */
- userdb = PORT_ArenaZAlloc(id_cryptoctx->pool, sizeof(*userdb));
- if (userdb == NULL)
- return SECFailure;
- userdb->name = reassemble_nssdb_name(id_cryptoctx->pool, configdir);
- if (userdb->name == NULL)
- return SECFailure;
- userdb->userdb = SECMOD_OpenUserDB(p);
- if (userdb->userdb == NULL) {
- pkiDebug("%s: error loading NSS cert database \"%s\"\n",
- __FUNCTION__, configdir);
- return ENOENT;
- }
- pkiDebug("%s: opened NSS database \"%s\"\n", __FUNCTION__, configdir);
-
- /* Add us to the list and set the new list. */
- id_userdbs[i++] = userdb;
- id_userdbs[i++] = NULL;
- id_cryptoctx->id_userdbs = id_userdbs;
-
- /* Load the CAs from the database. */
- cert_load_ca_certs_from_slot(context, id_cryptoctx, userdb->userdb,
- userdb->name);
-
- /* Load the keys from the database. */
- return cert_load_certs_with_keys_from_slot(context, id_cryptoctx,
- userdb->userdb, NULL, NULL,
- userdb->name);
-}
-
-/* Load up a certificate and associated key. */
-krb5_error_code
-crypto_load_certs(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_opts *idopts,
- pkinit_identity_crypto_context id_cryptoctx,
- krb5_principal princ,
- krb5_boolean defer_id_prompts)
-{
- SECStatus status;
-
- id_cryptoctx->defer_id_prompt = defer_id_prompts;
-
- switch (idopts->idtype) {
- case IDTYPE_FILE:
- id_cryptoctx->defer_with_dummy_password = TRUE;
- status = crypto_load_files(context,
- plg_cryptoctx,
- req_cryptoctx,
- idopts->cert_filename,
- idopts->key_filename,
- NULL, PR_TRUE, PR_FALSE, id_cryptoctx);
- if (status != SECSuccess) {
- pkiDebug("%s: error loading files \"%s\" and \"%s\": %s\n",
- __FUNCTION__, idopts->cert_filename,
- idopts->key_filename, PORT_ErrorToName(PORT_GetError()));
- return defer_id_prompts ? 0 : ENOMEM;
- }
- return 0;
- break;
- case IDTYPE_NSS:
- id_cryptoctx->defer_with_dummy_password = FALSE;
- status = crypto_load_nssdb(context,
- plg_cryptoctx,
- req_cryptoctx,
- idopts->cert_filename, id_cryptoctx);
- if (status != SECSuccess) {
- pkiDebug("%s: error loading NSS certdb \"%s\": %s\n",
- __FUNCTION__, idopts->cert_filename,
- PORT_ErrorToName(PORT_GetError()));
- return ENOMEM;
- }
- return 0;
- break;
- case IDTYPE_DIR:
- id_cryptoctx->defer_with_dummy_password = TRUE;
- status = crypto_load_dir(context,
- plg_cryptoctx,
- req_cryptoctx,
- idopts->cert_filename,
- PR_TRUE, PR_FALSE, PR_FALSE, id_cryptoctx);
- if (status != SECSuccess) {
- pkiDebug("%s: error loading directory \"%s\": %s\n",
- __FUNCTION__, idopts->cert_filename,
- PORT_ErrorToName(PORT_GetError()));
- return defer_id_prompts ? 0 : ENOMEM;
- }
- return 0;
- break;
- case IDTYPE_PKCS11:
- id_cryptoctx->defer_with_dummy_password = FALSE;
- status = crypto_load_pkcs11(context,
- plg_cryptoctx,
- req_cryptoctx, idopts, id_cryptoctx);
- if (status != SECSuccess) {
- pkiDebug("%s: error loading module \"%s\": %s\n",
- __FUNCTION__, idopts->p11_module_name,
- PORT_ErrorToName(PORT_GetError()));
- return ENOMEM;
- }
- return 0;
- break;
- case IDTYPE_PKCS12:
- id_cryptoctx->defer_with_dummy_password = FALSE;
- status = crypto_load_pkcs12(context,
- plg_cryptoctx,
- req_cryptoctx,
- idopts->cert_filename, id_cryptoctx);
- if (status != SECSuccess) {
- pkiDebug("%s: error loading PKCS12 bundle \"%s\"\n",
- __FUNCTION__, idopts->cert_filename);
- return ENOMEM;
- }
- return 0;
- break;
- default:
- return EINVAL;
- break;
- }
-}
-
-/* Drop "self" certificate and keys that we didn't select. */
-krb5_error_code
-crypto_free_cert_info(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context id_cryptoctx)
-{
- /* Mimic the OpenSSL-based implementation's check first. */
- if (id_cryptoctx == NULL)
- return EINVAL;
-
- /* Maybe should we nuke the id_certs list here? */
- return 0;
-}
-
-/* Count how many candidate "self" certificates and keys we have. We could as
- * easily count the keys. */
-krb5_error_code
-crypto_cert_get_count(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context id_cryptoctx,
- int *cert_count)
-{
- CERTCertListNode *node;
-
- *cert_count = 0;
- if (!CERT_LIST_EMPTY(id_cryptoctx->id_certs))
- for (node = CERT_LIST_HEAD(id_cryptoctx->id_certs);
- (node != NULL) &&
- (node->cert != NULL) &&
- !CERT_LIST_END(node, id_cryptoctx->id_certs);
- node = CERT_LIST_NEXT(node))
- (*cert_count)++;
- pkiDebug("%s: %d candidate key/certificate pairs found\n",
- __FUNCTION__, *cert_count);
- return 0;
-}
-
-/* Start walking the list of "self" certificates and keys. */
-krb5_error_code
-crypto_cert_iteration_begin(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context id_cryptoctx,
- pkinit_cert_iter_handle *iter_handle)
-{
- PLArenaPool *pool;
- struct _pkinit_cert_iter_info *handle;
-
- if (CERT_LIST_EMPTY(id_cryptoctx->id_certs))
- return ENOENT;
- pool = PORT_NewArena(sizeof(double));
- if (pool == NULL)
- return ENOMEM;
- handle = PORT_ArenaZAlloc(pool, sizeof(*handle));
- if (handle == NULL) {
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
- handle->pool = pool;
- handle->id_cryptoctx = id_cryptoctx;
- handle->node = CERT_LIST_HEAD(handle->id_cryptoctx->id_certs);
- *iter_handle = handle;
- return 0;
-}
-
-/* Stop walking the list of "self" certificates and keys. */
-krb5_error_code
-crypto_cert_iteration_end(krb5_context context,
- pkinit_cert_iter_handle iter_handle)
-{
- PORT_FreeArena(iter_handle->pool, PR_TRUE);
- return 0;
-}
-
-/* Walk to the first/next "self" certificate and key. The cert_handle we
- * produce here has to be useful beyond the life of the iteration handle, so it
- * can't be allocated from the iteration handle's memory pool. */
-krb5_error_code
-crypto_cert_iteration_next(krb5_context context,
- pkinit_cert_iter_handle iter_handle,
- pkinit_cert_handle *cert_handle)
-{
- PLArenaPool *pool;
-
- /* Check if we're at the last node. */
- if (CERT_LIST_END(iter_handle->node,
- iter_handle->id_cryptoctx->id_certs)) {
- /* No more entries. */
- *cert_handle = NULL;
- return PKINIT_ITER_NO_MORE;
- }
- /* Create a pool to hold info about this certificate. */
- pool = PORT_NewArena(sizeof(double));
- if (pool == NULL)
- return ENOMEM;
- *cert_handle = PORT_ArenaZAlloc(pool, sizeof(**cert_handle));
- if (*cert_handle == NULL) {
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
- (*cert_handle)->pool = pool;
- /* Return a copy of the certificate in this node, and then move on to
- * the next one. */
- (*cert_handle)->id_cryptoctx = iter_handle->id_cryptoctx;
- (*cert_handle)->cert = CERT_DupCertificate(iter_handle->node->cert);
- iter_handle->node = CERT_LIST_NEXT(iter_handle->node);
- return 0;
-}
-
-/* Read names, key usage, and extended key usage from the cert. */
-static SECItem *
-cert_get_ext_by_tag(CERTCertificate *cert, SECOidTag tag)
-{
- SECOidData *oid;
- int i;
-
- oid = SECOID_FindOIDByTag(tag);
- for (i = 0;
- (cert->extensions != NULL) && (cert->extensions[i] != NULL);
- i++)
- if (SECITEM_ItemsAreEqual(&cert->extensions[i]->id, &oid->oid))
- return &cert->extensions[i]->value;
- return NULL;
-}
-
-/* Check for the presence of a particular key usage in the cert's keyUsage
- * extension field. If it's not there, NSS just sets all of the bits, which is
- * consistent with what the OpenSSL version of this does. */
-static unsigned int
-cert_get_ku_bits(krb5_context context, CERTCertificate *cert)
-{
- unsigned int ku = 0;
-
- if (cert->keyUsage & KU_DIGITAL_SIGNATURE)
- ku |= PKINIT_KU_DIGITALSIGNATURE;
- if (cert->keyUsage & KU_KEY_ENCIPHERMENT)
- ku |= PKINIT_KU_KEYENCIPHERMENT;
- return ku;
-}
-
-static unsigned int
-cert_get_eku_bits(krb5_context context, CERTCertificate *cert, PRBool kdc)
-{
- PLArenaPool *pool;
- SECItem *ext, **oids;
- SECOidData *clientauth, *serverauth, *email;
- int i;
- unsigned int eku;
-
- /* Pull out the extension. */
- ext = cert_get_ext_by_tag(cert, SEC_OID_X509_EXT_KEY_USAGE);
- if (ext == NULL)
- return 0;
-
- /* Look up the well-known OIDs. */
- clientauth = SECOID_FindOIDByTag(SEC_OID_EXT_KEY_USAGE_CLIENT_AUTH);
- serverauth = SECOID_FindOIDByTag(SEC_OID_EXT_KEY_USAGE_SERVER_AUTH);
- email = SECOID_FindOIDByTag(SEC_OID_EXT_KEY_USAGE_EMAIL_PROTECT);
-
- /* Decode the list of OIDs. */
- pool = PORT_NewArena(sizeof(double));
- oids = NULL;
- if (SEC_ASN1DecodeItem(pool, &oids,
- SEC_ASN1_GET(SEC_SequenceOfObjectIDTemplate),
- ext) != SECSuccess) {
- PORT_FreeArena(pool, PR_TRUE);
- return 0;
- }
- eku = 0;
- for (i = 0; (oids != NULL) && (oids[i] != NULL); i++) {
- if (SECITEM_ItemsAreEqual(oids[i], &email->oid))
- eku |= PKINIT_EKU_EMAILPROTECTION;
- if (kdc) {
- if (SECITEM_ItemsAreEqual(oids[i], &pkinit_kp_kdc))
- eku |= PKINIT_EKU_PKINIT;
- if (SECITEM_ItemsAreEqual(oids[i], &serverauth->oid))
- eku |= PKINIT_EKU_CLIENTAUTH;
- } else {
- if (SECITEM_ItemsAreEqual(oids[i], &pkinit_kp_client))
- eku |= PKINIT_EKU_PKINIT;
- if (SECITEM_ItemsAreEqual(oids[i], &clientauth->oid))
- eku |= PKINIT_EKU_CLIENTAUTH;
- }
- if (SECITEM_ItemsAreEqual(oids[i], &pkinit_kp_mssclogin))
- eku |= PKINIT_EKU_MSSCLOGIN;
- }
- PORT_FreeArena(pool, PR_TRUE);
- return eku;
-}
-
-krb5_error_code
-crypto_cert_get_matching_data(krb5_context context,
- pkinit_cert_handle cert_handle,
- pkinit_cert_matching_data **ret_data)
-{
- pkinit_cert_matching_data *md;
-
- md = malloc(sizeof(*md));
- if (md == NULL) {
- return ENOMEM;
- }
- md->ch = cert_handle;
- md->subject_dn = strdup(cert_handle->cert->subjectName);
- /* FIXME: string representation varies from OpenSSL's */
- md->issuer_dn = strdup(cert_handle->cert->issuerName);
- /* FIXME: string representation varies from OpenSSL's */
- md->ku_bits = cert_get_ku_bits(context, cert_handle->cert);
- md->eku_bits = cert_get_eku_bits(context, cert_handle->cert, PR_FALSE);
- if (cert_retrieve_cert_sans(context, cert_handle->cert,
- &md->sans, &md->sans, NULL) != 0)
- md->sans = NULL;
- *ret_data = md;
- return 0;
-}
-
-/* Free up the data for this certificate. */
-krb5_error_code
-crypto_cert_release(krb5_context context, pkinit_cert_handle cert_handle)
-{
- CERT_DestroyCertificate(cert_handle->cert);
- PORT_FreeArena(cert_handle->pool, PR_TRUE);
- return 0;
-}
-
-/* Free names, key usage, and extended key usage from the cert matching data
- * structure -- everything except the cert_handle it contains, anyway. */
-krb5_error_code
-crypto_cert_free_matching_data(krb5_context context,
- pkinit_cert_matching_data *data)
-{
- free(data->subject_dn);
- free(data->issuer_dn);
- free(data);
- return 0;
-}
-
-/* Mark the cert tracked in the matching data structure as the one we're going
- * to use. */
-krb5_error_code
-crypto_cert_select(krb5_context context, pkinit_cert_matching_data *data)
-{
- CERTCertificate *cert;
-
- cert = CERT_DupCertificate(data->ch->cert);
- if (data->ch->id_cryptoctx->id_cert != NULL)
- CERT_DestroyCertificate(data->ch->id_cryptoctx->id_cert);
- data->ch->id_cryptoctx->id_cert = cert;
- crypto_update_signer_identity(context, data->ch->id_cryptoctx);
- return 0;
-}
-
-/* Try to select the "default" cert, which for now is the only cert, if we only
- * have one. */
-krb5_error_code
-crypto_cert_select_default(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context id_cryptoctx)
-{
- CERTCertListNode *node;
- CERTCertificate *cert;
- krb5_principal *sans;
- krb5_data *c;
- krb5_error_code code;
- int result, count, i;
-
- result = crypto_cert_get_count(context,
- plg_cryptoctx,
- req_cryptoctx, id_cryptoctx, &count);
- if (result != 0)
- return result;
- if (count == 1)
- /* use the only cert */
- cert = (CERT_LIST_HEAD(id_cryptoctx->id_certs))->cert;
- else {
- pkiDebug("%s: searching for a KDC certificate\n", __FUNCTION__);
- /* look for a cert that includes a TGS principal name */
- cert = NULL;
- for (node = CERT_LIST_HEAD(id_cryptoctx->id_certs);
- (node != NULL) &&
- (node->cert != NULL) &&
- !CERT_LIST_END(node, id_cryptoctx->id_certs);
- node = CERT_LIST_NEXT(node)) {
- sans = NULL;
- pkiDebug("%s: checking candidate certificate \"%s\"\n",
- __FUNCTION__, node->cert->subjectName);
- code = cert_retrieve_cert_sans(context, node->cert,
- &sans, NULL, NULL);
- if ((code == 0) && (sans != NULL)) {
- for (i = 0; sans[i] != NULL; i++) {
- c = krb5_princ_component(context, sans[i], 0);
- if ((c->length == KRB5_TGS_NAME_SIZE) &&
- (memcmp(c->data, KRB5_TGS_NAME,
- KRB5_TGS_NAME_SIZE) == 0)) {
- cert = node->cert;
- pkiDebug("%s: selecting %s "
- "certificate \"%s\"\n",
- __FUNCTION__,
- KRB5_TGS_NAME, cert->subjectName);
- }
- krb5_free_principal(context, sans[i]);
- }
- free(sans);
- sans = NULL;
- }
- if (cert != NULL)
- break;
- }
- if (cert == NULL)
- return ENOENT;
- }
- if (id_cryptoctx->id_cert != NULL)
- CERT_DestroyCertificate(id_cryptoctx->id_cert);
- id_cryptoctx->id_cert = CERT_DupCertificate(cert);
- crypto_update_signer_identity(context, id_cryptoctx);
- return 0;
-}
-
-krb5_error_code
-crypto_load_cas_and_crls(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_opts * idopts,
- pkinit_identity_crypto_context id_cryptoctx,
- int idtype, int catype, char *id)
-{
- SECStatus status;
- PRBool cert_self, cert_mark_trusted, load_crl;
-
- /* Figure out what we're doing here. */
- switch (catype) {
- case CATYPE_ANCHORS:
- /* Screen out source types we can't use. */
- switch (idtype) {
- case IDTYPE_FILE:
- case IDTYPE_DIR:
- case IDTYPE_NSS:
- /* We only support these sources. */
- break;
- default:
- return EINVAL;
- break;
- }
- /* Mark certs we load as trusted roots. */
- cert_self = PR_FALSE;
- cert_mark_trusted = PR_TRUE;
- load_crl = PR_FALSE;
- break;
- case CATYPE_INTERMEDIATES:
- /* Screen out source types we can't use. */
- switch (idtype) {
- case IDTYPE_FILE:
- case IDTYPE_DIR:
- case IDTYPE_NSS:
- /* We only support these sources. */
- break;
- default:
- return EINVAL;
- break;
- }
- /* Hang on to certs as reference material. */
- cert_self = PR_FALSE;
- cert_mark_trusted = PR_FALSE;
- load_crl = PR_FALSE;
- break;
- case CATYPE_CRLS:
- /* Screen out source types we can't use. */
- switch (idtype) {
- case IDTYPE_FILE:
- case IDTYPE_DIR:
- /* We only support these sources. */
- break;
- default:
- return EINVAL;
- break;
- }
- /* No certs, just CRLs. */
- cert_self = PR_FALSE;
- cert_mark_trusted = PR_FALSE;
- load_crl = PR_TRUE;
- break;
- default:
- return ENOSYS;
- break;
- }
-
- switch (idtype) {
- case IDTYPE_FILE:
- status = crypto_load_files(context,
- plg_cryptoctx,
- req_cryptoctx,
- load_crl ? NULL : id,
- NULL,
- load_crl ? id : NULL,
- cert_self, cert_mark_trusted, id_cryptoctx);
- if (status != SECSuccess) {
- pkiDebug("%s: error loading file \"%s\"\n", __FUNCTION__, id);
- return ENOMEM;
- }
- return 0;
- break;
- case IDTYPE_NSS:
- status = crypto_load_nssdb(context,
- plg_cryptoctx,
- req_cryptoctx, id, id_cryptoctx);
- if (status != SECSuccess) {
- pkiDebug("%s: error loading NSS certdb \"%s\"\n",
- __FUNCTION__, idopts->cert_filename);
- return ENOMEM;
- }
- return 0;
- break;
- case IDTYPE_DIR:
- status = crypto_load_dir(context,
- plg_cryptoctx,
- req_cryptoctx,
- id,
- cert_self, cert_mark_trusted, load_crl,
- id_cryptoctx);
- if (status != SECSuccess) {
- pkiDebug("%s: error loading directory \"%s\"\n", __FUNCTION__, id);
- return ENOMEM;
- }
- return 0;
- break;
- default:
- return EINVAL;
- break;
- }
-}
-
-/* Retrieve the client's copy of the KDC's certificate. */
-krb5_error_code
-pkinit_get_kdc_cert(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context id_cryptoctx,
- krb5_principal princ)
-{
- /* Nothing to do. */
- return 0;
-}
-
-/* Create typed-data with sets of acceptable DH parameters. */
-krb5_error_code
-pkinit_create_td_dh_parameters(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context id_cryptoctx,
- pkinit_plg_opts *opts, krb5_pa_data ***pa_data)
-{
- struct domain_parameters *params;
- SECItem tmp, *oid;
- krb5_algorithm_identifier id[sizeof(oakley_groups) /
- sizeof(oakley_groups[0])];
- krb5_algorithm_identifier *ids[(sizeof(id) / sizeof(id[0])) + 1];
- unsigned int i, j;
- krb5_data *data;
- krb5_pa_data **typed_data;
- krb5_error_code code;
-
- *pa_data = NULL;
-
- /* Fetch the algorithm OID. */
- oid = get_oid_from_tag(SEC_OID_X942_DIFFIE_HELMAN_KEY);
- if (oid == NULL)
- return ENOMEM;
- /* Walk the lists of parameters that we know. */
- for (i = 0, j = 0; i < sizeof(id) / sizeof(id[0]); i++) {
- if (oakley_groups[i].bits < opts->dh_min_bits)
- continue;
- /* Encode these parameters for use as algorithm parameters. */
- if (oakley_parse_group(req_cryptoctx->pool, &oakley_groups[i],
- &params) != 0)
- continue;
- memset(&params, 0, sizeof(params));
- if (SEC_ASN1EncodeItem(req_cryptoctx->pool, &tmp,
- params,
- domain_parameters_template) != SECSuccess)
- continue;
- /* Add it to the list. */
- memset(&id[j], 0, sizeof(id[j]));
- id[j].algorithm.data = (char *)oid->data;
- id[j].algorithm.length = oid->len;
- id[j].parameters.data = (char *)tmp.data;
- id[j].parameters.length = tmp.len;
- ids[j] = &id[j];
- j++;
- }
- if (j == 0)
- return ENOENT;
- ids[j] = NULL;
- /* Pass it back up. */
- data = NULL;
- code = (*k5int_encode_krb5_td_dh_parameters)(ids, &data);
- if (code != 0)
- return code;
- typed_data = malloc(sizeof(*typed_data) * 2);
- if (typed_data == NULL) {
- krb5_free_data(context, data);
- return ENOMEM;
- }
- typed_data[0] = malloc(sizeof(**typed_data));
- if (typed_data[0] == NULL) {
- free(typed_data);
- krb5_free_data(context, data);
- return ENOMEM;
- }
- typed_data[0]->pa_type = TD_DH_PARAMETERS;
- typed_data[0]->length = data->length;
- typed_data[0]->contents = (unsigned char *) data->data;
- typed_data[1] = NULL;
- *pa_data = typed_data;
- free(data);
- return code;
-}
-
-/* Parse typed-data with sets of acceptable DH parameters and return the
- * minimum prime size that the KDC will accept. */
-krb5_error_code
-pkinit_process_td_dh_params(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context id_cryptoctx,
- krb5_algorithm_identifier **algId,
- int *new_dh_size)
-{
- struct domain_parameters params;
- SECItem item;
- int i, size;
-
- /* Set an initial reasonable guess if we got no hints that we could
- * parse. */
- *new_dh_size = 2048;
- for (i = 0; (algId != NULL) && (algId[i] != NULL); i++) {
- /* Decode the domain parameters. */
- item.len = algId[i]->parameters.length;
- item.data = (unsigned char *)algId[i]->parameters.data;
- memset(&params, 0, sizeof(params));
- if (SEC_ASN1DecodeItem(req_cryptoctx->pool, &params,
- domain_parameters_template,
- &item) != SECSuccess)
- continue;
- /* Count the size of the prime by finding the first non-zero
- * byte and working out the size of the integer. */
- size = get_integer_bits(&params.p);
- /* If this is the first parameter set, or the current parameter
- * size is lower than our previous guess, use it. */
- if ((i == 0) || (size < *new_dh_size))
- *new_dh_size = size;
- }
- return 0;
-}
-
-/* Create typed-data with the client cert that we didn't like. */
-krb5_error_code
-pkinit_create_td_invalid_certificate(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context
- id_cryptoctx, krb5_pa_data ***pa_data)
-{
- CERTCertificate *invalid;
- krb5_external_principal_identifier id;
- krb5_external_principal_identifier *ids[2];
- struct issuer_and_serial_number isn;
- krb5_data *data;
- SECItem item;
- krb5_pa_data **typed_data;
- krb5_error_code code;
-
- *pa_data = NULL;
-
- /* We didn't trust the peer's certificate. FIXME: or was it a
- * certificate that was somewhere in its certifying chain? */
- if (req_cryptoctx->peer_cert == NULL)
- return ENOENT;
- invalid = req_cryptoctx->peer_cert;
-
- /* Fill in the identifier. */
- memset(&id, 0, sizeof(id));
- if (req_cryptoctx->peer_cert->keyIDGenerated) {
- isn.issuer = invalid->derIssuer;
- isn.serial = invalid->serialNumber;
- if (SEC_ASN1EncodeItem(req_cryptoctx->pool, &item, &isn,
- issuer_and_serial_number_template) != &item)
- return ENOMEM;
- id.issuerAndSerialNumber.data = (char *)item.data;
- id.issuerAndSerialNumber.length = item.len;
- } else {
- item = invalid->subjectKeyID;
- id.subjectKeyIdentifier.data = (char *)item.data;
- id.subjectKeyIdentifier.length = item.len;
- }
- ids[0] = &id;
- ids[1] = NULL;
-
- /* Pass it back up. */
- data = NULL;
- code = (*k5int_encode_krb5_td_trusted_certifiers)(ids, &data);
- if (code != 0)
- return code;
- typed_data = malloc(sizeof(*typed_data) * 2);
- if (typed_data == NULL) {
- krb5_free_data(context, data);
- return ENOMEM;
- }
- typed_data[0] = malloc(sizeof(**typed_data));
- if (typed_data[0] == NULL) {
- free(typed_data);
- krb5_free_data(context, data);
- return ENOMEM;
- }
- typed_data[0]->pa_type = TD_INVALID_CERTIFICATES;
- typed_data[0]->length = data->length;
- typed_data[0]->contents = (unsigned char *) data->data;
- typed_data[1] = NULL;
- *pa_data = typed_data;
- free(data);
- return code;
-}
-
-/* Create typed-data with a list of certifiers that we would accept. */
-krb5_error_code
-pkinit_create_td_trusted_certifiers(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context
- id_cryptoctx, krb5_pa_data ***pa_data)
-{
- krb5_external_principal_identifier **ids;
- krb5_external_principal_identifier *id;
- struct issuer_and_serial_number isn;
- krb5_data *data;
- SECItem item;
- krb5_pa_data **typed_data;
- krb5_error_code code;
- int i;
- unsigned int trustf;
- SECStatus status;
- PK11SlotList *slist;
- PK11SlotListElement *sle;
- CERTCertificate *cert;
- CERTCertList *sclist, *clist;
- CERTCertListNode *node;
-
- *pa_data = NULL;
-
- /* Build the list of trusted roots. */
- clist = CERT_NewCertList();
- if (clist == NULL)
- return ENOMEM;
-
- /* Get the list of tokens. All of them. */
- slist = PK11_GetAllTokens(CKM_INVALID_MECHANISM, PR_FALSE,
- PR_FALSE,
- crypto_pwcb_prep(id_cryptoctx, NULL, context));
- if (slist == NULL) {
- CERT_DestroyCertList(clist);
- return ENOENT;
- }
-
- /* Walk the list of tokens. */
- i = 0;
- status = SECSuccess;
- for (sle = slist->head; sle != NULL; sle = sle->next) {
- /* Skip over slots we would still need to log in to before using. */
- if (!PK11_IsLoggedIn(sle->slot,
- crypto_pwcb_prep(id_cryptoctx, NULL, context)) &&
- PK11_NeedLogin(sle->slot)) {
- pkiDebug("%s: skipping token \"%s\"\n",
- __FUNCTION__, PK11_GetTokenName(sle->slot));
- continue;
- }
- /* Get the list of certs, and skip the slot if it doesn't have
- * any. */
- sclist = PK11_ListCertsInSlot(sle->slot);
- if (sclist == NULL) {
- pkiDebug("%s: nothing found in token \"%s\"\n",
- __FUNCTION__, PK11_GetTokenName(sle->slot));
- continue;
- }
- if (CERT_LIST_EMPTY(sclist)) {
- CERT_DestroyCertList(sclist);
- pkiDebug("%s: nothing found in token \"%s\"\n",
- __FUNCTION__, PK11_GetTokenName(sle->slot));
- continue;
- }
- /* Walk the list of certs, and for each one that's a trusted
- * root, add it to the list. */
- for (node = CERT_LIST_HEAD(sclist);
- (node != NULL) &&
- (node->cert != NULL) &&
- !CERT_LIST_END(node, sclist);
- node = CERT_LIST_NEXT(node)) {
- /* If we have no trust for it, we can't trust it. */
- if (node->cert->trust == NULL)
- continue;
- /* We need to trust it to issue client certs. */
- trustf = SEC_GET_TRUST_FLAGS(node->cert->trust, trustSSL);
- if (!(trustf & CERTDB_TRUSTED_CLIENT_CA))
- continue;
- /* DestroyCertList frees all of the certs in the list,
- * so we need to create a copy that it can own. */
- cert = CERT_DupCertificate(node->cert);
- if (cert_maybe_add_to_list(clist, cert) != SECSuccess)
- status = ENOMEM;
- else
- i++;
- }
- CERT_DestroyCertList(sclist);
- }
- PK11_FreeSlotList(slist);
- if (status != SECSuccess) {
- CERT_DestroyCertList(clist);
- return ENOMEM;
- }
-
- /* Allocate some temporary storage. */
- id = PORT_ArenaZAlloc(req_cryptoctx->pool, sizeof(**ids) * i);
- ids = PORT_ArenaZAlloc(req_cryptoctx->pool, sizeof(*ids) * (i + 1));
- if ((id == NULL) || (ids == NULL)) {
- CERT_DestroyCertList(clist);
- return ENOMEM;
- }
-
- /* Fill in the identifiers. */
- i = 0;
- for (node = CERT_LIST_HEAD(clist);
- (node != NULL) &&
- (node->cert != NULL) &&
- !CERT_LIST_END(node, clist);
- node = CERT_LIST_NEXT(node)) {
- if (node->cert->keyIDGenerated) {
- isn.issuer = node->cert->derIssuer;
- isn.serial = node->cert->serialNumber;
- if (SEC_ASN1EncodeItem(req_cryptoctx->pool, &item, &isn,
- issuer_and_serial_number_template) !=
- &item) {
- CERT_DestroyCertList(clist);
- return ENOMEM;
- }
- id[i].issuerAndSerialNumber.data = (char *)item.data;
- id[i].issuerAndSerialNumber.length = item.len;
- } else {
- item = node->cert->subjectKeyID;
- id[i].subjectKeyIdentifier.data = (char *)item.data;
- id[i].subjectKeyIdentifier.length = item.len;
- }
- ids[i] = &id[i];
- i++;
- }
- ids[i] = NULL;
-
- /* Pass the list back up. */
- data = NULL;
- code = (*k5int_encode_krb5_td_trusted_certifiers)(ids, &data);
- CERT_DestroyCertList(clist);
- if (code != 0)
- return code;
- typed_data = malloc(sizeof(*typed_data) * 2);
- if (typed_data == NULL) {
- krb5_free_data(context, data);
- return ENOMEM;
- }
- typed_data[0] = malloc(sizeof(**typed_data));
- if (typed_data[0] == NULL) {
- free(typed_data);
- krb5_free_data(context, data);
- return ENOMEM;
- }
- typed_data[0]->pa_type = TD_TRUSTED_CERTIFIERS;
- typed_data[0]->length = data->length;
- typed_data[0]->contents = (unsigned char *) data->data;
- typed_data[1] = NULL;
- *pa_data = typed_data;
- free(data);
- return code;
-}
-
-krb5_error_code
-pkinit_process_td_trusted_certifiers(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context
- id_cryptoctx,
- krb5_external_principal_identifier **
- trustedCertifiers,
- int td_type)
-{
- /* We should select a different client certificate based on the list of
- * trusted certifiers, but for now we'll just chicken out. */
- return KRB5KDC_ERR_PREAUTH_FAILED;
-}
-
-/* Check if the encoded issuer/serial matches our (the KDC's) certificate. */
-krb5_error_code
-pkinit_check_kdc_pkid(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context id_cryptoctx,
- unsigned char *pkid_buf,
- unsigned int pkid_len, int *valid_kdcPkId)
-{
- PLArenaPool *pool;
- CERTCertificate *cert;
- SECItem pkid;
- struct issuer_and_serial_number isn;
-
- pool = PORT_NewArena(sizeof(double));
- if (pool == NULL)
- return ENOMEM;
-
- /* Verify that we have selected a certificate for our (the KDC's) own
- * use. */
- if (id_cryptoctx->id_cert == NULL)
- return ENOENT;
- cert = id_cryptoctx->id_cert;
-
- /* Decode the pair. */
- pkid.data = pkid_buf;
- pkid.len = pkid_len;
- memset(&isn, 0, sizeof(isn));
- if (SEC_ASN1DecodeItem(pool, &isn, issuer_and_serial_number_template,
- &pkid) != SECSuccess) {
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
-
- /* Compare the issuer and serial number. */
- *valid_kdcPkId = SECITEM_ItemsAreEqual(&isn.issuer,
- &cert->derIssuer) &&
- SECITEM_ItemsAreEqual(&isn.serial, &cert->serialNumber);
-
- /* Clean up. */
- PORT_FreeArena(pool, PR_TRUE);
-
- return 0;
-}
-
-krb5_error_code
-pkinit_identity_set_prompter(pkinit_identity_crypto_context id_cryptoctx,
- krb5_prompter_fct prompter, void *prompter_data)
-{
- id_cryptoctx->pwcb_args.prompter = prompter;
- id_cryptoctx->pwcb_args.prompter_data = prompter_data;
- return 0;
-}
-
-/* Convert a DH secret and optional data to a keyblock using the specified
- * digest and a big-endian counter of the specified length that starts at the
- * specified value. */
-static krb5_error_code
-pkinit_octetstring_hkdf(krb5_context context,
- SECOidTag hash_alg,
- int counter_start, size_t counter_length,
- krb5_enctype etype,
- unsigned char *dh_key, unsigned int dh_key_len,
- char *other_data, unsigned int other_data_len,
- krb5_keyblock *krb5key)
-{
- PK11Context *ctx;
- unsigned int left, length, rnd_len;
- unsigned char counter[8], buf[512]; /* the longest digest we support */
- int i;
- char *rnd_buf;
- size_t kbyte, klength;
- krb5_data rnd_data;
- krb5_error_code result;
- NSSInitContext *ncontext;
-
- if (counter_length > sizeof(counter))
- return EINVAL;
- result = krb5_c_keylengths(context, etype, &kbyte, &klength);
- if (result != 0)
- return result;
- rnd_buf = malloc(dh_key_len);
- if (rnd_buf == NULL)
- return ENOMEM;
-
- memset(counter, 0, sizeof(counter));
- for (i = sizeof(counter) - 1; i >= 0; i--)
- counter[i] = (counter_start >> (8 * (counter_length - 1 - i))) & 0xff;
- rnd_len = kbyte;
- left = rnd_len;
- ncontext = NSS_InitContext(DEFAULT_CONFIGDIR,
- NULL,
- NULL,
- NULL,
- NULL,
- NSS_INIT_READONLY |
- NSS_INIT_NOCERTDB |
- NSS_INIT_NOMODDB |
- NSS_INIT_FORCEOPEN |
- NSS_INIT_NOROOTINIT |
- NSS_INIT_PK11RELOAD);
- while (left > 0) {
- ctx = PK11_CreateDigestContext(hash_alg);
- if (ctx == NULL) {
- krb5int_zap(buf, sizeof(buf));
- krb5int_zap(rnd_buf, dh_key_len);
- free(rnd_buf);
- return ENOMEM;
- }
- if (PK11_DigestBegin(ctx) != SECSuccess) {
- PK11_DestroyContext(ctx, PR_TRUE);
- krb5int_zap(buf, sizeof(buf));
- krb5int_zap(rnd_buf, dh_key_len);
- free(rnd_buf);
- return ENOMEM;
- }
- if (PK11_DigestOp(ctx, counter, counter_length) != SECSuccess) {
- PK11_DestroyContext(ctx, PR_TRUE);
- krb5int_zap(buf, sizeof(buf));
- krb5int_zap(rnd_buf, dh_key_len);
- free(rnd_buf);
- return ENOMEM;
- }
- if (PK11_DigestOp(ctx, dh_key, dh_key_len) != SECSuccess) {
- PK11_DestroyContext(ctx, PR_TRUE);
- krb5int_zap(buf, sizeof(buf));
- krb5int_zap(rnd_buf, dh_key_len);
- free(rnd_buf);
- return ENOMEM;
- }
- if ((other_data_len > 0) &&
- (PK11_DigestOp(ctx, (const unsigned char *) other_data,
- other_data_len) != SECSuccess)) {
- PK11_DestroyContext(ctx, PR_TRUE);
- krb5int_zap(buf, sizeof(buf));
- krb5int_zap(rnd_buf, dh_key_len);
- free(rnd_buf);
- return ENOMEM;
- }
- if (PK11_DigestFinal(ctx, buf, &length, sizeof(buf)) != SECSuccess) {
- PK11_DestroyContext(ctx, PR_TRUE);
- krb5int_zap(buf, sizeof(buf));
- krb5int_zap(rnd_buf, dh_key_len);
- free(rnd_buf);
- return ENOMEM;
- }
- PK11_DestroyContext(ctx, PR_TRUE);
- if (left < length) {
- length = left;
- }
- memcpy(rnd_buf + rnd_len - left, buf, length);
- left -= length;
- for (i = counter_length - 1; i >= 0; i--) {
- counter[i] = ((counter[i] + 1) & 0xff);
- if (counter[i] != 0)
- break;
- }
- }
-
- if (NSS_ShutdownContext(ncontext) != SECSuccess)
- pkiDebug("%s: error shutting down context\n", __FUNCTION__);
-
- krb5key->contents = malloc(klength);
- if (krb5key->contents == NULL) {
- krb5key->length = 0;
- return ENOMEM;
- }
- krb5key->length = klength;
- krb5key->enctype = etype;
-
- rnd_data.data = rnd_buf;
- rnd_data.length = rnd_len;
- result = krb5_c_random_to_key(context, etype, &rnd_data, krb5key);
-
- krb5int_zap(buf, sizeof(buf));
- krb5int_zap(rnd_buf, dh_key_len);
- free(rnd_buf);
-
- return result;
-}
-
-/* Convert a DH secret to a keyblock, RFC4556-style. */
-krb5_error_code
-pkinit_octetstring2key(krb5_context context,
- krb5_enctype etype,
- unsigned char *dh_key,
- unsigned int dh_key_len, krb5_keyblock *krb5key)
-{
- return pkinit_octetstring_hkdf(context,
- SEC_OID_SHA1, 0, 1, etype,
- dh_key, dh_key_len, NULL, 0,
- krb5key);
-}
-
-/* Return TRUE if the item and the "algorithm" part of the algorithm identifier
- * are the same. */
-static PRBool
-data_and_ptr_and_length_equal(const krb5_data *data,
- const void *ptr, size_t len)
-{
- return (data->length == len) && (memcmp(data->data, ptr, len) == 0);
-}
-
-/* Encode the other info used by the agility KDF. Taken almost verbatim from
- * parts of the agility KDF in pkinit_crypto_openssl.c */
-static krb5_error_code
-encode_agility_kdf_other_info(krb5_context context,
- krb5_data *alg_oid,
- krb5_const_principal party_u_info,
- krb5_const_principal party_v_info,
- krb5_enctype enctype,
- krb5_data *as_req,
- krb5_data *pk_as_rep,
- krb5_data **other_info)
-{
- krb5_error_code retval = 0;
- krb5_sp80056a_other_info other_info_fields;
- krb5_pkinit_supp_pub_info supp_pub_info_fields;
- krb5_data *supp_pub_info = NULL;
- krb5_algorithm_identifier alg_id;
-
- /* If this is anonymous pkinit, we need to use the anonymous principal for
- * party_u_info */
- if (party_u_info &&
- krb5_principal_compare_any_realm(context, party_u_info,
- krb5_anonymous_principal()))
- party_u_info = krb5_anonymous_principal();
-
- /* Encode the ASN.1 octet string for "SuppPubInfo" */
- supp_pub_info_fields.enctype = enctype;
- supp_pub_info_fields.as_req = *as_req;
- supp_pub_info_fields.pk_as_rep = *pk_as_rep;
- retval = encode_krb5_pkinit_supp_pub_info(&supp_pub_info_fields,
- &supp_pub_info);
- if (retval != 0)
- goto cleanup;
-
- /* Now encode the ASN.1 octet string for "OtherInfo" */
- memset(&alg_id, 0, sizeof alg_id);
- alg_id.algorithm = *alg_oid; /*alias, don't have to free it*/
-
- other_info_fields.algorithm_identifier = alg_id;
- other_info_fields.party_u_info = (krb5_principal) party_u_info;
- other_info_fields.party_v_info = (krb5_principal) party_v_info;
- other_info_fields.supp_pub_info = *supp_pub_info;
- retval = encode_krb5_sp80056a_other_info(&other_info_fields, other_info);
- if (retval != 0)
- goto cleanup;
-
-cleanup:
- krb5_free_data(context, supp_pub_info);
-
- return retval;
-}
-
-/* Convert a DH secret to a keyblock using the key derivation function
- * identified by the passed-in algorithm identifier. Return ENOSYS if it's not
- * one that we support. */
-krb5_error_code
-pkinit_alg_agility_kdf(krb5_context context,
- krb5_data *secret,
- krb5_data *alg_oid,
- krb5_const_principal party_u_info,
- krb5_const_principal party_v_info,
- krb5_enctype enctype,
- krb5_data *as_req,
- krb5_data *pk_as_rep,
- krb5_keyblock *key_block)
-{
- krb5_data *other_info = NULL;
- krb5_error_code retval = ENOSYS;
-
- retval = encode_agility_kdf_other_info(context,
- alg_oid,
- party_u_info,
- party_v_info,
- enctype, as_req, pk_as_rep,
- &other_info);
- if (retval != 0)
- return retval;
-
- if (data_and_ptr_and_length_equal(alg_oid, krb5_pkinit_sha512_oid,
- krb5_pkinit_sha512_oid_len))
- retval = pkinit_octetstring_hkdf(context,
- SEC_OID_SHA512, 1, 4, enctype,
- (unsigned char *)secret->data,
- secret->length, other_info->data,
- other_info->length, key_block);
- else if (data_and_ptr_and_length_equal(alg_oid, krb5_pkinit_sha256_oid,
- krb5_pkinit_sha256_oid_len))
- retval = pkinit_octetstring_hkdf(context,
- SEC_OID_SHA256, 1, 4, enctype,
- (unsigned char *)secret->data,
- secret->length, other_info->data,
- other_info->length, key_block);
- else if (data_and_ptr_and_length_equal(alg_oid, krb5_pkinit_sha1_oid,
- krb5_pkinit_sha1_oid_len))
- retval = pkinit_octetstring_hkdf(context,
- SEC_OID_SHA1, 1, 4, enctype,
- (unsigned char *)secret->data,
- secret->length, other_info->data,
- other_info->length, key_block);
- else
- retval = KRB5KDC_ERR_NO_ACCEPTABLE_KDF;
-
- krb5_free_data(context, other_info);
-
- return retval;
-}
-
-static int
-cert_add_string(unsigned char ***list, int *count,
- int len, const unsigned char *value)
-{
- unsigned char **tmp;
-
- tmp = malloc(sizeof(tmp[0]) * (*count + 2));
- if (tmp == NULL) {
- return ENOMEM;
- }
- memcpy(tmp, *list, *count * sizeof(tmp[0]));
- tmp[*count] = malloc(len + 1);
- if (tmp[*count] == NULL) {
- free(tmp);
- return ENOMEM;
- }
- memcpy(tmp[*count], value, len);
- tmp[*count][len] = '\0';
- tmp[*count + 1] = NULL;
- if (*count != 0) {
- free(*list);
- }
- *list = tmp;
- (*count)++;
- return 0;
-}
-
-static int
-cert_add_princ(krb5_context context, krb5_principal princ,
- krb5_principal **sans_inout, int *n_sans_inout)
-{
- krb5_principal *tmp;
-
- tmp = malloc(sizeof(krb5_principal *) * (*n_sans_inout + 2));
- if (tmp == NULL) {
- return ENOMEM;
- }
- memcpy(tmp, *sans_inout, sizeof(tmp[0]) * *n_sans_inout);
- if (krb5_copy_principal(context, princ, &tmp[*n_sans_inout]) != 0) {
- free(tmp);
- return ENOMEM;
- }
- tmp[*n_sans_inout + 1] = NULL;
- if (*n_sans_inout > 0) {
- free(*sans_inout);
- }
- *sans_inout = tmp;
- (*n_sans_inout)++;
- return 0;
-}
-
-static int
-cert_add_upn(PLArenaPool * pool, krb5_context context, SECItem *name,
- krb5_principal **sans_inout, int *n_sans_inout)
-{
- SECItem decoded;
- char *unparsed;
- krb5_principal tmp;
- int i;
-
- /* Decode the string. */
- memset(&decoded, 0, sizeof(decoded));
- if (SEC_ASN1DecodeItem(pool, &decoded,
- SEC_ASN1_GET(SEC_UTF8StringTemplate),
- name) != SECSuccess) {
- return ENOMEM;
- }
- unparsed = malloc(decoded.len + 1);
- if (unparsed == NULL) {
- return ENOMEM;
- }
- memcpy(unparsed, decoded.data, decoded.len);
- unparsed[decoded.len] = '\0';
- /* Parse the string into a principal name. */
- if (krb5_parse_name(context, unparsed, &tmp) != 0) {
- free(unparsed);
- return ENOMEM;
- }
- free(unparsed);
- /* Unparse the name back into a string and make sure it matches what
- * was in the certificate. */
- if (krb5_unparse_name(context, tmp, &unparsed) != 0) {
- krb5_free_principal(context, tmp);
- return ENOMEM;
- }
- if ((strlen(unparsed) != decoded.len) ||
- (memcmp(unparsed, decoded.data, decoded.len) != 0)) {
- krb5_free_unparsed_name(context, unparsed);
- krb5_free_principal(context, tmp);
- return ENOMEM;
- }
- /* Add the principal name to the list. */
- i = cert_add_princ(context, tmp, sans_inout, n_sans_inout);
- krb5_free_unparsed_name(context, unparsed);
- krb5_free_principal(context, tmp);
- return i;
-}
-
-static int
-cert_add_kpn(PLArenaPool * pool, krb5_context context, SECItem *name,
- krb5_principal** sans_inout, int *n_sans_inout)
-{
- struct kerberos_principal_name kname;
- SECItem **names;
- krb5_data *comps;
- krb5_principal_data tmp;
- unsigned long name_type;
- int i, j;
-
- /* Decode the structure. */
- memset(&kname, 0, sizeof(kname));
- if (SEC_ASN1DecodeItem(pool, &kname,
- kerberos_principal_name_template,
- name) != SECSuccess)
- return ENOMEM;
-
- /* Recover the name type and count the components. */
- if (SEC_ASN1DecodeInteger(&kname.principal_name.name_type,
- &name_type) != SECSuccess)
- return ENOMEM;
- names = kname.principal_name.name_string;
- for (i = 0; (names != NULL) && (names[i] != NULL); i++)
- continue;
- comps = malloc(sizeof(comps[0]) * i);
-
- /* Fake up a principal structure. */
- for (j = 0; j < i; j++) {
- comps[j].length = names[j]->len;
- comps[j].data = (char *) names[j]->data;
- }
- memset(&tmp, 0, sizeof(tmp));
- tmp.type = name_type;
- tmp.realm.length = kname.realm.len;
- tmp.realm.data = (char *) kname.realm.data;
- tmp.length = i;
- tmp.data = comps;
-
- /* Add the principal name to the list. */
- i = cert_add_princ(context, &tmp, sans_inout, n_sans_inout);
- free(comps);
- return i;
-}
-
-static const char *
-crypto_get_identity_by_slot(krb5_context context,
- pkinit_identity_crypto_context id_cryptoctx,
- PK11SlotInfo *slot)
-{
- PK11SlotInfo *mslot;
- struct _pkinit_identity_crypto_userdb *userdb;
- struct _pkinit_identity_crypto_module *module;
- int i, j;
-
- mslot = id_cryptoctx->id_p12_slot.slot;
- if ((mslot != NULL) && (PK11_GetSlotID(mslot) == PK11_GetSlotID(slot)))
- return id_cryptoctx->id_p12_slot.p12name;
- for (i = 0;
- (id_cryptoctx->id_userdbs != NULL) &&
- (id_cryptoctx->id_userdbs[i] != NULL);
- i++) {
- userdb = id_cryptoctx->id_userdbs[i];
- if (PK11_GetSlotID(userdb->userdb) == PK11_GetSlotID(slot))
- return userdb->name;
- }
- for (i = 0;
- (id_cryptoctx->id_modules != NULL) &&
- (id_cryptoctx->id_modules[i] != NULL);
- i++) {
- module = id_cryptoctx->id_modules[i];
- for (j = 0; j < module->module->slotCount; j++) {
- mslot = module->module->slots[j];
- if (PK11_GetSlotID(mslot) == PK11_GetSlotID(slot))
- return module->name;
- }
- }
- return NULL;
-}
-
-static void
-crypto_update_signer_identity(krb5_context context,
- pkinit_identity_crypto_context id_cryptoctx)
-{
- PK11SlotList *slist;
- PK11SlotListElement *sle;
- CERTCertificate *cert;
- struct _pkinit_identity_crypto_file *obj;
- int i;
-
- id_cryptoctx->identity = NULL;
- if (id_cryptoctx->id_cert == NULL)
- return;
- cert = id_cryptoctx->id_cert;
- for (i = 0;
- (id_cryptoctx->id_objects != NULL) &&
- (id_cryptoctx->id_objects[i] != NULL);
- i++) {
- obj = id_cryptoctx->id_objects[i];
- if ((obj->cert != NULL) && CERT_CompareCerts(obj->cert, cert)) {
- id_cryptoctx->identity = obj->name;
- return;
- }
- }
- if (cert->slot != NULL) {
- id_cryptoctx->identity = crypto_get_identity_by_slot(context,
- id_cryptoctx,
- cert->slot);
- if (id_cryptoctx->identity != NULL)
- return;
- }
- slist = PK11_GetAllSlotsForCert(cert, NULL);
- if (slist != NULL) {
- for (sle = PK11_GetFirstSafe(slist);
- sle != NULL;
- sle = PK11_GetNextSafe(slist, sle, PR_FALSE)) {
- id_cryptoctx->identity = crypto_get_identity_by_slot(context,
- id_cryptoctx,
- sle->slot);
- if (id_cryptoctx->identity != NULL) {
- PK11_FreeSlotList(slist);
- return;
- }
- }
- PK11_FreeSlotList(slist);
- }
-}
-
-krb5_error_code
-crypto_retrieve_signer_identity(krb5_context context,
- pkinit_identity_crypto_context id_cryptoctx,
- const char **identity)
-{
- *identity = id_cryptoctx->identity;
- if (*identity == NULL)
- return ENOENT;
- return 0;
-}
-
-static krb5_error_code
-cert_retrieve_cert_sans(krb5_context context,
- CERTCertificate *cert,
- krb5_principal **pkinit_sans_out,
- krb5_principal **upn_sans_out,
- unsigned char ***kdc_hostname_out)
-{
- PLArenaPool *pool;
- CERTGeneralName name;
- SECItem *ext, **encoded_names;
- int i, n_pkinit_sans, n_upn_sans, n_hostnames;
-
- /* Pull out the extension. */
- ext = cert_get_ext_by_tag(cert, SEC_OID_X509_SUBJECT_ALT_NAME);
- if (ext == NULL)
- return ENOENT;
-
- /* Split up the list of names. */
- pool = PORT_NewArena(sizeof(double));
- if (pool == NULL)
- return ENOMEM;
- encoded_names = NULL;
- if (SEC_ASN1DecodeItem(pool, &encoded_names,
- SEC_ASN1_GET(SEC_SequenceOfAnyTemplate),
- ext) != SECSuccess) {
- pkiDebug("%s: error decoding subjectAltName extension\n",
- __FUNCTION__);
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
-
- /* Check each name in turn. */
- for (i = 0, n_pkinit_sans = 0, n_upn_sans = 0, n_hostnames = 0;
- (encoded_names != NULL) && (encoded_names[i] != NULL);
- i++) {
- memset(&name, 0, sizeof(name));
- if (CERT_DecodeGeneralName(pool, encoded_names[i], &name) != &name) {
- pkiDebug("%s: error decoding GeneralName value, skipping\n",
- __FUNCTION__);
- continue;
- }
- switch (name.type) {
- case certDNSName:
- /* hostname, easy */
- if ((kdc_hostname_out != NULL) &&
- (cert_add_string(kdc_hostname_out, &n_hostnames,
- name.name.other.len,
- name.name.other.data) != 0)) {
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
- break;
- case certOtherName:
- /* possibly a kerberos principal name */
- if (SECITEM_ItemsAreEqual(&name.name.OthName.oid,
- &pkinit_nt_principal)) {
- /* Add it to the list. */
- if ((pkinit_sans_out != NULL) &&
- (cert_add_kpn(pool, context, &name.name.OthName.name,
- pkinit_sans_out, &n_pkinit_sans) != 0)) {
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
- /* If both lists are the same, fix the count. */
- if (pkinit_sans_out == upn_sans_out)
- n_upn_sans = n_pkinit_sans;
- } else
- /* possibly a user principal name */
- if (SECITEM_ItemsAreEqual(&name.name.OthName.oid,
- &pkinit_nt_upn)) {
- /* Add it to the list. */
- if ((upn_sans_out != NULL) &&
- (cert_add_upn(pool, context, &name.name.OthName.name,
- upn_sans_out, &n_upn_sans) != 0)) {
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
- /* If both lists are the same, fix the count. */
- if (upn_sans_out == pkinit_sans_out)
- n_pkinit_sans = n_upn_sans;
- }
- break;
- default:
- break;
- }
- }
- PORT_FreeArena(pool, PR_TRUE);
-
- return 0;
-}
-
-krb5_error_code
-crypto_retrieve_cert_sans(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context id_cryptoctx,
- krb5_principal **pkinit_sans,
- krb5_principal **upn_sans,
- unsigned char ***kdc_hostname)
-{
- return cert_retrieve_cert_sans(context,
- req_cryptoctx->peer_cert,
- pkinit_sans, upn_sans, kdc_hostname);
-}
-
-krb5_error_code
-crypto_check_cert_eku(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context id_cryptoctx,
- int checking_kdc_cert,
- int allow_secondary_usage, int *eku_valid)
-{
- int ku, eku;
-
- *eku_valid = 0;
-
- ku = cert_get_ku_bits(context, req_cryptoctx->peer_cert);
- if (!(ku & PKINIT_KU_DIGITALSIGNATURE)) {
- return 0;
- }
-
- eku = cert_get_eku_bits(context, req_cryptoctx->peer_cert,
- checking_kdc_cert ? PR_TRUE : PR_FALSE);
- if (checking_kdc_cert) {
- if (eku & PKINIT_EKU_PKINIT) {
- *eku_valid = 1;
- } else if (allow_secondary_usage && (eku & PKINIT_EKU_CLIENTAUTH)) {
- *eku_valid = 1;
- }
- } else {
- if (eku & PKINIT_EKU_PKINIT) {
- *eku_valid = 1;
- } else if (allow_secondary_usage && (eku & PKINIT_EKU_MSSCLOGIN)) {
- *eku_valid = 1;
- }
- }
- return 0;
-}
-
-krb5_error_code
-cms_contentinfo_create(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context id_cryptoctx,
- int cms_msg_type,
- unsigned char *in_data, unsigned int in_length,
- unsigned char **out_data, unsigned int *out_data_len)
-{
- PLArenaPool *pool;
- SECItem *oid, encoded;
- SECOidTag encapsulated_tag;
- struct content_info cinfo;
-
- switch (cms_msg_type) {
- case CMS_SIGN_DRAFT9:
- encapsulated_tag = get_pkinit_data_auth_data9_tag();
- break;
- case CMS_SIGN_CLIENT:
- encapsulated_tag = get_pkinit_data_auth_data_tag();
- break;
- case CMS_SIGN_SERVER:
- encapsulated_tag = get_pkinit_data_dhkey_data_tag();
- break;
- case CMS_ENVEL_SERVER:
- encapsulated_tag = get_pkinit_data_rkey_data_tag();
- break;
- default:
- return ENOSYS;
- break;
- }
-
- oid = get_oid_from_tag(encapsulated_tag);
- if (oid == NULL) {
- return ENOMEM;
- }
-
- pool = PORT_NewArena(sizeof(double));
- if (pool == NULL) {
- return ENOMEM;
- }
-
- memset(&cinfo, 0, sizeof(cinfo));
- cinfo.content_type = *oid;
- cinfo.content.data = in_data;
- cinfo.content.len = in_length;
-
- memset(&encoded, 0, sizeof(encoded));
- if (SEC_ASN1EncodeItem(pool, &encoded, &cinfo,
- content_info_template) != &encoded) {
- PORT_FreeArena(pool, PR_TRUE);
- pkiDebug("%s: error encoding data\n", __FUNCTION__);
- return ENOMEM;
- }
-
- if (secitem_to_buf_len(&encoded, out_data, out_data_len) != 0) {
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
-#ifdef DEBUG_DER
- derdump(*out_data, *out_data_len);
-#endif
-#ifdef DEBUG_CMS
- cmsdump(*out_data, *out_data_len);
-#endif
-
- PORT_FreeArena(pool, PR_TRUE);
-
- return 0;
-}
-
-/* Create a signed-data content info, add a signature to it, and return it. */
-enum sdcc_include_certchain {
- signeddata_common_create_omit_chain,
- signeddata_common_create_with_chain
-};
-enum sdcc_include_signed_attrs {
- signeddata_common_create_omit_signed_attrs,
- signeddata_common_create_with_signed_attrs
-};
-static krb5_error_code
-crypto_signeddata_common_create(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context id_cryptoctx,
- NSSCMSMessage *msg,
- SECOidTag digest,
- enum sdcc_include_certchain certchain_mode,
- enum sdcc_include_signed_attrs add_signedattrs,
- NSSCMSSignedData **signed_data_out)
-{
- NSSCMSSignedData *sdata;
- NSSCMSSignerInfo *signer;
- NSSCMSCertChainMode chainmode;
-
- /* Create a signed-data object. */
- sdata = NSS_CMSSignedData_Create(msg);
- if (sdata == NULL)
- return ENOMEM;
-
- if (id_cryptoctx->id_cert != NULL) {
- /* Create a signer and add it to the signed-data pointer. */
- signer = NSS_CMSSignerInfo_Create(msg, id_cryptoctx->id_cert, digest);
- if (signer == NULL)
- return ENOMEM;
- chainmode = (certchain_mode == signeddata_common_create_with_chain) ?
- NSSCMSCM_CertChain :
- NSSCMSCM_CertOnly;
- if (NSS_CMSSignerInfo_IncludeCerts(signer,
- chainmode,
- certUsageAnyCA) != SECSuccess) {
- pkiDebug("%s: error setting IncludeCerts\n", __FUNCTION__);
- return ENOMEM;
- }
- if (NSS_CMSSignedData_AddSignerInfo(sdata, signer) != SECSuccess)
- return ENOMEM;
-
- if (add_signedattrs == signeddata_common_create_with_signed_attrs) {
- /* The presence of any signed attribute means the digest
- * becomes a signed attribute, too. */
- if (NSS_CMSSignerInfo_AddSigningTime(signer,
- PR_Now()) != SECSuccess) {
- pkiDebug("%s: error adding signing time\n", __FUNCTION__);
- return ENOMEM;
- }
- }
- }
-
- *signed_data_out = sdata;
- return 0;
-}
-
-/* Create signed-then-enveloped data. */
-krb5_error_code
-cms_envelopeddata_create(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context id_cryptoctx,
- krb5_preauthtype pa_type,
- int include_certchain,
- unsigned char *key_pack,
- unsigned int key_pack_len,
- unsigned char **envel_data,
- unsigned int *envel_data_len)
-{
- NSSCMSMessage *msg;
- NSSCMSContentInfo *info;
- NSSCMSEnvelopedData *env;
- NSSCMSRecipientInfo *recipient;
- NSSCMSSignedData *sdata;
- PLArenaPool *pool;
- SECOidTag encapsulated_tag, digest;
- SECItem plain, encoded;
- enum sdcc_include_signed_attrs add_signed_attrs;
-
- switch (pa_type) {
- case KRB5_PADATA_PK_AS_REQ_OLD:
- case KRB5_PADATA_PK_AS_REP_OLD:
- digest = SEC_OID_MD5;
- add_signed_attrs = signeddata_common_create_omit_signed_attrs;
- encapsulated_tag = get_pkinit_data_rkey_data_tag();
- break;
- case KRB5_PADATA_PK_AS_REQ:
- case KRB5_PADATA_PK_AS_REP:
- digest = SEC_OID_SHA1;
- add_signed_attrs = signeddata_common_create_with_signed_attrs;
- encapsulated_tag = get_pkinit_data_rkey_data_tag();
- break;
- default:
- return ENOSYS;
- break;
- }
-
- if (id_cryptoctx->id_cert == NULL) {
- pkiDebug("%s: no signer identity\n", __FUNCTION__);
- return ENOENT;
- }
-
- if (req_cryptoctx->peer_cert == NULL) {
- pkiDebug("%s: no recipient identity\n", __FUNCTION__);
- return ENOENT;
- }
-
- pool = PORT_NewArena(sizeof(double));
- if (pool == NULL) {
- return ENOMEM;
- }
-
- /* Create the containing message. */
- msg = NSS_CMSMessage_Create(pool);
- if (msg == NULL) {
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
-
- /* Create an enveloped-data pointer and set it as the message's
- * contents. */
- env = NSS_CMSEnvelopedData_Create(msg, SEC_OID_DES_EDE3_CBC, 0);
- if (env == NULL) {
- pkiDebug("%s: error creating enveloped-data\n", __FUNCTION__);
- NSS_CMSMessage_Destroy(msg);
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
- info = NSS_CMSMessage_GetContentInfo(msg);
- if (info == NULL) {
- NSS_CMSMessage_Destroy(msg);
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
- if (NSS_CMSContentInfo_SetContent_EnvelopedData(msg, info,
- env) != SECSuccess) {
- pkiDebug("%s: error setting enveloped-data content\n", __FUNCTION__);
- NSS_CMSMessage_Destroy(msg);
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
-
- /* Create a recipient and add it to the enveloped-data pointer. */
- recipient = NSS_CMSRecipientInfo_Create(msg, req_cryptoctx->peer_cert);
- if (recipient == NULL) {
- pkiDebug("%s: error creating recipient-info\n", __FUNCTION__);
- NSS_CMSMessage_Destroy(msg);
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
- if (NSS_CMSEnvelopedData_AddRecipient(env, recipient) != SECSuccess) {
- pkiDebug("%s: error adding recipient\n", __FUNCTION__);
- NSS_CMSMessage_Destroy(msg);
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
-
- /* Create a signed-data pointer and set it as the enveloped-data's
- * contents. */
- info = NSS_CMSEnvelopedData_GetContentInfo(env);
- if (info == NULL) {
- NSS_CMSMessage_Destroy(msg);
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
- sdata = NULL;
- if ((crypto_signeddata_common_create(context,
- plg_cryptoctx,
- req_cryptoctx,
- id_cryptoctx,
- msg,
- digest,
- include_certchain ?
- signeddata_common_create_with_chain :
- signeddata_common_create_omit_chain,
- add_signed_attrs,
- &sdata) != 0) || (sdata == NULL)) {
- NSS_CMSMessage_Destroy(msg);
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
- if (NSS_CMSContentInfo_SetContent_SignedData(msg, info,
- sdata) != SECSuccess) {
- pkiDebug("%s: error setting signed-data content\n", __FUNCTION__);
- NSS_CMSMessage_Destroy(msg);
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
-
- /* Set the raw data as the contents for the signed-data. */
- info = NSS_CMSSignedData_GetContentInfo(sdata);
- if (info == NULL) {
- NSS_CMSMessage_Destroy(msg);
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
- if (NSS_CMSContentInfo_SetContent(msg, info, encapsulated_tag,
- NULL) != SECSuccess) {
- pkiDebug("%s: error setting encapsulated content\n", __FUNCTION__);
- NSS_CMSMessage_Destroy(msg);
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
-
- /* Encode and export. */
- memset(&plain, 0, sizeof(plain));
- plain.data = key_pack;
- plain.len = key_pack_len;
- memset(&encoded, 0, sizeof(encoded));
- if (NSS_CMSDEREncode(msg, &plain, &encoded, pool) != SECSuccess) {
- pkiDebug("%s: error encoding enveloped-data\n", __FUNCTION__);
- NSS_CMSMessage_Destroy(msg);
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
- if (secitem_to_buf_len(&encoded, envel_data, envel_data_len) != 0) {
- NSS_CMSMessage_Destroy(msg);
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
-#ifdef DEBUG_DER
- derdump(*envel_data, *envel_data_len);
-#endif
-#ifdef DEBUG_CMS
- cmsdump(*envel_data, *envel_data_len);
-#endif
-
- NSS_CMSMessage_Destroy(msg);
- PORT_FreeArena(pool, PR_TRUE);
-
- return 0;
-}
-
-/* Check if this cert is marked as a CA which is trusted to issue certs for
- * the indicated usage. Return PR_TRUE if it is. */
-static PRBool
-crypto_is_cert_trusted(CERTCertificate *cert, SECCertUsage usage)
-{
- CERTCertTrust trust;
- unsigned int ca_trust;
-
- if (usage == certUsageSSLClient)
- ca_trust = CERTDB_TRUSTED_CLIENT_CA;
- else if (usage == certUsageSSLServer)
- ca_trust = CERTDB_TRUSTED_CA;
- else {
- pkiDebug("%s: internal error: needed CA trust unknown\n", __FUNCTION__);
- return PR_FALSE;
- }
- memset(&trust, 0, sizeof(trust));
- if (CERT_GetCertTrust(cert, &trust) != SECSuccess) {
- pkiDebug("%s: unable to find trust for \"%s\"\n", __FUNCTION__,
- cert->subjectName);
- return PR_FALSE;
- }
- if ((SEC_GET_TRUST_FLAGS(&trust, trustSSL) & ca_trust) != ca_trust) {
- pkiDebug("%s: \"%s\" is not a trusted CA\n", __FUNCTION__,
- cert->subjectName);
- return PR_FALSE;
- }
- return PR_TRUE;
-}
-
-/* Check if this cert includes an AuthorityInfoAccess extension which points
- * to an OCSP responder. Return PR_TRUE if it does. */
-static PRBool
-crypto_cert_has_ocsp_responder(CERTCertificate *cert)
-{
- CERTAuthInfoAccess **aia;
- SECOidData *ocsp;
- SECItem encoded_aia;
- int i;
-
- /* Look up the OID for "use an OCSP responder". */
- ocsp = SECOID_FindOIDByTag(SEC_OID_PKIX_OCSP);
- if (ocsp == NULL) {
- pkiDebug("%s: internal error: OCSP not known\n", __FUNCTION__);
- return PR_FALSE;
- }
- /* Find the AIA extension. */
- memset(&encoded_aia, 0, sizeof(encoded_aia));
- if (CERT_FindCertExtension(cert, SEC_OID_X509_AUTH_INFO_ACCESS,
- &encoded_aia) != SECSuccess) {
- pkiDebug("%s: no AuthorityInfoAccess extension for \"%s\"\n",
- __FUNCTION__, cert->subjectName);
- return PR_FALSE;
- }
- /* Decode the AIA extension. */
- aia = CERT_DecodeAuthInfoAccessExtension(cert->arena, &encoded_aia);
- if (aia == NULL) {
- pkiDebug("%s: error parsing AuthorityInfoAccess for \"%s\"\n",
- __FUNCTION__, cert->subjectName);
- return PR_FALSE;
- }
- /* We're looking for at least one OCSP responder. */
- for (i = 0; (aia[i] != NULL); i++)
- if (SECITEM_ItemsAreEqual(&(aia[i]->method), &(ocsp->oid))) {
- pkiDebug("%s: found OCSP responder for \"%s\"\n",
- __FUNCTION__, cert->subjectName);
- return PR_TRUE;
- }
- return PR_FALSE;
-}
-
-/* In the original implementation, the assumption has been that we'd use any
- * CRLs, and if we were missing a CRL for the certificate or any point in its
- * issuing chain, we'd raise a failure iff the require_crl_checking flag was
- * set.
- *
- * This is not exactly how NSS does things. When checking the revocation
- * status of a particular certificate, NSS will consult a cached copy of a CRL
- * issued by the certificate's issuer if one's available. If the CRL shows
- * that the certificate is revoked, it returns an error. If it succeeds,
- * however, processing continues, and if the certificate contains an AIA
- * extension which lists an OCSP responder, the library attempts to contact the
- * responder to also give it a chance to tell us that the certificate has been
- * revoked. We can control what happens if this connection attempt fails by
- * calling CERT_SetOCSPFailureMode().
- *
- * We attempt to compensate for this difference in behavior by walking the
- * issuing chain ourselves, ensuring that for the certificate and all of its
- * issuers, that either we have a CRL on-hand for its issuer, or if OCSP
- * checking is allowed, that the certificate contains the location of an OCSP
- * responder. We stop only when we reach a trusted CA certificate, as NSS
- * does. */
-static int
-crypto_check_for_revocation_information(CERTCertificate *cert,
- CERTCertDBHandle *certdb,
- PRBool allow_ocsp_checking,
- SECCertUsage usage)
-{
- CERTCertificate *issuer;
- CERTSignedCrl *crl;
-
- issuer = CERT_FindCertIssuer(cert, PR_Now(), usage);
- while (issuer != NULL) {
- /* Do we have a CRL for this cert's issuer? */
- crl = SEC_FindCrlByName(certdb, &cert->derIssuer, SEC_CRL_TYPE);
- if (crl != NULL) {
- pkiDebug("%s: have CRL for \"%s\"\n", __FUNCTION__,
- cert->issuerName);
- } else {
- SEC_DestroyCrl(crl);
- if (allow_ocsp_checking) {
- /* Check if the cert points to an OCSP responder. */
- if (!crypto_cert_has_ocsp_responder(cert)) {
- /* No CRL, no OCSP responder. */
- pkiDebug("%s: no OCSP responder for \"%s\"\n", __FUNCTION__,
- cert->subjectName);
- return -1;
- }
- } else {
- /* No CRL, and OCSP not allowed. */
- pkiDebug("%s: no CRL for issuer \"%s\"\n", __FUNCTION__,
- cert->issuerName);
- return -1;
- }
- }
- /* Check if this issuer is a trusted CA. If it is, we're done. */
- if (crypto_is_cert_trusted(issuer, usage)) {
- pkiDebug("%s: \"%s\" is a trusted CA\n", __FUNCTION__,
- issuer->subjectName);
- CERT_DestroyCertificate(issuer);
- return 0;
- }
- /* Move on to the next link in the chain. */
- cert = issuer;
- issuer = CERT_FindCertIssuer(cert, PR_Now(), usage);
- if (issuer == NULL) {
- pkiDebug("%s: unable to find issuer for \"%s\"\n", __FUNCTION__,
- cert->subjectName);
- /* Don't leak the reference to the last intermediate. */
- CERT_DestroyCertificate(cert);
- return -1;
- }
- if (SECITEM_ItemsAreEqual(&cert->derCert, &issuer->derCert)) {
- pkiDebug("%s: \"%s\" is self-signed, but not trusted\n",
- __FUNCTION__, cert->subjectName);
- /* Don't leak the references to the self-signed cert. */
- CERT_DestroyCertificate(issuer);
- CERT_DestroyCertificate(cert);
- return -1;
- }
- /* Don't leak the reference to the just-traversed intermediate. */
- CERT_DestroyCertificate(cert);
- cert = NULL;
- }
- return -1;
-}
-
-/* Verify that we have a signed-data content info, that it has one signer, that
- * the signer can be trusted, and then check the type of the encapsulated
- * content and return that content. */
-static krb5_error_code
-crypto_signeddata_common_verify(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context id_cryptoctx,
- int require_crl_checking,
- NSSCMSContentInfo *cinfo,
- CERTCertDBHandle *certdb,
- SECCertUsage usage,
- SECOidTag expected_type,
- SECOidTag expected_type2,
- PLArenaPool *pool,
- int cms_msg_type,
- SECItem **plain_out,
- int *is_signed_out)
-{
- NSSCMSSignedData *sdata;
- NSSCMSSignerInfo *signer;
- NSSCMSMessage *ecmsg;
- NSSCMSContentInfo *ecinfo;
- CERTCertificate *cert;
- SECOidTag encapsulated_tag;
- SEC_OcspFailureMode ocsp_failure_mode;
- SECOidData *expected, *received;
- SECStatus status;
- SECItem *edata;
- int n_signers;
- PRBool allow_ocsp_checking = PR_TRUE;
-
- *is_signed_out = 0;
-
- /* Handle cases where we're passed data containing signed-data. */
- if (NSS_CMSContentInfo_GetContentTypeTag(cinfo) == SEC_OID_PKCS7_DATA) {
- /* Look at the payload data. */
- edata = NSS_CMSContentInfo_GetContent(cinfo);
- if (edata == NULL) {
- pkiDebug("%s: no plain-data content\n", __FUNCTION__);
- return ENOMEM;
- }
- /* See if it's content-info. */
- ecmsg = NSS_CMSMessage_CreateFromDER(edata,
- NULL, NULL,
- crypto_pwcb,
- crypto_pwcb_prep(id_cryptoctx,
- NULL, context),
- NULL, NULL);
- if (ecmsg == NULL) {
- pkiDebug("%s: plain-data not parsable\n", __FUNCTION__);
- return ENOMEM;
- }
- /* Check if it actually contains signed-data. */
- ecinfo = NSS_CMSMessage_GetContentInfo(ecmsg);
- if (ecinfo == NULL) {
- pkiDebug("%s: plain-data has no cinfo\n", __FUNCTION__);
- NSS_CMSMessage_Destroy(ecmsg);
- return ENOMEM;
- }
- if (NSS_CMSContentInfo_GetContentTypeTag(ecinfo) !=
- SEC_OID_PKCS7_SIGNED_DATA) {
- pkiDebug("%s: plain-data is not sdata\n", __FUNCTION__);
- NSS_CMSMessage_Destroy(ecmsg);
- return EINVAL;
- }
- pkiDebug("%s: parsed plain-data (length=%ld) as signed-data\n",
- __FUNCTION__, (long) edata->len);
- cinfo = ecinfo;
- } else
- /* Okay, it's a normal signed-data blob. */
- ecmsg = NULL;
-
- /* Check that we have signed data, that it has exactly one signature,
- * and fish out the signer information. */
- if (NSS_CMSContentInfo_GetContentTypeTag(cinfo) !=
- SEC_OID_PKCS7_SIGNED_DATA) {
- pkiDebug("%s: content type mismatch\n", __FUNCTION__);
- if (ecmsg != NULL)
- NSS_CMSMessage_Destroy(ecmsg);
- return EINVAL;
- }
- sdata = NSS_CMSContentInfo_GetContent(cinfo);
- if (sdata == NULL) {
- pkiDebug("%s: decoding error? content-info was NULL\n", __FUNCTION__);
- if (ecmsg != NULL)
- NSS_CMSMessage_Destroy(ecmsg);
- return ENOENT;
- }
- n_signers = NSS_CMSSignedData_SignerInfoCount(sdata);
- if (n_signers > 1) {
- pkiDebug("%s: wrong number of signers (%d, not 0 or 1)\n",
- __FUNCTION__, n_signers);
- if (ecmsg != NULL)
- NSS_CMSMessage_Destroy(ecmsg);
- return ENOENT;
- }
- if (n_signers < 1)
- signer = NULL;
- else {
- /* Import the bundle's certs and locate the signerInfo. */
- if (NSS_CMSSignedData_ImportCerts(sdata, certdb, usage,
- PR_FALSE) != SECSuccess) {
- pkiDebug("%s: error importing signer certs\n", __FUNCTION__);
- if (ecmsg != NULL)
- NSS_CMSMessage_Destroy(ecmsg);
- return ENOENT;
- }
- signer = NSS_CMSSignedData_GetSignerInfo(sdata, 0);
- if (signer == NULL) {
- pkiDebug("%s: no signers?\n", __FUNCTION__);
- if (ecmsg != NULL)
- NSS_CMSMessage_Destroy(ecmsg);
- return ENOENT;
- }
- if (!NSS_CMSSignedData_HasDigests(sdata)) {
- pkiDebug("%s: no digests?\n", __FUNCTION__);
- if (ecmsg != NULL)
- NSS_CMSMessage_Destroy(ecmsg);
- return ENOENT;
- }
- if (require_crl_checking && (signer->cert != NULL))
- if (crypto_check_for_revocation_information(signer->cert, certdb,
- allow_ocsp_checking,
- usage) != 0) {
- if (ecmsg != NULL)
- NSS_CMSMessage_Destroy(ecmsg);
- return KRB5KDC_ERR_REVOCATION_STATUS_UNAVAILABLE;
- }
- if (allow_ocsp_checking) {
- status = CERT_EnableOCSPChecking(certdb);
- if (status != SECSuccess) {
- pkiDebug("%s: error enabling OCSP: %s\n", __FUNCTION__,
- PR_ErrorToString(status == SECFailure ?
- PORT_GetError() : status,
- PR_LANGUAGE_I_DEFAULT));
- if (ecmsg != NULL)
- NSS_CMSMessage_Destroy(ecmsg);
- return ENOMEM;
- }
- ocsp_failure_mode = require_crl_checking ?
- ocspMode_FailureIsVerificationFailure :
- ocspMode_FailureIsNotAVerificationFailure;
- status = CERT_SetOCSPFailureMode(ocsp_failure_mode);
- if (status != SECSuccess) {
- pkiDebug("%s: error setting OCSP failure mode: %s\n",
- __FUNCTION__,
- PR_ErrorToString(status == SECFailure ?
- PORT_GetError() : status,
- PR_LANGUAGE_I_DEFAULT));
- if (ecmsg != NULL)
- NSS_CMSMessage_Destroy(ecmsg);
- return ENOMEM;
- }
- } else {
- status = CERT_DisableOCSPChecking(certdb);
- if ((status != SECSuccess) &&
- (PORT_GetError() != SEC_ERROR_OCSP_NOT_ENABLED)) {
- pkiDebug("%s: error disabling OCSP: %s\n", __FUNCTION__,
- PR_ErrorToString(status == SECFailure ?
- PORT_GetError() : status,
- PR_LANGUAGE_I_DEFAULT));
- if (ecmsg != NULL)
- NSS_CMSMessage_Destroy(ecmsg);
- return ENOMEM;
- }
- }
- status = NSS_CMSSignedData_VerifySignerInfo(sdata, 0, certdb, usage);
- if (status != SECSuccess) {
- pkiDebug("%s: signer verify failed: %s\n", __FUNCTION__,
- PR_ErrorToString(status == SECFailure ?
- PORT_GetError() : status,
- PR_LANGUAGE_I_DEFAULT));
- if (ecmsg != NULL)
- NSS_CMSMessage_Destroy(ecmsg);
- switch (cms_msg_type) {
- case CMS_SIGN_DRAFT9:
- case CMS_SIGN_CLIENT:
- switch (PORT_GetError()) {
- case SEC_ERROR_REVOKED_CERTIFICATE:
- return KRB5KDC_ERR_REVOKED_CERTIFICATE;
- case SEC_ERROR_UNKNOWN_ISSUER:
- return KRB5KDC_ERR_CANT_VERIFY_CERTIFICATE;
- default:
- return KRB5KDC_ERR_CLIENT_NOT_TRUSTED;
- }
- break;
- case CMS_SIGN_SERVER:
- case CMS_ENVEL_SERVER:
- switch (PORT_GetError()) {
- case SEC_ERROR_REVOKED_CERTIFICATE:
- return KRB5KDC_ERR_REVOKED_CERTIFICATE;
- case SEC_ERROR_UNKNOWN_ISSUER:
- return KRB5KDC_ERR_CANT_VERIFY_CERTIFICATE;
- default:
- return KRB5KDC_ERR_KDC_NOT_TRUSTED;
- }
- break;
- default:
- return ENOMEM;
- }
- }
- pkiDebug("%s: signer verify passed\n", __FUNCTION__);
- *is_signed_out = 1;
- }
- /* Pull out the payload. */
- ecinfo = NSS_CMSSignedData_GetContentInfo(sdata);
- if (ecinfo == NULL) {
- pkiDebug("%s: error getting encapsulated content\n", __FUNCTION__);
- if (ecmsg != NULL)
- NSS_CMSMessage_Destroy(ecmsg);
- return ENOMEM;
- }
- encapsulated_tag = NSS_CMSContentInfo_GetContentTypeTag(ecinfo);
- if ((encapsulated_tag != expected_type) &&
- ((expected_type2 == SEC_OID_UNKNOWN) ||
- (encapsulated_tag != expected_type2))) {
- pkiDebug("%s: wrong encapsulated content type\n", __FUNCTION__);
- expected = SECOID_FindOIDByTag(expected_type);
- if (encapsulated_tag != SEC_OID_UNKNOWN)
- received = SECOID_FindOIDByTag(encapsulated_tag);
- else
- received = NULL;
- if (expected != NULL) {
- if (received != NULL) {
- pkiDebug("%s: was expecting \"%s\"(%d), but got \"%s\"(%d)\n",
- __FUNCTION__,
- expected->desc, expected->offset,
- received->desc, received->offset);
- } else {
- pkiDebug("%s: was expecting \"%s\"(%d), "
- "but got unrecognized type (%d)\n",
- __FUNCTION__,
- expected->desc, expected->offset, encapsulated_tag);
- }
- }
- if (ecmsg != NULL)
- NSS_CMSMessage_Destroy(ecmsg);
- return EINVAL;
- }
- *plain_out = NSS_CMSContentInfo_GetContent(ecinfo);
- if ((*plain_out != NULL) && ((*plain_out)->len == 0))
- pkiDebug("%s: warning: encapsulated content appears empty\n",
- __FUNCTION__);
- if (signer != NULL) {
- /* Save the peer cert -- we'll need it later. */
- pkiDebug("%s: saving peer certificate\n", __FUNCTION__);
- if (req_cryptoctx->peer_cert != NULL)
- CERT_DestroyCertificate(req_cryptoctx->peer_cert);
- cert = NSS_CMSSignerInfo_GetSigningCertificate(signer, certdb);
- req_cryptoctx->peer_cert = CERT_DupCertificate(cert);
- }
- if (ecmsg != NULL) {
- *plain_out = SECITEM_ArenaDupItem(pool, *plain_out);
- NSS_CMSMessage_Destroy(ecmsg);
- }
- return 0;
-}
-
-/* Verify signed-then-enveloped data, and return the data that was signed. */
-krb5_error_code
-cms_envelopeddata_verify(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context id_cryptoctx,
- krb5_preauthtype pa_type,
- int require_crl_checking,
- unsigned char *envel_data,
- unsigned int envel_data_len,
- unsigned char **signed_data,
- unsigned int *signed_data_len)
-{
- NSSCMSMessage *msg;
- NSSCMSContentInfo *info;
- NSSCMSEnvelopedData *env;
- CERTCertDBHandle *certdb;
- PLArenaPool *pool;
- SECItem *plain, encoded;
- SECCertUsage usage;
- SECOidTag expected_tag, expected_tag2;
- int is_signed, ret;
-
- pool = PORT_NewArena(sizeof(double));
- if (pool == NULL)
- return ENOMEM;
- certdb = CERT_GetDefaultCertDB();
-
- /* Decode the message. */
-#ifdef DEBUG_DER
- derdump(envel_data, envel_data_len);
-#endif
- encoded.data = envel_data;
- encoded.len = envel_data_len;
- msg = NSS_CMSMessage_CreateFromDER(&encoded,
- NULL, NULL,
- crypto_pwcb,
- crypto_pwcb_prep(id_cryptoctx,
- NULL, context),
- NULL, NULL);
- if (msg == NULL)
- return ENOMEM;
-
- /* Make sure it's enveloped-data. */
- info = NSS_CMSMessage_GetContentInfo(msg);
- if (info == NULL) {
- NSS_CMSMessage_Destroy(msg);
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
- if (NSS_CMSContentInfo_GetContentTypeTag(info) !=
- SEC_OID_PKCS7_ENVELOPED_DATA) {
- NSS_CMSMessage_Destroy(msg);
- PORT_FreeArena(pool, PR_TRUE);
- return EINVAL;
- }
-
- /* Okay, it's enveloped-data. */
- env = NSS_CMSContentInfo_GetContent(info);
-
- /* Pull out the encapsulated content. It should be signed-data. */
- info = NSS_CMSEnvelopedData_GetContentInfo(env);
- if (info == NULL) {
- NSS_CMSMessage_Destroy(msg);
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
-
- /* Pull out the signed data and verify it. */
- expected_tag = get_pkinit_data_rkey_data_tag();
- expected_tag2 = SEC_OID_PKCS7_DATA;
- usage = certUsageSSLServer;
- plain = NULL;
- is_signed = 0;
- ret = crypto_signeddata_common_verify(context,
- plg_cryptoctx,
- req_cryptoctx,
- id_cryptoctx,
- require_crl_checking,
- info,
- certdb,
- usage,
- expected_tag,
- expected_tag2,
- pool,
- CMS_ENVEL_SERVER,
- &plain,
- &is_signed);
- if ((ret != 0) || (plain == NULL) || !is_signed) {
- NSS_CMSMessage_Destroy(msg);
- PORT_FreeArena(pool, PR_TRUE);
- return ret ? ret : ENOMEM;
- }
- /* Export the payload. */
- if (secitem_to_buf_len(plain, signed_data, signed_data_len) != 0) {
- NSS_CMSMessage_Destroy(msg);
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
- NSS_CMSMessage_Destroy(msg);
- PORT_FreeArena(pool, PR_TRUE);
-
- return 0;
-}
-
-krb5_error_code
-cms_signeddata_create(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context id_cryptoctx,
- int cms_msg_type,
- int include_certchain,
- unsigned char *payload,
- unsigned int payload_len,
- unsigned char **signed_data,
- unsigned int *signed_data_len)
-{
- NSSCMSMessage *msg;
- NSSCMSContentInfo *info;
- NSSCMSSignedData *sdata;
- PLArenaPool *pool;
- SECItem plain, encoded;
- SECOidTag digest, encapsulated_tag;
- enum sdcc_include_signed_attrs add_signed_attrs;
-
- switch (cms_msg_type) {
- case CMS_SIGN_DRAFT9:
- digest = SEC_OID_MD5;
- add_signed_attrs = signeddata_common_create_omit_signed_attrs;
- encapsulated_tag = get_pkinit_data_auth_data9_tag();
- break;
- case CMS_SIGN_CLIENT:
- digest = SEC_OID_SHA1;
- add_signed_attrs = signeddata_common_create_with_signed_attrs;
- encapsulated_tag = get_pkinit_data_auth_data_tag();
- break;
- case CMS_SIGN_SERVER:
- digest = SEC_OID_SHA1;
- add_signed_attrs = signeddata_common_create_with_signed_attrs;
- encapsulated_tag = get_pkinit_data_dhkey_data_tag();
- break;
- case CMS_ENVEL_SERVER:
- default:
- return ENOSYS;
- break;
- }
-
- pool = PORT_NewArena(sizeof(double));
- if (pool == NULL)
- return ENOMEM;
-
- /* Create the containing message. */
- msg = NSS_CMSMessage_Create(pool);
- if (msg == NULL) {
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
-
- /* Create a signed-data pointer and set it as the message's
- * contents. */
- info = NSS_CMSMessage_GetContentInfo(msg);
- if (info == NULL) {
- NSS_CMSMessage_Destroy(msg);
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
- sdata = NULL;
- if ((crypto_signeddata_common_create(context,
- plg_cryptoctx,
- req_cryptoctx,
- id_cryptoctx,
- msg,
- digest,
- include_certchain ?
- signeddata_common_create_with_chain :
- signeddata_common_create_omit_chain,
- add_signed_attrs,
- &sdata) != 0) || (sdata == NULL)) {
- NSS_CMSMessage_Destroy(msg);
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
- if (NSS_CMSContentInfo_SetContent_SignedData(msg, info,
- sdata) != SECSuccess) {
- pkiDebug("%s: error setting signed-data content\n", __FUNCTION__);
- NSS_CMSMessage_Destroy(msg);
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
-
- /* Set the data as the contents of the signed-data. */
- info = NSS_CMSSignedData_GetContentInfo(sdata);
- if (info == NULL) {
- NSS_CMSMessage_Destroy(msg);
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
- if (NSS_CMSContentInfo_SetContent(msg, info, encapsulated_tag,
- NULL) != SECSuccess) {
- pkiDebug("%s: error setting encapsulated content type\n",
- __FUNCTION__);
- NSS_CMSMessage_Destroy(msg);
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
-
- /* Encode and export. */
- memset(&plain, 0, sizeof(plain));
- plain.data = payload;
- plain.len = payload_len;
- memset(&encoded, 0, sizeof(encoded));
- if (NSS_CMSDEREncode(msg, &plain, &encoded, pool) != SECSuccess) {
- NSS_CMSMessage_Destroy(msg);
- PORT_FreeArena(pool, PR_TRUE);
- pkiDebug("%s: error encoding signed-data: %s\n", __FUNCTION__,
- PORT_ErrorToName(PORT_GetError()));
- return ENOMEM;
- }
- if (secitem_to_buf_len(&encoded, signed_data, signed_data_len) != 0) {
- NSS_CMSMessage_Destroy(msg);
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
-#ifdef DEBUG_DER
- derdump(*signed_data, *signed_data_len);
-#endif
-#ifdef DEBUG_CMS
- cmsdump(*signed_data, *signed_data_len);
-#endif
-
- NSS_CMSMessage_Destroy(msg);
- PORT_FreeArena(pool, PR_TRUE);
-
- return 0;
-}
-
-krb5_error_code
-cms_signeddata_verify(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context id_cryptoctx,
- int cms_msg_type,
- int require_crl_checking,
- unsigned char *signed_data,
- unsigned int signed_data_len,
- unsigned char **payload,
- unsigned int *payload_len,
- unsigned char **authz_data,
- unsigned int *authz_data_len,
- int *is_signed)
-{
- NSSCMSMessage *msg;
- NSSCMSContentInfo *info;
- CERTCertDBHandle *certdb;
- SECCertUsage usage;
- SECOidTag expected_tag, expected_tag2;
- PLArenaPool *pool;
- SECItem *plain, encoded;
- struct content_info simple_content_info;
- int was_signed, ret;
-
- switch (cms_msg_type) {
- case CMS_SIGN_DRAFT9:
- usage = certUsageSSLClient;
- expected_tag = get_pkinit_data_auth_data9_tag();
- break;
- case CMS_SIGN_CLIENT:
- usage = certUsageSSLClient;
- expected_tag = get_pkinit_data_auth_data_tag();
- break;
- case CMS_SIGN_SERVER:
- usage = certUsageSSLServer;
- expected_tag = get_pkinit_data_dhkey_data_tag();
- break;
- case CMS_ENVEL_SERVER:
- default:
- return ENOSYS;
- break;
- }
- expected_tag2 = SEC_OID_UNKNOWN;
-
- pool = PORT_NewArena(sizeof(double));
- if (pool == NULL)
- return ENOMEM;
- certdb = CERT_GetDefaultCertDB();
-
-#ifdef DEBUG_DER
- derdump(signed_data, signed_data_len);
-#endif
-
- memset(&encoded, 0, sizeof(encoded));
- encoded.data = signed_data;
- encoded.len = signed_data_len;
-
- /* Take a quick look at what it claims to be. */
- memset(&simple_content_info, 0, sizeof(simple_content_info));
- if (SEC_ASN1DecodeItem(pool, &simple_content_info,
- content_info_template, &encoded) == SECSuccess)
- /* If it's unsigned data of the right type... */
- if (SECOID_FindOIDTag(&simple_content_info.content_type) ==
- expected_tag) {
- /* Pull out the payload -- it's not wrapped in a
- * SignedData. */
- pkiDebug("%s: data is not signed\n", __FUNCTION__);
- if (is_signed != NULL)
- *is_signed = 0;
- if (secitem_to_buf_len(&simple_content_info.content,
- payload, payload_len) != 0) {
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
- return 0;
- }
-
- /* Decode the message. */
- msg = NSS_CMSMessage_CreateFromDER(&encoded,
- NULL, NULL,
- crypto_pwcb,
- crypto_pwcb_prep(id_cryptoctx,
- NULL, context),
- NULL, NULL);
- if (msg == NULL)
- return ENOMEM;
-
- /* Double-check that it's signed. */
- info = NSS_CMSMessage_GetContentInfo(msg);
- if (info == NULL) {
- NSS_CMSMessage_Destroy(msg);
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
- switch (NSS_CMSContentInfo_GetContentTypeTag(info)) {
- case SEC_OID_PKCS7_SIGNED_DATA:
- /* It's signed: try to verify the signature. */
- pkiDebug("%s: data is probably signed, checking\n", __FUNCTION__);
- plain = NULL;
- was_signed = 0;
- ret = crypto_signeddata_common_verify(context,
- plg_cryptoctx,
- req_cryptoctx,
- id_cryptoctx,
- require_crl_checking,
- info,
- certdb,
- usage,
- expected_tag,
- expected_tag2,
- pool,
- cms_msg_type,
- &plain,
- &was_signed);
- if ((ret != 0) || (plain == NULL)) {
- NSS_CMSMessage_Destroy(msg);
- PORT_FreeArena(pool, PR_TRUE);
- return ret ? ret : ENOMEM;
- }
- if (is_signed != NULL)
- *is_signed = was_signed;
- break;
- case SEC_OID_PKCS7_DATA:
- /* It's not signed: try to pull out the payload. */
- pkiDebug("%s: data is not signed\n", __FUNCTION__);
- if (is_signed != NULL)
- *is_signed = 0;
- plain = NSS_CMSContentInfo_GetContent(info);
- break;
- default:
- NSS_CMSMessage_Destroy(msg);
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
-
- /* Export the payload. */
- if ((plain == NULL) ||
- (secitem_to_buf_len(plain, payload, payload_len) != 0)) {
- NSS_CMSMessage_Destroy(msg);
- PORT_FreeArena(pool, PR_TRUE);
- return ENOMEM;
- }
- NSS_CMSMessage_Destroy(msg);
- PORT_FreeArena(pool, PR_TRUE);
-
- return 0;
-}
-
-/*
- * Add an item to the pkinit_identity_crypto_context's list of deferred
- * identities.
- */
-krb5_error_code
-crypto_set_deferred_id(krb5_context context,
- pkinit_identity_crypto_context id_cryptoctx,
- const char *identity, const char *password)
-{
- unsigned long ck_flags;
-
- ck_flags = pkinit_get_deferred_id_flags(id_cryptoctx->deferred_ids,
- identity);
- return pkinit_set_deferred_id(&id_cryptoctx->deferred_ids,
- identity, ck_flags, password);
-}
-
-/*
- * Retrieve a read-only copy of the pkinit_identity_crypto_context's list of
- * deferred identities, sure to be valid only until the next time someone calls
- * either pkinit_set_deferred_id() or crypto_set_deferred_id().
- */
-const pkinit_deferred_id *
-crypto_get_deferred_ids(krb5_context context,
- pkinit_identity_crypto_context id_cryptoctx)
-{
- pkinit_deferred_id *deferred;
- const pkinit_deferred_id *ret;
-
- deferred = id_cryptoctx->deferred_ids;
- ret = (const pkinit_deferred_id *)deferred;
- return ret;
-}
diff --git a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
index 74fffbf32129..ac107c2c1b67 100644
--- a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
+++ b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
@@ -67,10 +67,6 @@ static krb5_error_code pkinit_decode_data
const uint8_t *data, unsigned int data_len, uint8_t **decoded,
unsigned int *decoded_len);
-static krb5_error_code decode_data
-(uint8_t **, unsigned int *, const uint8_t *, unsigned int, EVP_PKEY *pkey,
- X509 *cert);
-
#ifdef DEBUG_DH
static void print_dh(DH *, char *);
static void print_pubkey(BIGNUM *, char *);
@@ -1154,7 +1150,7 @@ cms_signeddata_create(krb5_context context,
X509_ALGOR *alg = NULL;
ASN1_OCTET_STRING *digest = NULL;
unsigned int alg_len = 0, digest_len = 0;
- unsigned char *y = NULL, *alg_buf = NULL, *digest_buf = NULL;
+ unsigned char *y = NULL;
X509 *cert = NULL;
ASN1_OBJECT *oid = NULL, *oid_copy;
@@ -1321,18 +1317,12 @@ cms_signeddata_create(krb5_context context,
goto cleanup2;
X509_ALGOR_set0(alg, OBJ_nid2obj(NID_sha1), V_ASN1_NULL, NULL);
alg_len = i2d_X509_ALGOR(alg, NULL);
- alg_buf = malloc(alg_len);
- if (alg_buf == NULL)
- goto cleanup2;
digest = ASN1_OCTET_STRING_new();
if (digest == NULL)
goto cleanup2;
ASN1_OCTET_STRING_set(digest, md_data2, (int)md_len2);
digest_len = i2d_ASN1_OCTET_STRING(digest, NULL);
- digest_buf = malloc(digest_len);
- if (digest_buf == NULL)
- goto cleanup2;
digestInfo_len = ASN1_object_size(1, (int)(alg_len + digest_len),
V_ASN1_SEQUENCE);
@@ -1421,9 +1411,7 @@ cleanup2:
#ifndef WITHOUT_PKCS11
if (id_cryptoctx->pkcs11_method == 1 &&
id_cryptoctx->mech == CKM_RSA_PKCS) {
- free(digest_buf);
free(digestInfo_buf);
- free(alg_buf);
if (digest != NULL)
ASN1_OCTET_STRING_free(digest);
}
@@ -2101,11 +2089,21 @@ crypto_retrieve_X509_sans(krb5_context context,
{
krb5_error_code retval = EINVAL;
char buf[DN_BUF_LEN];
- int p = 0, u = 0, d = 0, l;
+ int p = 0, u = 0, d = 0, ret = 0, l;
krb5_principal *princs = NULL;
krb5_principal *upns = NULL;
unsigned char **dnss = NULL;
- unsigned int i, num_found = 0;
+ unsigned int i, num_found = 0, num_sans = 0;
+ X509_EXTENSION *ext = NULL;
+ GENERAL_NAMES *ialt = NULL;
+ GENERAL_NAME *gen = NULL;
+
+ if (princs_ret != NULL)
+ *princs_ret = NULL;
+ if (upn_ret != NULL)
+ *upn_ret = NULL;
+ if (dns_ret != NULL)
+ *dns_ret = NULL;
if (princs_ret == NULL && upn_ret == NULL && dns_ret == NULL) {
pkiDebug("%s: nowhere to return any values!\n", __FUNCTION__);
@@ -2121,143 +2119,137 @@ crypto_retrieve_X509_sans(krb5_context context,
buf, sizeof(buf));
pkiDebug("%s: looking for SANs in cert = %s\n", __FUNCTION__, buf);
- if ((l = X509_get_ext_by_NID(cert, NID_subject_alt_name, -1)) >= 0) {
- X509_EXTENSION *ext = NULL;
- GENERAL_NAMES *ialt = NULL;
- GENERAL_NAME *gen = NULL;
- int ret = 0;
- unsigned int num_sans = 0;
+ l = X509_get_ext_by_NID(cert, NID_subject_alt_name, -1);
+ if (l < 0)
+ return 0;
- if (!(ext = X509_get_ext(cert, l)) || !(ialt = X509V3_EXT_d2i(ext))) {
- pkiDebug("%s: found no subject alt name extensions\n",
- __FUNCTION__);
- goto cleanup;
- }
- num_sans = sk_GENERAL_NAME_num(ialt);
+ if (!(ext = X509_get_ext(cert, l)) || !(ialt = X509V3_EXT_d2i(ext))) {
+ pkiDebug("%s: found no subject alt name extensions\n", __FUNCTION__);
+ goto cleanup;
+ }
+ num_sans = sk_GENERAL_NAME_num(ialt);
- pkiDebug("%s: found %d subject alt name extension(s)\n",
- __FUNCTION__, num_sans);
+ pkiDebug("%s: found %d subject alt name extension(s)\n", __FUNCTION__,
+ num_sans);
- /* OK, we're likely returning something. Allocate return values */
- if (princs_ret != NULL) {
- princs = calloc(num_sans + 1, sizeof(krb5_principal));
- if (princs == NULL) {
- retval = ENOMEM;
- goto cleanup;
- }
+ /* OK, we're likely returning something. Allocate return values */
+ if (princs_ret != NULL) {
+ princs = calloc(num_sans + 1, sizeof(krb5_principal));
+ if (princs == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
}
- if (upn_ret != NULL) {
- upns = calloc(num_sans + 1, sizeof(krb5_principal));
- if (upns == NULL) {
- retval = ENOMEM;
- goto cleanup;
- }
+ }
+ if (upn_ret != NULL) {
+ upns = calloc(num_sans + 1, sizeof(krb5_principal));
+ if (upns == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
}
- if (dns_ret != NULL) {
- dnss = calloc(num_sans + 1, sizeof(*dnss));
- if (dnss == NULL) {
- retval = ENOMEM;
- goto cleanup;
- }
+ }
+ if (dns_ret != NULL) {
+ dnss = calloc(num_sans + 1, sizeof(*dnss));
+ if (dnss == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
}
+ }
- for (i = 0; i < num_sans; i++) {
- krb5_data name = { 0, 0, NULL };
+ for (i = 0; i < num_sans; i++) {
+ krb5_data name = { 0, 0, NULL };
- gen = sk_GENERAL_NAME_value(ialt, i);
- switch (gen->type) {
- case GEN_OTHERNAME:
- name.length = gen->d.otherName->value->value.sequence->length;
- name.data = (char *)gen->d.otherName->value->value.sequence->data;
- if (princs != NULL
- && OBJ_cmp(plgctx->id_pkinit_san,
- gen->d.otherName->type_id) == 0) {
+ gen = sk_GENERAL_NAME_value(ialt, i);
+ switch (gen->type) {
+ case GEN_OTHERNAME:
+ name.length = gen->d.otherName->value->value.sequence->length;
+ name.data = (char *)gen->d.otherName->value->value.sequence->data;
+ if (princs != NULL &&
+ OBJ_cmp(plgctx->id_pkinit_san,
+ gen->d.otherName->type_id) == 0) {
#ifdef DEBUG_ASN1
- print_buffer_bin((unsigned char *)name.data, name.length,
- "/tmp/pkinit_san");
+ print_buffer_bin((unsigned char *)name.data, name.length,
+ "/tmp/pkinit_san");
#endif
- ret = k5int_decode_krb5_principal_name(&name, &princs[p]);
- if (ret) {
- pkiDebug("%s: failed decoding pkinit san value\n",
- __FUNCTION__);
- } else {
- p++;
- num_found++;
- }
- } else if (upns != NULL
- && OBJ_cmp(plgctx->id_ms_san_upn,
- gen->d.otherName->type_id) == 0) {
- /* Prevent abuse of embedded null characters. */
- if (memchr(name.data, '\0', name.length))
- break;
- ret = krb5_parse_name(context, name.data, &upns[u]);
- if (ret) {
- pkiDebug("%s: failed parsing ms-upn san value\n",
- __FUNCTION__);
- } else {
- u++;
- num_found++;
- }
+ ret = k5int_decode_krb5_principal_name(&name, &princs[p]);
+ if (ret) {
+ pkiDebug("%s: failed decoding pkinit san value\n",
+ __FUNCTION__);
} else {
- pkiDebug("%s: unrecognized othername oid in SAN\n",
+ p++;
+ num_found++;
+ }
+ } else if (upns != NULL &&
+ OBJ_cmp(plgctx->id_ms_san_upn,
+ gen->d.otherName->type_id) == 0) {
+ /* Prevent abuse of embedded null characters. */
+ if (memchr(name.data, '\0', name.length))
+ break;
+ ret = krb5_parse_name_flags(context, name.data,
+ KRB5_PRINCIPAL_PARSE_ENTERPRISE,
+ &upns[u]);
+ if (ret) {
+ pkiDebug("%s: failed parsing ms-upn san value\n",
__FUNCTION__);
- continue;
+ } else {
+ u++;
+ num_found++;
}
+ } else {
+ pkiDebug("%s: unrecognized othername oid in SAN\n",
+ __FUNCTION__);
+ continue;
+ }
- break;
- case GEN_DNS:
- if (dnss != NULL) {
- /* Prevent abuse of embedded null characters. */
- if (memchr(gen->d.dNSName->data, '\0',
- gen->d.dNSName->length))
- break;
- pkiDebug("%s: found dns name = %s\n",
- __FUNCTION__, gen->d.dNSName->data);
- dnss[d] = (unsigned char *)
- strdup((char *)gen->d.dNSName->data);
- if (dnss[d] == NULL) {
- pkiDebug("%s: failed to duplicate dns name\n",
- __FUNCTION__);
- } else {
- d++;
- num_found++;
- }
+ break;
+ case GEN_DNS:
+ if (dnss != NULL) {
+ /* Prevent abuse of embedded null characters. */
+ if (memchr(gen->d.dNSName->data, '\0', gen->d.dNSName->length))
+ break;
+ pkiDebug("%s: found dns name = %s\n", __FUNCTION__,
+ gen->d.dNSName->data);
+ dnss[d] = (unsigned char *)
+ strdup((char *)gen->d.dNSName->data);
+ if (dnss[d] == NULL) {
+ pkiDebug("%s: failed to duplicate dns name\n",
+ __FUNCTION__);
+ } else {
+ d++;
+ num_found++;
}
- break;
- default:
- pkiDebug("%s: SAN type = %d expecting %d\n",
- __FUNCTION__, gen->type, GEN_OTHERNAME);
}
+ break;
+ default:
+ pkiDebug("%s: SAN type = %d expecting %d\n", __FUNCTION__,
+ gen->type, GEN_OTHERNAME);
}
- sk_GENERAL_NAME_pop_free(ialt, GENERAL_NAME_free);
}
+ sk_GENERAL_NAME_pop_free(ialt, GENERAL_NAME_free);
retval = 0;
- if (princs)
+ if (princs != NULL && *princs != NULL) {
*princs_ret = princs;
- if (upns)
+ princs = NULL;
+ }
+ if (upns != NULL && *upns != NULL) {
*upn_ret = upns;
- if (dnss)
+ upns = NULL;
+ }
+ if (dnss != NULL && *dnss != NULL) {
*dns_ret = dnss;
+ dnss = NULL;
+ }
cleanup:
- if (retval) {
- if (princs != NULL) {
- for (i = 0; princs[i] != NULL; i++)
- krb5_free_principal(context, princs[i]);
- free(princs);
- }
- if (upns != NULL) {
- for (i = 0; upns[i] != NULL; i++)
- krb5_free_principal(context, upns[i]);
- free(upns);
- }
- if (dnss != NULL) {
- for (i = 0; dnss[i] != NULL; i++)
- free(dnss[i]);
- free(dnss);
- }
- }
+ for (i = 0; princs != NULL && princs[i] != NULL; i++)
+ krb5_free_principal(context, princs[i]);
+ free(princs);
+ for (i = 0; upns != NULL && upns[i] != NULL; i++)
+ krb5_free_principal(context, upns[i]);
+ free(upns);
+ for (i = 0; dnss != NULL && dnss[i] != NULL; i++)
+ free(dnss[i]);
+ free(dnss);
return retval;
}
@@ -2313,7 +2305,6 @@ crypto_check_cert_eku(krb5_context context,
X509_NAME_oneline(X509_get_subject_name(reqctx->received_cert),
buf, sizeof(buf));
- pkiDebug("%s: looking for EKUs in cert = %s\n", __FUNCTION__, buf);
if ((i = X509_get_ext_by_NID(reqctx->received_cert,
NID_ext_key_usage, -1)) >= 0) {
@@ -2347,7 +2338,6 @@ crypto_check_cert_eku(krb5_context context,
if (found_eku) {
ASN1_BIT_STRING *usage = NULL;
- pkiDebug("%s: found acceptable EKU, checking for digitalSignature\n", __FUNCTION__);
/* check that digitalSignature KeyUsage is present */
X509_check_ca(reqctx->received_cert);
@@ -2356,12 +2346,10 @@ crypto_check_cert_eku(krb5_context context,
if (!ku_reject(reqctx->received_cert,
X509v3_KU_DIGITAL_SIGNATURE)) {
- pkiDebug("%s: found digitalSignature KU\n",
- __FUNCTION__);
+ TRACE_PKINIT_EKU(context);
*valid_eku = 1;
} else
- pkiDebug("%s: didn't find digitalSignature KU\n",
- __FUNCTION__);
+ TRACE_PKINIT_EKU_NO_KU(context);
}
ASN1_BIT_STRING_free(usage);
}
@@ -3581,12 +3569,14 @@ openssl_callback(int ok, X509_STORE_CTX * ctx)
{
#ifdef DEBUG
if (!ok) {
+ X509 *cert = X509_STORE_CTX_get_current_cert(ctx);
+ int err = X509_STORE_CTX_get_error(ctx);
+ const char *errmsg = X509_verify_cert_error_string(err);
char buf[DN_BUF_LEN];
- X509_NAME_oneline(X509_get_subject_name(ctx->current_cert), buf, sizeof(buf));
+ X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf));
pkiDebug("cert = %s\n", buf);
- pkiDebug("callback function: %d (%s)\n", ctx->error,
- X509_verify_cert_error_string(ctx->error));
+ pkiDebug("callback function: %d (%s)\n", err, errmsg);
}
#endif
return ok;
@@ -3979,12 +3969,34 @@ pkinit_decode_data_fs(krb5_context context,
const uint8_t *data, unsigned int data_len,
uint8_t **decoded_data, unsigned int *decoded_data_len)
{
- if (decode_data(decoded_data, decoded_data_len, data, data_len,
- id_cryptoctx->my_key, sk_X509_value(id_cryptoctx->my_certs,
- id_cryptoctx->cert_index)) <= 0) {
- pkiDebug("failed to decode data\n");
+ X509 *cert = sk_X509_value(id_cryptoctx->my_certs,
+ id_cryptoctx->cert_index);
+ EVP_PKEY *pkey = id_cryptoctx->my_key;
+ uint8_t *buf;
+ int buf_len, decrypt_len;
+
+ *decoded_data = NULL;
+ *decoded_data_len = 0;
+
+ if (cert != NULL && !X509_check_private_key(cert, pkey)) {
+ pkiDebug("private key does not match certificate\n");
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+
+ buf_len = EVP_PKEY_size(pkey);
+ buf = malloc(buf_len + 10);
+ if (buf == NULL)
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+
+ decrypt_len = EVP_PKEY_decrypt_old(buf, data, data_len, pkey);
+ if (decrypt_len <= 0) {
+ pkiDebug("unable to decrypt received data (len=%d)\n", data_len);
+ free(buf);
return KRB5KDC_ERR_PREAUTH_FAILED;
}
+
+ *decoded_data = buf;
+ *decoded_data_len = decrypt_len;
return 0;
}
@@ -4027,6 +4039,9 @@ pkinit_decode_data_pkcs11(krb5_context context,
uint8_t *cp;
int r;
+ *decoded_data = NULL;
+ *decoded_data_len = 0;
+
if (pkinit_open_session(context, id_cryptoctx)) {
pkiDebug("can't open pkcs11 session\n");
return KRB5KDC_ERR_PREAUTH_FAILED;
@@ -4075,6 +4090,9 @@ pkinit_decode_data(krb5_context context,
{
krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ *decoded_data = NULL;
+ *decoded_data_len = 0;
+
if (id_cryptoctx->pkcs11_method != 1)
retval = pkinit_decode_data_fs(context, id_cryptoctx, data, data_len,
decoded_data, decoded_data_len);
@@ -4188,41 +4206,6 @@ pkinit_sign_data(krb5_context context,
}
-static int
-decode_data(uint8_t **out_data, unsigned int *out_data_len,
- const uint8_t *data, unsigned int data_len, EVP_PKEY *pkey,
- X509 *cert)
-{
- int retval;
- unsigned char *buf = NULL;
- int buf_len = 0;
-
- if (cert && !X509_check_private_key(cert, pkey)) {
- pkiDebug("private key does not match certificate\n");
- return 0;
- }
-
- buf_len = EVP_PKEY_size(pkey);
- buf = malloc((size_t) buf_len + 10);
- if (buf == NULL)
- return 0;
-
-#if OPENSSL_VERSION_NUMBER >= 0x00909000L
- retval = EVP_PKEY_decrypt_old(buf, data, (int)data_len, pkey);
-#else
- retval = EVP_PKEY_decrypt(buf, data, (int)data_len, pkey);
-#endif
- if (retval <= 0) {
- pkiDebug("unable to decrypt received data (len=%d)\n", data_len);
- free(buf);
- return 0;
- }
- *out_data = buf;
- *out_data_len = retval;
-
- return 1;
-}
-
static krb5_error_code
create_signature(unsigned char **sig, unsigned int *sig_len,
unsigned char *data, unsigned int data_len, EVP_PKEY *pkey)
@@ -4310,8 +4293,7 @@ pkinit_get_certs_pkcs12(krb5_context context,
fp = fopen(idopts->cert_filename, "rb");
if (fp == NULL) {
- pkiDebug("Failed to open PKCS12 file '%s', error %d\n",
- idopts->cert_filename, errno);
+ TRACE_PKINIT_PKCS_OPEN_FAIL(context, idopts->cert_filename, errno);
goto cleanup;
}
set_cloexec_file(fp);
@@ -4319,8 +4301,7 @@ pkinit_get_certs_pkcs12(krb5_context context,
p12 = d2i_PKCS12_fp(fp, NULL);
fclose(fp);
if (p12 == NULL) {
- pkiDebug("Failed to decode PKCS12 file '%s' contents\n",
- idopts->cert_filename);
+ TRACE_PKINIT_PKCS_DECODE_FAIL(context, idopts->cert_filename);
goto cleanup;
}
/*
@@ -4338,7 +4319,7 @@ pkinit_get_certs_pkcs12(krb5_context context,
char *p12name = reassemble_pkcs12_name(idopts->cert_filename);
const char *tmp;
- pkiDebug("Initial PKCS12_parse with no password failed\n");
+ TRACE_PKINIT_PKCS_PARSE_FAIL_FIRST(context);
if (id_cryptoctx->defer_id_prompt) {
/* Supply the identity name to be passed to the responder. */
@@ -4379,14 +4360,14 @@ pkinit_get_certs_pkcs12(krb5_context context,
NULL, NULL, 1, &kprompt);
k5int_set_prompt_types(context, 0);
if (r) {
- pkiDebug("Failed to prompt for PKCS12 password");
+ TRACE_PKINIT_PKCS_PROMPT_FAIL(context);
goto cleanup;
}
}
ret = PKCS12_parse(p12, rdat.data, &y, &x, NULL);
if (ret == 0) {
- pkiDebug("Second PKCS12_parse with password failed\n");
+ TRACE_PKINIT_PKCS_PARSE_FAIL_SECOND(context);
goto cleanup;
}
}
@@ -4509,8 +4490,7 @@ pkinit_get_certs_fs(krb5_context context,
}
if (idopts->key_filename == NULL) {
- pkiDebug("%s: failed to get user's private key location\n",
- __FUNCTION__);
+ TRACE_PKINIT_NO_PRIVKEY(context);
goto cleanup;
}
@@ -4538,8 +4518,7 @@ pkinit_get_certs_dir(krb5_context context,
char *dirname, *suf;
if (idopts->cert_filename == NULL) {
- pkiDebug("%s: failed to get user's certificate directory location\n",
- __FUNCTION__);
+ TRACE_PKINIT_NO_CERT(context);
return ENOENT;
}
@@ -4583,8 +4562,7 @@ pkinit_get_certs_dir(krb5_context context,
retval = pkinit_load_fs_cert_and_key(context, id_cryptoctx,
certname, keyname, i);
if (retval == 0) {
- pkiDebug("%s: Successfully loaded cert (and key) for %s\n",
- __FUNCTION__, dentry->d_name);
+ TRACE_PKINIT_LOADED_CERT(context, dentry->d_name);
i++;
}
else
@@ -4592,8 +4570,7 @@ pkinit_get_certs_dir(krb5_context context,
}
if (!id_cryptoctx->defer_id_prompt && i == 0) {
- pkiDebug("%s: No cert/key pairs found in directory '%s'\n",
- __FUNCTION__, idopts->cert_filename);
+ TRACE_PKINIT_NO_CERT_AND_KEY(context, idopts->cert_filename);
retval = ENOENT;
goto cleanup;
}
@@ -4948,135 +4925,15 @@ cleanup:
}
/*
- * Get number of certificates available after crypto_load_certs()
- */
-krb5_error_code
-crypto_cert_get_count(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context id_cryptoctx,
- int *cert_count)
-{
- int count;
-
- if (id_cryptoctx == NULL || id_cryptoctx->creds[0] == NULL)
- return EINVAL;
-
- for (count = 0;
- count <= MAX_CREDS_ALLOWED && id_cryptoctx->creds[count] != NULL;
- count++);
- *cert_count = count;
- return 0;
-}
-
-
-/*
- * Begin iteration over the certs loaded in crypto_load_certs()
- */
-krb5_error_code
-crypto_cert_iteration_begin(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context id_cryptoctx,
- pkinit_cert_iter_handle *ih_ret)
-{
- struct _pkinit_cert_iter_data *id;
-
- if (id_cryptoctx == NULL || ih_ret == NULL)
- return EINVAL;
- if (id_cryptoctx->creds[0] == NULL) /* No cred info available */
- return ENOENT;
-
- id = calloc(1, sizeof(*id));
- if (id == NULL)
- return ENOMEM;
- id->magic = ITER_MAGIC;
- id->plgctx = plg_cryptoctx,
- id->reqctx = req_cryptoctx,
- id->idctx = id_cryptoctx;
- id->index = 0;
- *ih_ret = (pkinit_cert_iter_handle) id;
- return 0;
-}
-
-/*
- * End iteration over the certs loaded in crypto_load_certs()
- */
-krb5_error_code
-crypto_cert_iteration_end(krb5_context context,
- pkinit_cert_iter_handle ih)
-{
- struct _pkinit_cert_iter_data *id = (struct _pkinit_cert_iter_data *)ih;
-
- if (id == NULL || id->magic != ITER_MAGIC)
- return EINVAL;
- free(ih);
- return 0;
-}
-
-/*
- * Get next certificate handle
- */
-krb5_error_code
-crypto_cert_iteration_next(krb5_context context,
- pkinit_cert_iter_handle ih,
- pkinit_cert_handle *ch_ret)
-{
- struct _pkinit_cert_iter_data *id = (struct _pkinit_cert_iter_data *)ih;
- struct _pkinit_cert_data *cd;
- pkinit_identity_crypto_context id_cryptoctx;
-
- if (id == NULL || id->magic != ITER_MAGIC)
- return EINVAL;
-
- if (ch_ret == NULL)
- return EINVAL;
-
- id_cryptoctx = id->idctx;
- if (id_cryptoctx == NULL)
- return EINVAL;
-
- if (id_cryptoctx->creds[id->index] == NULL)
- return PKINIT_ITER_NO_MORE;
-
- cd = calloc(1, sizeof(*cd));
- if (cd == NULL)
- return ENOMEM;
-
- cd->magic = CERT_MAGIC;
- cd->plgctx = id->plgctx;
- cd->reqctx = id->reqctx;
- cd->idctx = id->idctx;
- cd->index = id->index;
- cd->cred = id_cryptoctx->creds[id->index++];
- *ch_ret = (pkinit_cert_handle)cd;
- return 0;
-}
-
-/*
- * Release cert handle
- */
-krb5_error_code
-crypto_cert_release(krb5_context context,
- pkinit_cert_handle ch)
-{
- struct _pkinit_cert_data *cd = (struct _pkinit_cert_data *)ch;
- if (cd == NULL || cd->magic != CERT_MAGIC)
- return EINVAL;
- free(cd);
- return 0;
-}
-
-/*
* Get certificate Key Usage and Extended Key Usage
*/
static krb5_error_code
-crypto_retieve_X509_key_usage(krb5_context context,
- pkinit_plg_crypto_context plgcctx,
- pkinit_req_crypto_context reqcctx,
- X509 *x,
- unsigned int *ret_ku_bits,
- unsigned int *ret_eku_bits)
+crypto_retrieve_X509_key_usage(krb5_context context,
+ pkinit_plg_crypto_context plgcctx,
+ pkinit_req_crypto_context reqcctx,
+ X509 *x,
+ unsigned int *ret_ku_bits,
+ unsigned int *ret_eku_bits)
{
krb5_error_code retval = 0;
int i;
@@ -5145,85 +5002,112 @@ out:
return retval;
}
-/*
- * Return a string format of an X509_NAME in buf where
- * size is an in/out parameter. On input it is the size
- * of the buffer, and on output it is the actual length
- * of the name.
- * If buf is NULL, returns the length req'd to hold name
- */
-static char *
-X509_NAME_oneline_ex(X509_NAME * a,
- char *buf,
- unsigned int *size,
- unsigned long flag)
+static krb5_error_code
+rfc2253_name(X509_NAME *name, char **str_out)
{
- BIO *out = NULL;
+ BIO *b = NULL;
+ char *str;
- out = BIO_new(BIO_s_mem ());
- if (X509_NAME_print_ex(out, a, 0, flag) > 0) {
- if (buf != NULL && (*size) > (unsigned int) BIO_number_written(out)) {
- memset(buf, 0, *size);
- BIO_read(out, buf, (int) BIO_number_written(out));
- }
- else {
- *size = BIO_number_written(out);
- }
- }
- BIO_free(out);
- return (buf);
+ *str_out = NULL;
+ b = BIO_new(BIO_s_mem());
+ if (b == NULL)
+ return ENOMEM;
+ if (X509_NAME_print_ex(b, name, 0, XN_FLAG_SEP_COMMA_PLUS) < 0)
+ goto error;
+ str = calloc(BIO_number_written(b) + 1, 1);
+ if (str == NULL)
+ goto error;
+ BIO_read(b, str, BIO_number_written(b));
+ BIO_free(b);
+ *str_out = str;
+ return 0;
+
+error:
+ BIO_free(b);
+ return ENOMEM;
}
/*
- * Get certificate information
+ * Get number of certificates available after crypto_load_certs()
*/
-krb5_error_code
-crypto_cert_get_matching_data(krb5_context context,
- pkinit_cert_handle ch,
- pkinit_cert_matching_data **ret_md)
+static krb5_error_code
+crypto_cert_get_count(pkinit_identity_crypto_context id_cryptoctx,
+ int *cert_count)
{
- krb5_error_code retval;
- pkinit_cert_matching_data *md;
- krb5_principal *pkinit_sans =NULL, *upn_sans = NULL;
- struct _pkinit_cert_data *cd = (struct _pkinit_cert_data *)ch;
- unsigned int i, j;
- char buf[DN_BUF_LEN];
- unsigned int bufsize = sizeof(buf);
+ int count;
- if (cd == NULL || cd->magic != CERT_MAGIC)
- return EINVAL;
- if (ret_md == NULL)
+ *cert_count = 0;
+ if (id_cryptoctx == NULL || id_cryptoctx->creds[0] == NULL)
return EINVAL;
- md = calloc(1, sizeof(*md));
+ for (count = 0;
+ count <= MAX_CREDS_ALLOWED && id_cryptoctx->creds[count] != NULL;
+ count++);
+ *cert_count = count;
+ return 0;
+}
+
+void
+crypto_cert_free_matching_data(krb5_context context,
+ pkinit_cert_matching_data *md)
+{
+ int i;
+
if (md == NULL)
- return ENOMEM;
+ return;
+ free(md->subject_dn);
+ free(md->issuer_dn);
+ for (i = 0; md->sans != NULL && md->sans[i] != NULL; i++)
+ krb5_free_principal(context, md->sans[i]);
+ free(md->sans);
+ free(md);
+}
- md->ch = ch;
+/*
+ * Free certificate matching data.
+ */
+void
+crypto_cert_free_matching_data_list(krb5_context context,
+ pkinit_cert_matching_data **list)
+{
+ int i;
- /* get the subject name (in rfc2253 format) */
- X509_NAME_oneline_ex(X509_get_subject_name(cd->cred->cert),
- buf, &bufsize, XN_FLAG_SEP_COMMA_PLUS);
- md->subject_dn = strdup(buf);
- if (md->subject_dn == NULL) {
- retval = ENOMEM;
+ for (i = 0; list != NULL && list[i] != NULL; i++)
+ crypto_cert_free_matching_data(context, list[i]);
+ free(list);
+}
+
+/*
+ * Get certificate matching data for cert.
+ */
+static krb5_error_code
+get_matching_data(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx, X509 *cert,
+ pkinit_cert_matching_data **md_out)
+{
+ krb5_error_code ret = ENOMEM;
+ pkinit_cert_matching_data *md = NULL;
+ krb5_principal *pkinit_sans = NULL, *upn_sans = NULL;
+ size_t i, j;
+
+ *md_out = NULL;
+
+ md = calloc(1, sizeof(*md));
+ if (md == NULL)
goto cleanup;
- }
- /* get the issuer name (in rfc2253 format) */
- X509_NAME_oneline_ex(X509_get_issuer_name(cd->cred->cert),
- buf, &bufsize, XN_FLAG_SEP_COMMA_PLUS);
- md->issuer_dn = strdup(buf);
- if (md->issuer_dn == NULL) {
- retval = ENOMEM;
+ ret = rfc2253_name(X509_get_subject_name(cert), &md->subject_dn);
+ if (ret)
+ goto cleanup;
+ ret = rfc2253_name(X509_get_issuer_name(cert), &md->issuer_dn);
+ if (ret)
goto cleanup;
- }
- /* get the san data */
- retval = crypto_retrieve_X509_sans(context, cd->plgctx, cd->reqctx,
- cd->cred->cert, &pkinit_sans,
- &upn_sans, NULL);
- if (retval)
+ /* Get the SAN data. */
+ ret = crypto_retrieve_X509_sans(context, plg_cryptoctx, req_cryptoctx,
+ cert, &pkinit_sans, &upn_sans, NULL);
+ if (ret)
goto cleanup;
j = 0;
@@ -5238,7 +5122,7 @@ crypto_cert_get_matching_data(krb5_context context,
if (j != 0) {
md->sans = calloc((size_t)j+1, sizeof(*md->sans));
if (md->sans == NULL) {
- retval = ENOMEM;
+ ret = ENOMEM;
goto cleanup;
}
j = 0;
@@ -5256,88 +5140,96 @@ crypto_cert_get_matching_data(krb5_context context,
} else
md->sans = NULL;
- /* get the KU and EKU data */
-
- retval = crypto_retieve_X509_key_usage(context, cd->plgctx, cd->reqctx,
- cd->cred->cert,
- &md->ku_bits, &md->eku_bits);
- if (retval)
+ /* Get the KU and EKU data. */
+ ret = crypto_retrieve_X509_key_usage(context, plg_cryptoctx,
+ req_cryptoctx, cert, &md->ku_bits,
+ &md->eku_bits);
+ if (ret)
goto cleanup;
- *ret_md = md;
- retval = 0;
+ *md_out = md;
+ md = NULL;
+
cleanup:
- if (retval) {
- if (md)
- crypto_cert_free_matching_data(context, md);
- }
- return retval;
+ crypto_cert_free_matching_data(context, md);
+ return ret;
}
-/*
- * Free certificate information
- */
krb5_error_code
-crypto_cert_free_matching_data(krb5_context context,
- pkinit_cert_matching_data *md)
+crypto_cert_get_matching_data(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ pkinit_cert_matching_data ***md_out)
{
- krb5_principal p;
- int i;
+ krb5_error_code ret;
+ pkinit_cert_matching_data **md_list = NULL;
+ int count, i;
- if (md == NULL)
- return EINVAL;
- if (md->subject_dn)
- free(md->subject_dn);
- if (md->issuer_dn)
- free(md->issuer_dn);
- if (md->sans) {
- for (i = 0, p = md->sans[i]; p != NULL; p = md->sans[++i])
- krb5_free_principal(context, p);
- free(md->sans);
+ ret = crypto_cert_get_count(id_cryptoctx, &count);
+ if (ret)
+ goto cleanup;
+
+ md_list = calloc(count + 1, sizeof(*md_list));
+ if (md_list == NULL) {
+ ret = ENOMEM;
+ goto cleanup;
}
- free(md);
- return 0;
+
+ for (i = 0; i < count; i++) {
+ ret = get_matching_data(context, plg_cryptoctx, req_cryptoctx,
+ id_cryptoctx->creds[i]->cert, &md_list[i]);
+ if (ret) {
+ pkiDebug("%s: crypto_cert_get_matching_data error %d, %s\n",
+ __FUNCTION__, ret, error_message(ret));
+ goto cleanup;
+ }
+ }
+
+ *md_out = md_list;
+ md_list = NULL;
+
+cleanup:
+ crypto_cert_free_matching_data_list(context, md_list);
+ return ret;
}
/*
- * Make this matching certificate "the chosen one"
+ * Set the certificate in idctx->creds[cred_index] as the selected certificate.
*/
krb5_error_code
-crypto_cert_select(krb5_context context,
- pkinit_cert_matching_data *md)
+crypto_cert_select(krb5_context context, pkinit_identity_crypto_context idctx,
+ size_t cred_index)
{
- struct _pkinit_cert_data *cd;
- if (md == NULL)
- return EINVAL;
+ pkinit_cred_info ci = NULL;
- cd = (struct _pkinit_cert_data *)md->ch;
- if (cd == NULL || cd->magic != CERT_MAGIC)
- return EINVAL;
+ if (cred_index >= MAX_CREDS_ALLOWED || idctx->creds[cred_index] == NULL)
+ return ENOENT;
+ ci = idctx->creds[cred_index];
/* copy the selected cert into our id_cryptoctx */
- if (cd->idctx->my_certs != NULL) {
- sk_X509_pop_free(cd->idctx->my_certs, X509_free);
- }
- cd->idctx->my_certs = sk_X509_new_null();
- sk_X509_push(cd->idctx->my_certs, cd->cred->cert);
- free(cd->idctx->identity);
+ if (idctx->my_certs != NULL)
+ sk_X509_pop_free(idctx->my_certs, X509_free);
+ idctx->my_certs = sk_X509_new_null();
+ sk_X509_push(idctx->my_certs, ci->cert);
+ free(idctx->identity);
/* hang on to the selected credential name */
- if (cd->idctx->creds[cd->index]->name != NULL)
- cd->idctx->identity = strdup(cd->idctx->creds[cd->index]->name);
+ if (ci->name != NULL)
+ idctx->identity = strdup(ci->name);
else
- cd->idctx->identity = NULL;
- cd->idctx->creds[cd->index]->cert = NULL; /* Don't free it twice */
- cd->idctx->cert_index = 0;
+ idctx->identity = NULL;
- if (cd->idctx->pkcs11_method != 1) {
- cd->idctx->my_key = cd->cred->key;
- cd->idctx->creds[cd->index]->key = NULL; /* Don't free it twice */
+ ci->cert = NULL; /* Don't free it twice */
+ idctx->cert_index = 0;
+ if (idctx->pkcs11_method != 1) {
+ idctx->my_key = ci->key;
+ ci->key = NULL; /* Don't free it twice */
}
#ifndef WITHOUT_PKCS11
else {
- cd->idctx->cert_id = cd->cred->cert_id;
- cd->idctx->creds[cd->index]->cert_id = NULL; /* Don't free it twice */
- cd->idctx->cert_id_len = cd->cred->cert_id_len;
+ idctx->cert_id = ci->cert_id;
+ ci->cert_id = NULL; /* Don't free it twice */
+ idctx->cert_id_len = ci->cert_id_len;
}
#endif
return 0;
@@ -5353,19 +5245,14 @@ crypto_cert_select_default(krb5_context context,
pkinit_identity_crypto_context id_cryptoctx)
{
krb5_error_code retval;
- int cert_count = 0;
+ int cert_count;
- retval = crypto_cert_get_count(context, plg_cryptoctx, req_cryptoctx,
- id_cryptoctx, &cert_count);
- if (retval) {
- pkiDebug("%s: crypto_cert_get_count error %d, %s\n",
- __FUNCTION__, retval, error_message(retval));
+ retval = crypto_cert_get_count(id_cryptoctx, &cert_count);
+ if (retval)
goto errout;
- }
+
if (cert_count != 1) {
- pkiDebug("%s: ERROR: There are %d certs to choose from, "
- "but there must be exactly one.\n",
- __FUNCTION__, cert_count);
+ TRACE_PKINIT_NO_DEFAULT_CERT(context, cert_count);
retval = EINVAL;
goto errout;
}
@@ -5513,7 +5400,7 @@ load_cas_and_crls(krb5_context context,
switch(catype) {
case CATYPE_ANCHORS:
if (sk_X509_num(ca_certs) == 0) {
- pkiDebug("no anchors in file, %s\n", filename);
+ TRACE_PKINIT_NO_CA_ANCHOR(context, filename);
if (id_cryptoctx->trustedCAs == NULL)
sk_X509_free(ca_certs);
} else {
@@ -5523,7 +5410,7 @@ load_cas_and_crls(krb5_context context,
break;
case CATYPE_INTERMEDIATES:
if (sk_X509_num(ca_certs) == 0) {
- pkiDebug("no intermediates in file, %s\n", filename);
+ TRACE_PKINIT_NO_CA_INTERMEDIATE(context, filename);
if (id_cryptoctx->intermediateCAs == NULL)
sk_X509_free(ca_certs);
} else {
@@ -5533,7 +5420,7 @@ load_cas_and_crls(krb5_context context,
break;
case CATYPE_CRLS:
if (sk_X509_CRL_num(ca_crls) == 0) {
- pkiDebug("no crls in file, %s\n", filename);
+ TRACE_PKINIT_NO_CRL(context, filename);
if (id_cryptoctx->revoked == NULL)
sk_X509_CRL_free(ca_crls);
} else {
@@ -5619,14 +5506,14 @@ crypto_load_cas_and_crls(krb5_context context,
int catype,
char *id)
{
- pkiDebug("%s: called with idtype %s and catype %s\n",
- __FUNCTION__, idtype2string(idtype), catype2string(catype));
switch (idtype) {
case IDTYPE_FILE:
+ TRACE_PKINIT_LOAD_FROM_FILE(context);
return load_cas_and_crls(context, plg_cryptoctx, req_cryptoctx,
id_cryptoctx, catype, id);
break;
case IDTYPE_DIR:
+ TRACE_PKINIT_LOAD_FROM_DIR(context);
return load_cas_and_crls_dir(context, plg_cryptoctx, req_cryptoctx,
id_cryptoctx, catype, id);
break;
@@ -6170,3 +6057,50 @@ 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 (der == NULL)
+ return ENOMEM;
+ if (i2d_X509(reqctx->received_cert, &p) <= 0) {
+ free(der);
+ return EINVAL;
+ }
+ *der_out = der;
+ *der_len = len;
+ return 0;
+}
+
+/*
+ * Get the certificate matching data from the request certificate.
+ */
+krb5_error_code
+crypto_req_cert_matching_data(krb5_context context,
+ pkinit_plg_crypto_context plgctx,
+ pkinit_req_crypto_context reqctx,
+ pkinit_cert_matching_data **md_out)
+{
+ *md_out = NULL;
+
+ if (reqctx == NULL || reqctx->received_cert == NULL)
+ return ENOENT;
+
+ return get_matching_data(context, plgctx, reqctx, reqctx->received_cert,
+ md_out);
+}
diff --git a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.h b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.h
index 2fe357c5e733..7411348fab8c 100644
--- a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.h
+++ b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.h
@@ -115,23 +115,4 @@ struct _pkinit_req_crypto_context {
DH *dh;
};
-#define CERT_MAGIC 0x53534c43
-struct _pkinit_cert_data {
- unsigned int magic;
- pkinit_plg_crypto_context plgctx;
- pkinit_req_crypto_context reqctx;
- pkinit_identity_crypto_context idctx;
- pkinit_cred_info cred;
- unsigned int index; /* Index of this cred in the creds[] array */
-};
-
-#define ITER_MAGIC 0x53534c49
-struct _pkinit_cert_iter_data {
- unsigned int magic;
- pkinit_plg_crypto_context plgctx;
- pkinit_req_crypto_context reqctx;
- pkinit_identity_crypto_context idctx;
- unsigned int index;
-};
-
#endif /* _PKINIT_CRYPTO_OPENSSL_H */
diff --git a/src/plugins/preauth/pkinit/pkinit_identity.c b/src/plugins/preauth/pkinit/pkinit_identity.c
index 177a2cad82fa..e8997c9351a8 100644
--- a/src/plugins/preauth/pkinit/pkinit_identity.c
+++ b/src/plugins/preauth/pkinit/pkinit_identity.c
@@ -93,9 +93,6 @@ idtype2string(int idtype)
case IDTYPE_PKCS11: return "PKCS11"; break;
case IDTYPE_PKCS12: return "PKCS12"; break;
case IDTYPE_ENVVAR: return "ENV"; break;
-#ifdef PKINIT_CRYPTO_IMPL_NSS
- case IDTYPE_NSS: return "NSS"; break;
-#endif
default: return "INVALID"; break;
}
}
@@ -125,7 +122,6 @@ pkinit_init_identity_opts(pkinit_identity_opts **idopts)
opts->anchors = NULL;
opts->intermediates = NULL;
opts->crls = NULL;
- opts->ocsp = NULL;
opts->cert_filename = NULL;
opts->key_filename = NULL;
@@ -174,12 +170,6 @@ pkinit_dup_identity_opts(pkinit_identity_opts *src_opts,
if (retval)
goto cleanup;
- if (src_opts->ocsp != NULL) {
- newopts->ocsp = strdup(src_opts->ocsp);
- if (newopts->ocsp == NULL)
- goto cleanup;
- }
-
if (src_opts->cert_filename != NULL) {
newopts->cert_filename = strdup(src_opts->cert_filename);
if (newopts->cert_filename == NULL)
@@ -413,10 +403,6 @@ process_option_identity(krb5_context context,
idtype = IDTYPE_DIR;
} else if (strncmp(value, "ENV:", typelen) == 0) {
idtype = IDTYPE_ENVVAR;
-#ifdef PKINIT_CRYPTO_IMPL_NSS
- } else if (strncmp(value, "NSS:", typelen) == 0) {
- idtype = IDTYPE_NSS;
-#endif
} else {
pkiDebug("%s: Unsupported type while processing '%s'\n",
__FUNCTION__, value);
@@ -453,13 +439,6 @@ process_option_identity(krb5_context context,
if (idopts->cert_filename == NULL)
retval = ENOMEM;
break;
-#ifdef PKINIT_CRYPTO_IMPL_NSS
- case IDTYPE_NSS:
- idopts->cert_filename = strdup(residual);
- if (idopts->cert_filename == NULL)
- retval = ENOMEM;
- break;
-#endif
default:
krb5_set_error_message(context, KRB5_PREAUTH_FAILED,
_("Internal error parsing "
@@ -496,10 +475,6 @@ process_option_ca_crl(krb5_context context,
idtype = IDTYPE_FILE;
} else if (strncmp(value, "DIR:", typelen) == 0) {
idtype = IDTYPE_DIR;
-#ifdef PKINIT_CRYPTO_IMPL_NSS
- } else if (strncmp(value, "NSS:", typelen) == 0) {
- idtype = IDTYPE_NSS;
-#endif
} else {
return ENOTSUP;
}
@@ -615,7 +590,6 @@ pkinit_identity_prompt(krb5_context context,
retval = pkinit_cert_matching(context, plg_cryptoctx,
req_cryptoctx, id_cryptoctx, princ);
if (retval) {
- pkiDebug("%s: No matching certificate found\n", __FUNCTION__);
crypto_free_cert_info(context, plg_cryptoctx, req_cryptoctx,
id_cryptoctx);
goto errout;
@@ -628,8 +602,6 @@ pkinit_identity_prompt(krb5_context context,
retval = crypto_cert_select_default(context, plg_cryptoctx,
req_cryptoctx, id_cryptoctx);
if (retval) {
- pkiDebug("%s: Failed while selecting default certificate\n",
- __FUNCTION__);
crypto_free_cert_info(context, plg_cryptoctx, req_cryptoctx,
id_cryptoctx);
goto errout;
@@ -674,10 +646,6 @@ pkinit_identity_prompt(krb5_context context,
if (retval)
goto errout;
}
- if (idopts->ocsp != NULL) {
- retval = ENOTSUP;
- goto errout;
- }
errout:
return retval;
diff --git a/src/plugins/preauth/pkinit/pkinit_matching.c b/src/plugins/preauth/pkinit/pkinit_matching.c
index a50c50c8dc92..c1ce84b82641 100644
--- a/src/plugins/preauth/pkinit/pkinit_matching.c
+++ b/src/plugins/preauth/pkinit/pkinit_matching.c
@@ -544,7 +544,7 @@ check_all_certs(krb5_context context,
rule_set *rs, /* rule to check */
pkinit_cert_matching_data **matchdata,
int *match_found,
- pkinit_cert_matching_data **matching_cert)
+ size_t *match_index)
{
krb5_error_code retval;
pkinit_cert_matching_data *md;
@@ -553,12 +553,12 @@ check_all_certs(krb5_context context,
int total_cert_matches = 0;
rule_component *rc;
int certs_checked = 0;
- pkinit_cert_matching_data *save_match = NULL;
+ size_t save_index = 0;
- if (match_found == NULL || matching_cert == NULL)
+ if (match_found == NULL || match_index == NULL)
return EINVAL;
- *matching_cert = NULL;
+ *match_index = 0;
*match_found = 0;
pkiDebug("%s: matching rule relation is %s with %d components\n",
@@ -590,7 +590,7 @@ check_all_certs(krb5_context context,
pkiDebug("%s: cert matches rule (OR relation)\n",
__FUNCTION__);
total_cert_matches++;
- save_match = md;
+ save_index = i;
goto nextcert;
}
if (!comp_match && rs->relation == relation_and) {
@@ -602,7 +602,7 @@ check_all_certs(krb5_context context,
if (rc == NULL && comp_match) {
pkiDebug("%s: cert matches rule (AND relation)\n", __FUNCTION__);
total_cert_matches++;
- save_match = md;
+ save_index = i;
}
nextcert:
continue;
@@ -611,7 +611,7 @@ check_all_certs(krb5_context context,
__FUNCTION__, certs_checked, total_cert_matches);
if (total_cert_matches == 1) {
*match_found = 1;
- *matching_cert = save_match;
+ *match_index = save_index;
}
retval = 0;
@@ -621,111 +621,6 @@ check_all_certs(krb5_context context,
return retval;
}
-static krb5_error_code
-free_all_cert_matching_data(krb5_context context,
- pkinit_cert_matching_data **matchdata)
-{
- krb5_error_code retval;
- pkinit_cert_matching_data *md;
- int i;
-
- if (matchdata == NULL)
- return EINVAL;
-
- for (i = 0, md = matchdata[i]; md != NULL; md = matchdata[++i]) {
- pkinit_cert_handle ch = md->ch;
- retval = crypto_cert_free_matching_data(context, md);
- if (retval) {
- pkiDebug("%s: crypto_cert_free_matching_data error %d, %s\n",
- __FUNCTION__, retval, error_message(retval));
- goto cleanup;
- }
- retval = crypto_cert_release(context, ch);
- if (retval) {
- pkiDebug("%s: crypto_cert_release error %d, %s\n",
- __FUNCTION__, retval, error_message(retval));
- goto cleanup;
- }
- }
- free(matchdata);
- retval = 0;
-
-cleanup:
- return retval;
-}
-
-static krb5_error_code
-obtain_all_cert_matching_data(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context id_cryptoctx,
- pkinit_cert_matching_data ***all_matching_data)
-{
- krb5_error_code retval;
- int i, cert_count;
- pkinit_cert_iter_handle ih = NULL;
- pkinit_cert_handle ch;
- pkinit_cert_matching_data **matchdata = NULL;
-
- retval = crypto_cert_get_count(context, plg_cryptoctx, req_cryptoctx,
- id_cryptoctx, &cert_count);
- if (retval) {
- pkiDebug("%s: crypto_cert_get_count error %d, %s\n",
- __FUNCTION__, retval, error_message(retval));
- goto cleanup;
- }
-
- pkiDebug("%s: crypto_cert_get_count says there are %d certs\n",
- __FUNCTION__, cert_count);
-
- matchdata = calloc((size_t)cert_count + 1, sizeof(*matchdata));
- if (matchdata == NULL)
- return ENOMEM;
-
- retval = crypto_cert_iteration_begin(context, plg_cryptoctx, req_cryptoctx,
- id_cryptoctx, &ih);
- if (retval) {
- pkiDebug("%s: crypto_cert_iteration_begin returned %d, %s\n",
- __FUNCTION__, retval, error_message(retval));
- goto cleanup;
- }
-
- for (i = 0; i < cert_count; i++) {
- retval = crypto_cert_iteration_next(context, ih, &ch);
- if (retval) {
- if (retval == PKINIT_ITER_NO_MORE)
- pkiDebug("%s: We thought there were %d certs, but "
- "crypto_cert_iteration_next stopped after %d?\n",
- __FUNCTION__, cert_count, i);
- else
- pkiDebug("%s: crypto_cert_iteration_next error %d, %s\n",
- __FUNCTION__, retval, error_message(retval));
- goto cleanup;
- }
-
- retval = crypto_cert_get_matching_data(context, ch, &matchdata[i]);
- if (retval) {
- pkiDebug("%s: crypto_cert_get_matching_data error %d, %s\n",
- __FUNCTION__, retval, error_message(retval));
- goto cleanup;
- }
-
- }
-
- *all_matching_data = matchdata;
- retval = 0;
-cleanup:
- if (ih != NULL)
- crypto_cert_iteration_end(context, ih);
- if (retval) {
- if (matchdata != NULL)
- free_all_cert_matching_data(context, matchdata);
- }
- pkiDebug("%s: returning %d, certinfo %p\n",
- __FUNCTION__, retval, *all_matching_data);
- return retval;
-}
-
krb5_error_code
pkinit_cert_matching(krb5_context context,
pkinit_plg_crypto_context plg_cryptoctx,
@@ -740,7 +635,7 @@ pkinit_cert_matching(krb5_context context,
rule_set *rs = NULL;
int match_found = 0;
pkinit_cert_matching_data **matchdata = NULL;
- pkinit_cert_matching_data *the_matching_cert = NULL;
+ size_t match_index = 0;
/* If no matching rules, select the default cert and we're done */
pkinit_libdefault_strings(context, krb5_princ_realm(context, princ),
@@ -777,7 +672,7 @@ pkinit_cert_matching(krb5_context context,
* until we are done.
*/
if (matchdata == NULL) {
- retval = obtain_all_cert_matching_data(context, plg_cryptoctx,
+ retval = crypto_cert_get_matching_data(context, plg_cryptoctx,
req_cryptoctx, id_cryptoctx,
&matchdata);
if (retval || matchdata == NULL) {
@@ -790,7 +685,7 @@ pkinit_cert_matching(krb5_context context,
retval = check_all_certs(context, plg_cryptoctx, req_cryptoctx,
id_cryptoctx, princ, rs, matchdata,
- &match_found, &the_matching_cert);
+ &match_found, &match_index);
if (retval) {
pkiDebug("%s: Error %d, checking certs against rule '%s'\n",
__FUNCTION__, retval, rules[x]);
@@ -803,26 +698,62 @@ pkinit_cert_matching(krb5_context context,
}
}
- if (match_found && the_matching_cert != NULL) {
+ if (match_found) {
pkiDebug("%s: Selecting the matching cert!\n", __FUNCTION__);
- retval = crypto_cert_select(context, the_matching_cert);
+ retval = crypto_cert_select(context, id_cryptoctx, match_index);
if (retval) {
pkiDebug("%s: crypto_cert_select error %d, %s\n",
__FUNCTION__, retval, error_message(retval));
goto cleanup;
}
} else {
+ TRACE_PKINIT_NO_MATCHING_CERT(context);
retval = ENOENT; /* XXX */
goto cleanup;
}
retval = 0;
+
cleanup:
- if (rules != NULL)
- profile_free_list(rules);
- if (rs != NULL)
- free_rule_set(context, rs);
- if (matchdata != NULL)
- free_all_cert_matching_data(context, matchdata);
+ profile_free_list(rules);
+ free_rule_set(context, rs);
+ crypto_cert_free_matching_data_list(context, matchdata);
return retval;
}
+
+krb5_error_code
+pkinit_client_cert_match(krb5_context context,
+ pkinit_plg_crypto_context plgctx,
+ pkinit_req_crypto_context reqctx,
+ const char *match_rule,
+ krb5_boolean *matched)
+{
+ krb5_error_code ret;
+ pkinit_cert_matching_data *md = NULL;
+ rule_component *rc = NULL;
+ int comp_match = 0;
+ rule_set *rs = NULL;
+
+ *matched = FALSE;
+ ret = parse_rule_set(context, match_rule, &rs);
+ if (ret)
+ goto cleanup;
+
+ ret = crypto_req_cert_matching_data(context, plgctx, reqctx, &md);
+ if (ret)
+ goto cleanup;
+
+ for (rc = rs->crs; rc != NULL; rc = rc->next) {
+ comp_match = component_match(context, rc, md);
+ if ((comp_match && rs->relation == relation_or) ||
+ (!comp_match && rs->relation == relation_and)) {
+ break;
+ }
+ }
+ *matched = comp_match;
+
+cleanup:
+ free_rule_set(context, rs);
+ crypto_cert_free_matching_data(context, md);
+ return ret;
+}
diff --git a/src/plugins/preauth/pkinit/pkinit_srv.c b/src/plugins/preauth/pkinit/pkinit_srv.c
index 295be25e18bf..4e9685885f80 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,
@@ -121,7 +168,9 @@ static krb5_error_code
verify_client_san(krb5_context context,
pkinit_kdc_context plgctx,
pkinit_kdc_req_context reqctx,
- krb5_principal client,
+ krb5_kdcpreauth_callbacks cb,
+ krb5_kdcpreauth_rock rock,
+ krb5_const_principal client,
int *valid_san)
{
krb5_error_code retval;
@@ -132,6 +181,7 @@ 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,
@@ -142,6 +192,13 @@ verify_client_san(krb5_context context,
retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
goto out;
}
+
+ if (princs == NULL && upns == NULL) {
+ TRACE_PKINIT_SERVER_NO_SAN(context);
+ retval = ENOENT;
+ goto out;
+ }
+
/* XXX Verify this is consistent with client side XXX */
#if 0
retval = call_san_checking_plugins(context, plgctx, reqctx, princs,
@@ -171,8 +228,8 @@ verify_client_san(krb5_context context,
__FUNCTION__, client_string, san_string);
krb5_free_unparsed_name(context, san_string);
#endif
- if (krb5_principal_compare(context, princs[i], client)) {
- pkiDebug("%s: pkinit san match found\n", __FUNCTION__);
+ if (cb->match_client(context, rock, princs[i])) {
+ TRACE_PKINIT_SERVER_MATCHING_SAN_FOUND(context);
*valid_san = 1;
retval = 0;
goto out;
@@ -199,8 +256,8 @@ verify_client_san(krb5_context context,
__FUNCTION__, client_string, san_string);
krb5_free_unparsed_name(context, san_string);
#endif
- if (krb5_principal_compare(context, upns[i], client)) {
- pkiDebug("%s: upn san match found\n", __FUNCTION__);
+ if (cb->match_client(context, rock, upns[i])) {
+ TRACE_PKINIT_SERVER_MATCHING_UPN_FOUND(context);
*valid_san = 1;
retval = 0;
goto out;
@@ -248,7 +305,7 @@ verify_client_eku(krb5_context context,
*eku_accepted = 0;
if (plgctx->opts->require_eku == 0) {
- pkiDebug("%s: configuration requests no EKU checking\n", __FUNCTION__);
+ TRACE_PKINIT_SERVER_EKU_SKIP(context);
*eku_accepted = 1;
retval = 0;
goto out;
@@ -271,6 +328,74 @@ 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];
+ TRACE_PKINIT_SERVER_CERT_AUTH(context, h->vt.name);
+ 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,
@@ -293,7 +418,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;
@@ -331,7 +455,7 @@ pkinit_server_verify_padata(krb5_context context,
switch ((int)data->pa_type) {
case KRB5_PADATA_PK_AS_REQ:
- pkiDebug("processing KRB5_PADATA_PK_AS_REQ\n");
+ TRACE_PKINIT_SERVER_PADATA_VERIFY(context);
retval = k5int_decode_krb5_pa_pk_as_req(&k5data, &reqp);
if (retval) {
pkiDebug("decode_krb5_pa_pk_as_req failed\n");
@@ -354,7 +478,7 @@ pkinit_server_verify_padata(krb5_context context,
break;
case KRB5_PADATA_PK_AS_REP_OLD:
case KRB5_PADATA_PK_AS_REQ_OLD:
- pkiDebug("processing KRB5_PADATA_PK_AS_REQ_OLD\n");
+ TRACE_PKINIT_SERVER_PADATA_VERIFY_OLD(context);
retval = k5int_decode_krb5_pa_pk_as_req_draft9(&k5data, &reqp9);
if (retval) {
pkiDebug("decode_krb5_pa_pk_as_req_draft9 failed\n");
@@ -382,31 +506,15 @@ pkinit_server_verify_padata(krb5_context context,
goto cleanup;
}
if (retval) {
- pkiDebug("pkcs7_signeddata_verify failed\n");
+ TRACE_PKINIT_SERVER_PADATA_VERIFY_FAIL(context);
goto cleanup;
}
if (is_signed) {
-
- retval = verify_client_san(context, plgctx, reqctx, 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())) {
@@ -728,7 +836,7 @@ pkinit_server_return_padata(krb5_context context,
return ENOENT;
}
- pkiDebug("pkinit_return_padata: entered!\n");
+ TRACE_PKINIT_SERVER_RETURN_PADATA(context);
reqctx = (pkinit_kdc_req_context)modreq;
if (encrypting_key->contents) {
@@ -1150,7 +1258,7 @@ static krb5_error_code
pkinit_init_kdc_profile(krb5_context context, pkinit_kdc_context plgctx)
{
krb5_error_code retval;
- char *eku_string = NULL;
+ char *eku_string = NULL, *ocsp_check = NULL;
pkiDebug("%s: entered for realm %s\n", __FUNCTION__, plgctx->realmname);
retval = pkinit_kdcdefault_string(context, plgctx->realmname,
@@ -1185,7 +1293,15 @@ pkinit_init_kdc_profile(krb5_context context, pkinit_kdc_context plgctx)
pkinit_kdcdefault_string(context, plgctx->realmname,
KRB5_CONF_PKINIT_KDC_OCSP,
- &plgctx->idopts->ocsp);
+ &ocsp_check);
+ if (ocsp_check != NULL) {
+ free(ocsp_check);
+ retval = ENOTSUP;
+ krb5_set_error_message(context, retval,
+ _("OCSP is not supported: (realm: %s)"),
+ plgctx->realmname);
+ goto errout;
+ }
pkinit_kdcdefault_integer(context, plgctx->realmname,
KRB5_CONF_PKINIT_DH_MIN_BITS,
@@ -1243,11 +1359,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];
@@ -1329,6 +1449,211 @@ 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 struct _krb5_db_entry_new *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) {
+ TRACE_PKINIT_SERVER_SAN_REJECT(context);
+ 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 struct _krb5_db_entry_new *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) {
+ TRACE_PKINIT_SERVER_EKU_REJECT(context);
+ return KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE;
+ }
+
+ return KRB5_PLUGIN_NO_HANDLE;
+}
+
+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;
+}
+
+/*
+ * Do certificate auth based on a match expression in the pkinit_cert_match
+ * attribute string. An expression should be in the same form as those used
+ * for the pkinit_cert_match configuration option.
+ */
+static krb5_error_code
+dbmatch_authorize(krb5_context context, krb5_certauth_moddata moddata,
+ const uint8_t *cert, size_t cert_len,
+ krb5_const_principal princ, const void *opts,
+ const struct _krb5_db_entry_new *db_entry,
+ char ***authinds_out)
+{
+ krb5_error_code ret;
+ const struct certauth_req_opts *req_opts = opts;
+ char *pattern;
+ krb5_boolean matched;
+
+ *authinds_out = NULL;
+
+ /* Fetch the matching pattern. Pass if it isn't specified. */
+ ret = req_opts->cb->get_string(context, req_opts->rock,
+ "pkinit_cert_match", &pattern);
+ if (ret)
+ return ret;
+ if (pattern == NULL)
+ return KRB5_PLUGIN_NO_HANDLE;
+
+ /* Check the certificate against the match expression. */
+ ret = pkinit_client_cert_match(context, req_opts->plgctx->cryptoctx,
+ req_opts->reqctx->cryptoctx, pattern,
+ &matched);
+ req_opts->cb->free_string(context, req_opts->rock, pattern);
+ if (ret)
+ return ret;
+ return matched ? 0 : KRB5KDC_ERR_CERTIFICATE_MISMATCH;
+}
+
+static krb5_error_code
+certauth_dbmatch_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 = "dbmatch";
+ vt->authorize = dbmatch_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_register(context, PLUGIN_INTERFACE_CERTAUTH, "dbmatch",
+ certauth_dbmatch_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,
@@ -1336,6 +1661,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;
@@ -1352,30 +1679,43 @@ pkinit_server_plugin_init(krb5_context context,
return ENOMEM;
for (i = 0, j = 0; i < numrealms; i++) {
- pkiDebug("%s: processing realm '%s'\n", __FUNCTION__, realmnames[i]);
- retval = pkinit_server_plugin_init_realm(context, realmnames[i], &plgctx);
- if (retval == 0 && plgctx != NULL)
+ TRACE_PKINIT_SERVER_INIT_REALM(context, realmnames[i]);
+ krb5_clear_error_message(context);
+ retval = pkinit_server_plugin_init_realm(context, realmnames[i],
+ &plgctx);
+ if (retval)
+ TRACE_PKINIT_SERVER_INIT_FAIL(context, realmnames[i], retval);
+ else
realm_contexts[j++] = plgctx;
}
if (j == 0) {
- retval = EINVAL;
- krb5_set_error_message(context, retval,
- _("No realms configured correctly for pkinit "
- "support"));
+ if (numrealms == 1) {
+ k5_prependmsg(context, retval, "PKINIT initialization failed");
+ } else {
+ retval = EINVAL;
+ k5_setmsg(context, retval,
+ _("No realms configured correctly for pkinit support"));
+ }
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;
-errout:
- if (retval) {
- pkinit_server_plugin_fini(context,
- (krb5_kdcpreauth_moddata)realm_contexts);
- }
+ 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:
+ free_realm_contexts(context, realm_contexts);
+ free_certauth_handles(context, certauth_modules);
return retval;
}
@@ -1403,17 +1743,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 b3f5cbb20e18..d4eb39d88b95 100644
--- a/src/plugins/preauth/pkinit/pkinit_trace.h
+++ b/src/plugins/preauth/pkinit/pkinit_trace.h
@@ -47,12 +47,14 @@
#define TRACE_PKINIT_CLIENT_KDF_OS2K(c, keyblock) \
TRACE(c, "PKINIT client used octetstring2key to compute reply key " \
"{keyblock}", keyblock)
+#define TRACE_PKINIT_CLIENT_NO_DRAFT9(c) \
+ TRACE(c, "PKINIT client ignoring draft 9 offer from RFC 4556 KDC")
#define TRACE_PKINIT_CLIENT_NO_IDENTITY(c) \
TRACE(c, "PKINIT client has no configured identity; giving up")
#define TRACE_PKINIT_CLIENT_REP_CHECKSUM_FAIL(c, expected, received) \
TRACE(c, "PKINIT client checksum mismatch: expected {cksum}, " \
"received {cksum}", expected, received)
-#define TRACE_PKINIT_CLIENT_REP_DH(c) \
+#define TRACE_PKINIT_CLIENT_REP_DH(c) \
TRACE(c, "PKINIT client verified DH reply")
#define TRACE_PKINIT_CLIENT_REP_DH_FAIL(c) \
TRACE(c, "PKINIT client could not verify DH reply")
@@ -91,4 +93,78 @@
#define TRACE_PKINIT_OPENSSL_ERROR(c, msg) \
TRACE(c, "PKINIT OpenSSL error: {str}", msg)
+#define TRACE_PKINIT_SERVER_CERT_AUTH(c, modname) \
+ TRACE(c, "PKINIT server authorizing cert with module {str}", \
+ modname)
+#define TRACE_PKINIT_SERVER_EKU_REJECT(c) \
+ TRACE(c, "PKINIT server found no acceptable EKU in client cert")
+#define TRACE_PKINIT_SERVER_EKU_SKIP(c) \
+ TRACE(c, "PKINIT server skipping EKU check due to configuration")
+#define TRACE_PKINIT_SERVER_INIT_REALM(c, realm) \
+ TRACE(c, "PKINIT server initializing realm {str}", realm)
+#define TRACE_PKINIT_SERVER_INIT_FAIL(c, realm, retval) \
+ TRACE(c, "PKINIT server initialization failed for realm {str}: {kerr}", \
+ realm, retval)
+#define TRACE_PKINIT_SERVER_MATCHING_UPN_FOUND(c) \
+ TRACE(c, "PKINIT server found a matching UPN SAN in client cert")
+#define TRACE_PKINIT_SERVER_MATCHING_SAN_FOUND(c) \
+ TRACE(c, "PKINIT server found a matching SAN in client cert")
+#define TRACE_PKINIT_SERVER_NO_SAN(c) \
+ TRACE(c, "PKINIT server found no SAN in client cert")
+#define TRACE_PKINIT_SERVER_PADATA_VERIFY(c) \
+ TRACE(c, "PKINIT server verifying KRB5_PADATA_PK_AS_REQ")
+#define TRACE_PKINIT_SERVER_PADATA_VERIFY_OLD(c) \
+ TRACE(c, "PKINIT server verifying KRB5_PADATA_PK_AS_REQ_OLD")
+#define TRACE_PKINIT_SERVER_PADATA_VERIFY_FAIL(c) \
+ TRACE(c, "PKINIT server failed to verify PA data")
+#define TRACE_PKINIT_SERVER_RETURN_PADATA(c) \
+ TRACE(c, "PKINIT server returning PA data")
+#define TRACE_PKINIT_SERVER_SAN_REJECT(c) \
+ TRACE(c, "PKINIT server found no acceptable SAN in client cert")
+
+#define TRACE_PKINIT_EKU(c) \
+ TRACE(c, "PKINIT found acceptable EKU and digitalSignature KU")
+#define TRACE_PKINIT_EKU_NO_KU(c) \
+ TRACE(c, "PKINIT found acceptable EKU but no digitalSignature KU")
+#define TRACE_PKINIT_LOADED_CERT(c, name) \
+ TRACE(c, "PKINIT loaded cert and key for {str}", name)
+#define TRACE_PKINIT_LOAD_FROM_FILE(c) \
+ TRACE(c, "PKINIT loading CA certs and CRLs from FILE")
+#define TRACE_PKINIT_LOAD_FROM_DIR(c) \
+ TRACE(c, "PKINIT loading CA certs and CRLs from DIR")
+#define TRACE_PKINIT_NO_CA_ANCHOR(c, file) \
+ TRACE(c, "PKINIT no anchor CA in file {str}", file)
+#define TRACE_PKINIT_NO_CA_INTERMEDIATE(c, file) \
+ TRACE(c, "PKINIT no intermediate CA in file {str}", file)
+#define TRACE_PKINIT_NO_CERT(c) \
+ TRACE(c, "PKINIT no certificate provided")
+#define TRACE_PKINIT_NO_CERT_AND_KEY(c, dirname) \
+ TRACE(c, "PKINIT no cert and key pair found in directory {str}", \
+ dirname)
+#define TRACE_PKINIT_NO_CRL(c, file) \
+ TRACE(c, "PKINIT no CRL in file {str}", file)
+#define TRACE_PKINIT_NO_DEFAULT_CERT(c, count) \
+ TRACE(c, "PKINIT error: There are {int} certs, but there must " \
+ "be exactly one.", count)
+#define TRACE_PKINIT_NO_MATCHING_CERT(c) \
+ TRACE(c, "PKINIT no matching certificate found")
+#define TRACE_PKINIT_NO_PRIVKEY(c) \
+ TRACE(c, "PKINIT no private key provided")
+#define TRACE_PKINIT_PKCS_DECODE_FAIL(c, name) \
+ TRACE(c, "PKINIT failed to decode PKCS12 file {str} contents", name)
+#define TRACE_PKINIT_PKCS_OPEN_FAIL(c, name, err) \
+ TRACE(c, "PKINIT failed to open PKCS12 file {str}: err {errno}", \
+ name, err)
+#define TRACE_PKINIT_PKCS_PARSE_FAIL_FIRST(c) \
+ TRACE(c, "PKINIT initial PKCS12_parse with no password failed")
+#define TRACE_PKINIT_PKCS_PARSE_FAIL_SECOND(c) \
+ TRACE(c, "PKINIT second PKCS12_parse with password failed")
+#define TRACE_PKINIT_PKCS_PROMPT_FAIL(c) \
+ TRACE(c, "PKINIT failed to prompt for PKCS12 password")
+
+#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/plugins/preauth/test/Makefile.in b/src/plugins/preauth/test/Makefile.in
index ac3cb8155bb2..77321b60f5e2 100644
--- a/src/plugins/preauth/test/Makefile.in
+++ b/src/plugins/preauth/test/Makefile.in
@@ -9,9 +9,9 @@ RELDIR=../plugins/preauth/test
SHLIB_EXPDEPS=$(KRB5_BASE_DEPLIBS)
SHLIB_EXPLIBS=$(KRB5_BASE_LIBS)
-STLIBOBJS=cltest.o kdctest.o
+STLIBOBJS=cltest.o kdctest.o common.o
-SRCS= $(srcdir)/cltest.c $(srcdir)/kdctest.c
+SRCS= $(srcdir)/cltest.c $(srcdir)/kdctest.c $(srcdir)/common.c
all-unix: all-liblinks
install-unix: install-libs
diff --git a/src/plugins/preauth/test/cltest.c b/src/plugins/preauth/test/cltest.c
index 4c31e1c0f8b2..f5f7c5aba167 100644
--- a/src/plugins/preauth/test/cltest.c
+++ b/src/plugins/preauth/test/cltest.c
@@ -1,7 +1,7 @@
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/* plugins/preauth/test/cltest.c - Test clpreauth module */
/*
- * Copyright (C) 2015 by the Massachusetts Institute of Technology.
+ * Copyright (C) 2015, 2017 by the Massachusetts Institute of Technology.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -32,7 +32,7 @@
/*
* This module is used to test preauth interface features. At this time, the
- * clpreauth module does two things:
+ * clpreauth module does the following:
*
* - It decrypts a message from the initial KDC pa-data using the reply key and
* prints it to stdout. (The unencrypted message "no key" can also be
@@ -45,17 +45,27 @@
* it to the server, instructing the kdcpreauth module to assert one or more
* space-separated authentication indicators. (This string is sent on both
* round trips if a second round trip is requested.)
+ *
+ * - If a KDC_ERR_ENCTYPE_NOSUPP error with e-data is received, it prints the
+ * accompanying error padata and sends a follow-up request containing
+ * "tryagain".
+ *
+ * - If the "fail_optimistic", "fail_2rt", or "fail_tryagain" gic options are
+ * set, it fails with a recognizable error string at the requested point in
+ * processing.
*/
#include "k5-int.h"
#include <krb5/clpreauth_plugin.h>
-
-#define TEST_PA_TYPE -123
+#include "common.h"
static krb5_preauthtype pa_types[] = { TEST_PA_TYPE, 0 };
struct client_state {
char *indicators;
+ krb5_boolean fail_optimistic;
+ krb5_boolean fail_2rt;
+ krb5_boolean fail_tryagain;
};
struct client_request_state {
@@ -70,6 +80,7 @@ test_init(krb5_context context, krb5_clpreauth_moddata *moddata_out)
st = malloc(sizeof(*st));
assert(st != NULL);
st->indicators = NULL;
+ st->fail_optimistic = st->fail_2rt = st->fail_tryagain = FALSE;
*moddata_out = (krb5_clpreauth_moddata)st;
return 0;
}
@@ -114,7 +125,6 @@ test_process(krb5_context context, krb5_clpreauth_moddata moddata,
struct client_state *st = (struct client_state *)moddata;
struct client_request_state *reqst = (struct client_request_state *)modreq;
krb5_error_code ret;
- krb5_pa_data **list, *pa;
krb5_keyblock *k;
krb5_enc_data enc;
krb5_data plain;
@@ -123,20 +133,18 @@ test_process(krb5_context context, krb5_clpreauth_moddata moddata,
if (pa_data->length == 0) {
/* This is an optimistic preauth test. Send a recognizable padata
* value so the KDC knows not to expect a cookie. */
- list = k5calloc(2, sizeof(*list), &ret);
- assert(!ret);
- pa = k5alloc(sizeof(*pa), &ret);
- assert(!ret);
- pa->pa_type = TEST_PA_TYPE;
- pa->contents = (uint8_t *)strdup("optimistic");
- assert(pa->contents != NULL);
- pa->length = 10;
- list[0] = pa;
- list[1] = NULL;
- *out_pa_data = list;
+ if (st->fail_optimistic) {
+ k5_setmsg(context, KRB5_PREAUTH_FAILED, "induced optimistic fail");
+ return KRB5_PREAUTH_FAILED;
+ }
+ *out_pa_data = make_pa_list("optimistic", 10);
return 0;
} else if (reqst->second_round_trip) {
printf("2rt: %.*s\n", pa_data->length, pa_data->contents);
+ if (st->fail_2rt) {
+ k5_setmsg(context, KRB5_PREAUTH_FAILED, "induced 2rt fail");
+ return KRB5_PREAUTH_FAILED;
+ }
} else if (pa_data->length == 6 &&
memcmp(pa_data->contents, "no key", 6) == 0) {
printf("no key\n");
@@ -157,17 +165,34 @@ test_process(krb5_context context, krb5_clpreauth_moddata moddata,
reqst->second_round_trip = TRUE;
indstr = (st->indicators != NULL) ? st->indicators : "";
- list = k5calloc(2, sizeof(*list), &ret);
- assert(!ret);
- pa = k5alloc(sizeof(*pa), &ret);
- assert(!ret);
- pa->pa_type = TEST_PA_TYPE;
- pa->contents = (uint8_t *)strdup(indstr);
- assert(pa->contents != NULL);
- pa->length = strlen(indstr);
- list[0] = pa;
- list[1] = NULL;
- *out_pa_data = list;
+ *out_pa_data = make_pa_list(indstr, strlen(indstr));
+ return 0;
+}
+
+static krb5_error_code
+test_tryagain(krb5_context context, krb5_clpreauth_moddata moddata,
+ krb5_clpreauth_modreq modreq, krb5_get_init_creds_opt *opt,
+ krb5_clpreauth_callbacks cb, krb5_clpreauth_rock rock,
+ krb5_kdc_req *request, krb5_data *enc_req, krb5_data *enc_prev,
+ krb5_preauthtype pa_type, krb5_error *error,
+ krb5_pa_data **padata, krb5_prompter_fct prompter,
+ void *prompter_data, krb5_pa_data ***padata_out)
+{
+ struct client_state *st = (struct client_state *)moddata;
+ int i;
+
+ *padata_out = NULL;
+ if (st->fail_tryagain) {
+ k5_setmsg(context, KRB5_PREAUTH_FAILED, "induced tryagain fail");
+ return KRB5_PREAUTH_FAILED;
+ }
+ if (error->error != KDC_ERR_ENCTYPE_NOSUPP)
+ return KRB5_PREAUTH_FAILED;
+ for (i = 0; padata[i] != NULL; i++) {
+ if (padata[i]->pa_type == TEST_PA_TYPE)
+ printf("tryagain: %.*s\n", padata[i]->length, padata[i]->contents);
+ }
+ *padata_out = make_pa_list("tryagain", 8);
return 0;
}
@@ -181,6 +206,12 @@ test_gic_opt(krb5_context kcontext, krb5_clpreauth_moddata moddata,
free(st->indicators);
st->indicators = strdup(value);
assert(st->indicators != NULL);
+ } else if (strcmp(attr, "fail_optimistic") == 0) {
+ st->fail_optimistic = TRUE;
+ } else if (strcmp(attr, "fail_2rt") == 0) {
+ st->fail_2rt = TRUE;
+ } else if (strcmp(attr, "fail_tryagain") == 0) {
+ st->fail_tryagain = TRUE;
}
return 0;
}
@@ -205,6 +236,7 @@ clpreauth_test_initvt(krb5_context context, int maj_ver,
vt->request_init = test_request_init;
vt->request_fini = test_request_fini;
vt->process = test_process;
+ vt->tryagain = test_tryagain;
vt->gic_opts = test_gic_opt;
return 0;
}
diff --git a/src/plugins/preauth/test/common.c b/src/plugins/preauth/test/common.c
new file mode 100644
index 000000000000..4d1f49dfafc5
--- /dev/null
+++ b/src/plugins/preauth/test/common.c
@@ -0,0 +1,61 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* plugins/preauth/test/common.c - common functions for test preauth module */
+/*
+ * Copyright (C) 2017 by the Massachusetts Institute of Technology.
+ * 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 "common.h"
+
+krb5_pa_data *
+make_pa(const char *contents, size_t len)
+{
+ krb5_error_code ret;
+ krb5_pa_data *pa;
+
+ pa = calloc(1, sizeof(*pa));
+ assert(pa != NULL);
+ pa->pa_type = TEST_PA_TYPE;
+ pa->contents = k5memdup(contents, len, &ret);
+ assert(!ret);
+ pa->length = len;
+ return pa;
+}
+
+/* Make a one-element padata list of type TEST_PA_TYPE. */
+krb5_pa_data **
+make_pa_list(const char *contents, size_t len)
+{
+ krb5_pa_data **list;
+
+ list = calloc(2, sizeof(*list));
+ assert(list != NULL);
+ list[0] = make_pa(contents, len);
+ return list;
+}
diff --git a/src/plugins/preauth/test/common.h b/src/plugins/preauth/test/common.h
new file mode 100644
index 000000000000..b748e0874bc6
--- /dev/null
+++ b/src/plugins/preauth/test/common.h
@@ -0,0 +1,41 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* plugins/preauth/test/common.h - Declarations for test preauth module */
+/*
+ * Copyright (C) 2017 by the Massachusetts Institute of Technology.
+ * 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.
+ */
+
+#ifndef COMMON_H
+#define COMMON_H
+
+#define TEST_PA_TYPE -123
+
+krb5_pa_data *make_pa(const char *contents, size_t len);
+krb5_pa_data **make_pa_list(const char *contents, size_t len);
+
+#endif /* COMMON_H */
diff --git a/src/plugins/preauth/test/deps b/src/plugins/preauth/test/deps
index b48f0003261c..b1429e9e15f5 100644
--- a/src/plugins/preauth/test/deps
+++ b/src/plugins/preauth/test/deps
@@ -11,7 +11,7 @@ cltest.so cltest.po $(OUTPRE)cltest.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
$(top_srcdir)/include/krb5/clpreauth_plugin.h $(top_srcdir)/include/krb5/plugin.h \
$(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
- cltest.c
+ cltest.c common.h
kdctest.so kdctest.po $(OUTPRE)kdctest.$(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 \
@@ -22,4 +22,14 @@ kdctest.so kdctest.po $(OUTPRE)kdctest.$(OBJEXT): $(BUILDTOP)/include/autoconf.h
$(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
$(top_srcdir)/include/krb5/kdcpreauth_plugin.h $(top_srcdir)/include/krb5/plugin.h \
$(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
- kdctest.c
+ common.h kdctest.c
+common.so common.po $(OUTPRE)common.$(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/plugin.h $(top_srcdir)/include/port-sockets.h \
+ $(top_srcdir)/include/socket-utils.h common.c common.h
diff --git a/src/plugins/preauth/test/kdctest.c b/src/plugins/preauth/test/kdctest.c
index 026dc680dc07..66b77969a3d0 100644
--- a/src/plugins/preauth/test/kdctest.c
+++ b/src/plugins/preauth/test/kdctest.c
@@ -1,7 +1,7 @@
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/* plugins/preauth/test/kdctest.c - Test kdcpreauth module */
/*
- * Copyright (C) 2015 by the Massachusetts Institute of Technology.
+ * Copyright (C) 2015, 2017 by the Massachusetts Institute of Technology.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -40,10 +40,20 @@
* key; the encrypted message "no attr" is sent if there is no string
* attribute.) It also sets a cookie containing "method-data".
*
- * - It retrieves the "2rt" attribute from the client principal. If set, the
- * verify method sends the client a KDC_ERR_MORE_PREAUTH_DATA_REQUIRED error
- * with the contents of the 2rt attribute as pa-data, and sets a cookie
- * containing "more".
+ * - If the "err" attribute is set on the client principal, the verify method
+ * returns an KDC_ERR_ETYPE_NOSUPP error on the first try, with the contents
+ * of the err attribute as pa-data. If the client tries again with the
+ * padata value "tryagain", the verify method preuthenticates successfully
+ * with no additional processing.
+ *
+ * - If the "failopt" attribute is set on the client principal, the verify
+ * method returns KDC_ERR_PREAUTH_FAILED on optimistic preauth attempts.
+ *
+ * - If the "2rt" attribute is set on client principal, the verify method sends
+ * the client a KDC_ERR_MORE_PREAUTH_DATA_REQUIRED error with the contents of
+ * the 2rt attribute as pa-data, and sets a cookie containing "more". If the
+ * "fail2rt" attribute is set on the client principal, the client's second
+ * try results in a KDC_ERR_PREAUTH_FAILED error.
*
* - It receives a space-separated list from the clpreauth module and asserts
* each string as an authentication indicator. It always succeeds in
@@ -52,6 +62,7 @@
#include "k5-int.h"
#include <krb5/kdcpreauth_plugin.h>
+#include "common.h"
#define TEST_PA_TYPE -123
@@ -73,11 +84,6 @@ test_edata(krb5_context context, krb5_kdc_req *req,
ret = cb->get_string(context, rock, "teststring", &attr);
assert(!ret);
- pa = k5alloc(sizeof(*pa), &ret);
- assert(!ret);
- if (pa == NULL)
- abort();
- pa->pa_type = TEST_PA_TYPE;
if (k != NULL) {
d = string2data((attr != NULL) ? attr : "no attr");
ret = krb5_c_encrypt_length(context, k->enctype, d.length, &enclen);
@@ -86,12 +92,10 @@ test_edata(krb5_context context, krb5_kdc_req *req,
assert(!ret);
ret = krb5_c_encrypt(context, k, 1024, NULL, &d, &enc);
assert(!ret);
- pa->contents = (uint8_t *)enc.ciphertext.data;
- pa->length = enc.ciphertext.length;
+ pa = make_pa(enc.ciphertext.data, enc.ciphertext.length);
+ free(enc.ciphertext.data);
} else {
- pa->contents = (uint8_t *)strdup("no key");
- assert(pa->contents != NULL);
- pa->length = 6;
+ pa = make_pa("no key", 6);
}
/* Exercise setting a cookie information from the edata method. */
@@ -111,12 +115,19 @@ test_verify(krb5_context context, krb5_data *req_pkt, krb5_kdc_req *request,
krb5_kdcpreauth_verify_respond_fn respond, void *arg)
{
krb5_error_code ret;
- krb5_boolean second_round_trip = FALSE;
- krb5_pa_data **list;
+ krb5_boolean second_round_trip = FALSE, optimistic = FALSE;
+ krb5_pa_data **list = NULL;
krb5_data cookie_data, d;
- char *str, *ind, *attr, *toksave = NULL;
+ char *str, *ind, *toksave = NULL;
+ char *attr_err, *attr_2rt, *attr_fail2rt, *attr_failopt;
- ret = cb->get_string(context, rock, "2rt", &attr);
+ ret = cb->get_string(context, rock, "err", &attr_err);
+ assert(!ret);
+ ret = cb->get_string(context, rock, "2rt", &attr_2rt);
+ assert(!ret);
+ ret = cb->get_string(context, rock, "fail2rt", &attr_fail2rt);
+ assert(!ret);
+ ret = cb->get_string(context, rock, "failopt", &attr_failopt);
assert(!ret);
/* Check the incoming cookie value. */
@@ -124,13 +135,36 @@ test_verify(krb5_context context, krb5_data *req_pkt, krb5_kdc_req *request,
/* Make sure we are seeing optimistic preauth and not a lost cookie. */
d = make_data(data->contents, data->length);
assert(data_eq_string(d, "optimistic"));
+ optimistic = TRUE;
} else if (data_eq_string(cookie_data, "more")) {
second_round_trip = TRUE;
} else {
- assert(data_eq_string(cookie_data, "method-data"));
+ assert(data_eq_string(cookie_data, "method-data") ||
+ data_eq_string(cookie_data, "err"));
}
- if (attr == NULL || second_round_trip) {
+ if (attr_err != NULL) {
+ d = make_data(data->contents, data->length);
+ if (data_eq_string(d, "tryagain")) {
+ /* Authenticate successfully. */
+ enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
+ } else {
+ d = string2data("err");
+ ret = cb->set_cookie(context, rock, TEST_PA_TYPE, &d);
+ assert(!ret);
+ ret = KRB5KDC_ERR_ETYPE_NOSUPP;
+ list = make_pa_list(attr_err, strlen(attr_err));
+ }
+ } else if (attr_2rt != NULL && !second_round_trip) {
+ d = string2data("more");
+ ret = cb->set_cookie(context, rock, TEST_PA_TYPE, &d);
+ assert(!ret);
+ ret = KRB5KDC_ERR_MORE_PREAUTH_DATA_REQUIRED;
+ list = make_pa_list(attr_2rt, strlen(attr_2rt));
+ } else if ((attr_fail2rt != NULL && second_round_trip) ||
+ (attr_failopt != NULL && optimistic)) {
+ ret = KRB5KDC_ERR_PREAUTH_FAILED;
+ } else {
/* Parse and assert the indicators. */
str = k5memdup0(data->contents, data->length, &ret);
if (ret)
@@ -142,21 +176,13 @@ test_verify(krb5_context context, krb5_data *req_pkt, krb5_kdc_req *request,
}
free(str);
enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
- cb->free_string(context, rock, attr);
- (*respond)(arg, 0, NULL, NULL, NULL);
- } else {
- d = string2data("more");
- ret = cb->set_cookie(context, rock, TEST_PA_TYPE, &d);
- list = k5calloc(2, sizeof(*list), &ret);
- assert(!ret);
- list[0] = k5alloc(sizeof(*list[0]), &ret);
- assert(!ret);
- list[0]->pa_type = TEST_PA_TYPE;
- list[0]->contents = (uint8_t *)attr;
- list[0]->length = strlen(attr);
- (*respond)(arg, KRB5KDC_ERR_MORE_PREAUTH_DATA_REQUIRED, NULL, list,
- NULL);
}
+
+ cb->free_string(context, rock, attr_err);
+ cb->free_string(context, rock, attr_2rt);
+ cb->free_string(context, rock, attr_fail2rt);
+ cb->free_string(context, rock, attr_failopt);
+ (*respond)(arg, ret, NULL, list, NULL);
}
static krb5_error_code