aboutsummaryrefslogtreecommitdiff
path: root/src/plugins
diff options
context:
space:
mode:
authorKevin Coffman <kwc@citi.umich.edu>2007-08-01 22:09:13 +0000
committerKevin Coffman <kwc@citi.umich.edu>2007-08-01 22:09:13 +0000
commit0ef0646069c1d1376aa632a4791ea7e429f5ae9b (patch)
tree5b9f842dc45a9a14d5698a6f3ff321cea612d7c5 /src/plugins
parent101446c6f40a13917fd0ba020bc276e82590058d (diff)
downloadkrb5-0ef0646069c1d1376aa632a4791ea7e429f5ae9b.zip
krb5-0ef0646069c1d1376aa632a4791ea7e429f5ae9b.tar.gz
krb5-0ef0646069c1d1376aa632a4791ea7e429f5ae9b.tar.bz2
Add PKINIT support
Pull up PKINIT support onto the trunk. Changes from the version in branch users/coffman/pkinit are: - Update the preauth plugin interface version to avoid conflict with any existing plugins. - Add a pkcs11.h locally to the pkinit code rather than depending on opensc being installed. ticket: new Target_Version: 1.6.3 git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@19745 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/preauth/cksum_body/cksum_body_main.c140
-rw-r--r--src/plugins/preauth/pkinit/Makefile.in109
-rw-r--r--src/plugins/preauth/pkinit/README263
-rw-r--r--src/plugins/preauth/pkinit/README.developers18
-rw-r--r--src/plugins/preauth/pkinit/configure.in19
-rw-r--r--src/plugins/preauth/pkinit/pkcs11.h1357
-rw-r--r--src/plugins/preauth/pkinit/pkinit.exports2
-rw-r--r--src/plugins/preauth/pkinit/pkinit.h354
-rw-r--r--src/plugins/preauth/pkinit/pkinit_accessor.c118
-rw-r--r--src/plugins/preauth/pkinit/pkinit_accessor.h83
-rw-r--r--src/plugins/preauth/pkinit/pkinit_clnt.c1492
-rw-r--r--src/plugins/preauth/pkinit/pkinit_crypto.h623
-rw-r--r--src/plugins/preauth/pkinit/pkinit_crypto_openssl.c5612
-rw-r--r--src/plugins/preauth/pkinit/pkinit_crypto_openssl.h265
-rw-r--r--src/plugins/preauth/pkinit/pkinit_identity.c668
-rw-r--r--src/plugins/preauth/pkinit/pkinit_lib.c477
-rw-r--r--src/plugins/preauth/pkinit/pkinit_matching.c830
-rw-r--r--src/plugins/preauth/pkinit/pkinit_profile.c376
-rw-r--r--src/plugins/preauth/pkinit/pkinit_srv.c1399
-rw-r--r--src/plugins/preauth/wpse/wpse_main.c87
20 files changed, 14264 insertions, 28 deletions
diff --git a/src/plugins/preauth/cksum_body/cksum_body_main.c b/src/plugins/preauth/cksum_body/cksum_body_main.c
index 8efda23..66b1620 100644
--- a/src/plugins/preauth/cksum_body/cksum_body_main.c
+++ b/src/plugins/preauth/cksum_body/cksum_body_main.c
@@ -41,7 +41,7 @@
* The AS-REP carries no preauthentication data for this scheme.
*/
-#ident "$Id$"
+#ident "$Id: cksum_body_main.c,v 1.4 2007/01/02 22:33:50 kwc Exp $"
#include "autoconf.h"
@@ -66,6 +66,11 @@ struct server_stats{
int successes, failures;
};
+typedef struct _test_svr_req_ctx {
+ int value1;
+ int value2;
+} test_svr_req_ctx;
+
static int
client_get_flags(krb5_context kcontext, krb5_preauthtype pa_type)
{
@@ -89,9 +94,9 @@ client_process(krb5_context kcontext,
void *gak_data,
krb5_data *salt, krb5_data *s2kparams,
krb5_keyblock *as_key,
- krb5_pa_data **out_pa_data)
+ krb5_pa_data ***out_pa_data)
{
- krb5_pa_data *send_pa;
+ krb5_pa_data **send_pa;
krb5_checksum checksum;
krb5_enctype enctype;
krb5_cksumtype *cksumtypes;
@@ -188,23 +193,31 @@ client_process(krb5_context kcontext,
}
/* Allocate the preauth data structure. */
- send_pa = malloc(sizeof(krb5_pa_data));
+ send_pa = malloc(2 * sizeof(krb5_pa_data *));
if (send_pa == NULL) {
krb5_free_checksum_contents(kcontext, &checksum);
return ENOMEM;
}
- send_pa->pa_type = KRB5_PADATA_CKSUM_BODY_REQ;
- send_pa->length = 4 + checksum.length;
- send_pa->contents = malloc(4 + checksum.length);
- if (send_pa->contents == NULL) {
+ send_pa[1] = NULL; /* Terminate list */
+ send_pa[0] = malloc(sizeof(krb5_pa_data));
+ if (send_pa[0] == NULL) {
+ krb5_free_checksum_contents(kcontext, &checksum);
+ free(send_pa);
+ return ENOMEM;
+ }
+ send_pa[0]->pa_type = KRB5_PADATA_CKSUM_BODY_REQ;
+ send_pa[0]->length = 4 + checksum.length;
+ send_pa[0]->contents = malloc(4 + checksum.length);
+ if (send_pa[0]->contents == NULL) {
krb5_free_checksum_contents(kcontext, &checksum);
+ free(send_pa[0]);
free(send_pa);
return ENOMEM;
}
/* Store the checksum. */
- memcpy(send_pa->contents, &cksumtype, 4);
- memcpy(send_pa->contents + 4, checksum.contents, checksum.length);
+ memcpy(send_pa[0]->contents, &cksumtype, 4);
+ memcpy(send_pa[0]->contents + 4, checksum.contents, checksum.length);
*out_pa_data = send_pa;
/* Clean up. */
@@ -229,7 +242,7 @@ client_gic_opt(krb5_context kcontext,
/* Initialize and tear down the server-side module, and do stat tracking. */
static krb5_error_code
-server_init(krb5_context kcontext, void **module_context)
+server_init(krb5_context kcontext, void **module_context, const char **realmnames)
{
struct server_stats *stats;
stats = malloc(sizeof(struct server_stats));
@@ -324,7 +337,8 @@ server_verify(krb5_context kcontext,
preauth_get_entry_data_proc server_get_entry_data,
void *pa_module_context,
void **pa_request_context,
- krb5_data **e_data)
+ krb5_data **e_data,
+ krb5_authdata ***authz_data)
{
krb5_int32 cksumtype;
krb5_checksum checksum;
@@ -338,9 +352,14 @@ server_verify(krb5_context kcontext,
krb5_error_code status;
struct server_stats *stats;
krb5_data *test_edata;
+ test_svr_req_ctx *svr_req_ctx;
+ krb5_authdata **my_authz_data = NULL;
stats = pa_module_context;
+#ifdef DEBUG
+ fprintf(stderr, "cksum_body: server_verify\n");
+#endif
/* Verify the preauth data. Start with the checksum type. */
if (data->length < 4) {
stats->failures++;
@@ -477,6 +496,54 @@ server_verify(krb5_context kcontext,
return KRB5KDC_ERR_PREAUTH_FAILED;
}
+ /*
+ * Return some junk authorization data just to exercise the
+ * code path handling the returned authorization data.
+ *
+ * NOTE that this is NOT VALID authorization data!
+ */
+#ifdef DEBUG
+ fprintf(stderr, "cksum_body: doing authorization data!\n");
+#endif
+#if 1 /* USE_5000_AD */
+#define AD_ALLOC_SIZE 5000
+ /* ad_header consists of a sequence tag (0x30) and length (0x82 0x1384)
+ * followed by octet string tag (0x04) and length (0x82 0x1380) */
+ krb5_octet ad_header[] = {0x30, 0x82, 0x13, 0x84, 0x04, 0x82, 0x13, 0x80};
+#else
+#define AD_ALLOC_SIZE 100
+ /* ad_header consists of a sequence tag (0x30) and length (0x62)
+ * followed by octet string tag (0x04) and length (0x60) */
+ krb5_octet ad_header[] = {0x30, 0x62, 0x04, 0x60};
+#endif
+ my_authz_data = malloc(2 * sizeof(*my_authz_data));
+ if (my_authz_data != NULL) {
+ my_authz_data[1] = NULL;
+ my_authz_data[0] = malloc(sizeof(krb5_authdata));
+ if (my_authz_data[0] == NULL) {
+ free(my_authz_data);
+ return ENOMEM;
+ }
+ my_authz_data[0]->contents = malloc(AD_ALLOC_SIZE);
+ if (my_authz_data[0]->contents == NULL) {
+ free(my_authz_data[0]);
+ free(my_authz_data);
+ return ENOMEM;
+ }
+ memset(my_authz_data[0]->contents, '\0', AD_ALLOC_SIZE);
+ my_authz_data[0]->magic = KV5M_AUTHDATA;
+ my_authz_data[0]->ad_type = 1;
+ my_authz_data[0]->length = AD_ALLOC_SIZE;
+ memcpy(my_authz_data[0]->contents, ad_header, sizeof(ad_header));
+ sprintf(my_authz_data[0]->contents + sizeof(ad_header),
+ "cksum authorization data: %d bytes worth!\n", AD_ALLOC_SIZE);
+ *authz_data = my_authz_data;
+#ifdef DEBUG
+ fprintf(stderr, "Returning %d bytes of authorization data\n",
+ AD_ALLOC_SIZE);
+#endif
+ }
+
/* Return edata to exercise code that handles edata... */
test_edata = malloc(sizeof(*test_edata));
if (test_edata != NULL) {
@@ -490,6 +557,18 @@ server_verify(krb5_context kcontext,
}
}
+ /* Return a request context to exercise code that handles it */
+ svr_req_ctx = malloc(sizeof(*svr_req_ctx));
+ if (svr_req_ctx != NULL) {
+ svr_req_ctx->value1 = 111111;
+ svr_req_ctx->value2 = 222222;
+#ifdef DEBUG
+ fprintf(stderr, "server_verify: returning context at %p\n",
+ svr_req_ctx);
+#endif
+ }
+ *pa_request_context = svr_req_ctx;
+
/* Note that preauthentication succeeded. */
enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
stats->successes++;
@@ -516,6 +595,37 @@ server_return(krb5_context kcontext,
return 0;
}
+/* Test server request context freeing */
+static krb5_error_code
+server_free_reqctx(krb5_context kcontext,
+ void *pa_module_context,
+ void **pa_request_context)
+{
+ test_svr_req_ctx *svr_req_ctx;
+#ifdef DEBUG
+ fprintf(stderr, "server_free_reqctx: entered!\n");
+#endif
+ if (pa_request_context == NULL)
+ return 0;
+
+ svr_req_ctx = *pa_request_context;
+ if (svr_req_ctx == NULL)
+ return 0;
+
+ if (svr_req_ctx->value1 != 111111 || svr_req_ctx->value2 != 222222) {
+ fprintf(stderr, "server_free_reqctx: got invalid req context "
+ "at %p with values %d and %d\n",
+ svr_req_ctx, svr_req_ctx->value1, svr_req_ctx->value2);
+ return EINVAL;
+ }
+#ifdef DEBUG
+ fprintf(stderr, "server_free_reqctx: freeing context at %p\n", svr_req_ctx);
+#endif
+ free(svr_req_ctx);
+ *pa_request_context = NULL;
+ return 0;
+}
+
static int
server_get_flags(krb5_context kcontext, krb5_preauthtype pa_type)
{
@@ -529,7 +639,7 @@ static krb5_preauthtype supported_server_pa_types[] = {
KRB5_PADATA_CKSUM_BODY_REQ, 0,
};
-struct krb5plugin_preauth_client_ftable_v0 preauthentication_client_0 = {
+struct krb5plugin_preauth_client_ftable_v1 preauthentication_client_1 = {
"cksum_body", /* name */
&supported_client_pa_types[0], /* pa_type_list */
NULL, /* enctype_list */
@@ -543,7 +653,7 @@ struct krb5plugin_preauth_client_ftable_v0 preauthentication_client_0 = {
client_gic_opt /* get init creds opt function */
};
-struct krb5plugin_preauth_server_ftable_v0 preauthentication_server_0 = {
+struct krb5plugin_preauth_server_ftable_v1 preauthentication_server_1 = {
"cksum_body",
&supported_server_pa_types[0],
server_init,
@@ -552,5 +662,5 @@ struct krb5plugin_preauth_server_ftable_v0 preauthentication_server_0 = {
server_get_edata,
server_verify,
server_return,
- NULL
+ server_free_reqctx
};
diff --git a/src/plugins/preauth/pkinit/Makefile.in b/src/plugins/preauth/pkinit/Makefile.in
new file mode 100644
index 0000000..7542e3c
--- /dev/null
+++ b/src/plugins/preauth/pkinit/Makefile.in
@@ -0,0 +1,109 @@
+thisconfigdir=../../..
+myfulldir=plugins/preauth/pkinit
+mydir=plugins/preauth/pkinit
+BUILDTOP=$(REL)..$(S)..$(S)..
+KRB5_RUN_ENV = @KRB5_RUN_ENV@
+KRB5_CONFIG_SETUP = KRB5_CONFIG=$(SRCTOP)/config-files/krb5.conf ; export KRB5_CONFIG ;
+PROG_LIBPATH=-L$(TOPLIBD)
+PROG_RPATH=$(KRB5_LIBDIR)
+MODULE_INSTALL_DIR = $(KRB5_PA_MODULE_DIR)
+DEFS=@DEFS@
+
+LOCALINCLUDES = -I../../../include/krb5 -I.
+
+LIBBASE=pkinit
+LIBMAJOR=0
+LIBMINOR=0
+SO_EXT=.so
+RELDIR=../plugins/preauth/pkinit
+# Depends on libk5crypto and libkrb5
+SHLIB_EXPDEPS = \
+ $(TOPLIBD)/libk5crypto$(SHLIBEXT) \
+ $(TOPLIBD)/libkrb5$(SHLIBEXT)
+LIBS+= -lcrypto
+SHLIB_EXPLIBS= -lkrb5 -lcom_err -lk5crypto -ldl $(SUPPORT_LIB) $(LIBS)
+
+SHLIB_DIRS=-L$(TOPLIBD)
+SHLIB_RDIRS=$(KRB5_LIBDIR)
+STOBJLISTS=OBJS.ST
+STLIBOBJS= \
+ pkinit_accessor.o \
+ pkinit_srv.o \
+ pkinit_lib.o \
+ pkinit_clnt.o \
+ pkinit_profile.o \
+ pkinit_identity.o \
+ pkinit_matching.o \
+ pkinit_crypto_openssl.o
+
+SRCS= \
+ $(srcdir)/pkinit_accessor.c \
+ $(srcdir)/pkinit_srv.c \
+ $(srcdir)/pkinit_lib.c \
+ $(srcdir)/pkinit_clnt.c \
+ $(srcdir)/pkinit_profile.c \
+ $(srcdir)/pkinit_identity.c \
+ $(srcdir)/pkinit_matching.c \
+ $(srcdir)/pkinit_crypto_openssl.c
+
+all-unix:: $(LIBBASE)$(SO_EXT)
+install-unix:: install-libs
+clean-unix:: clean-libs clean-libobjs
+
+clean::
+ $(RM) lib$(LIBBASE)$(SO_EXT)
+
+@libnover_frag@
+@libobj_frag@
+
+# +++ Dependency line eater +++
+#
+# Makefile dependencies follow. This must be the last section in
+# the Makefile.in file
+#
+pkinit_accessor.so pkinit_accessor.po $(OUTPRE)pkinit_accessor.$(OBJEXT): \
+ $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
+ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
+ $(COM_ERR_DEPS) $(SRCTOP)/include/k5-err.h $(SRCTOP)/include/k5-int-pkinit.h \
+ $(SRCTOP)/include/k5-int.h $(SRCTOP)/include/k5-platform.h \
+ $(SRCTOP)/include/k5-plugin.h $(SRCTOP)/include/k5-thread.h \
+ $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
+ $(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \
+ $(SRCTOP)/include/socket-utils.h pkinit_accessor.c \
+ pkinit_accessor.h
+pkinit_srv.so pkinit_srv.po $(OUTPRE)pkinit_srv.$(OBJEXT): \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/profile.h \
+ $(COM_ERR_DEPS) $(SRCTOP)/include/k5-int-pkinit.h $(SRCTOP)/include/krb5/preauth_plugin.h \
+ pkinit.h pkinit_accessor.h pkinit_crypto.h pkinit_srv.c
+pkinit_lib.so pkinit_lib.po $(OUTPRE)pkinit_lib.$(OBJEXT): \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/profile.h \
+ $(COM_ERR_DEPS) $(SRCTOP)/include/k5-int-pkinit.h $(SRCTOP)/include/krb5/preauth_plugin.h \
+ pkinit.h pkinit_accessor.h pkinit_crypto.h pkinit_lib.c
+pkinit_clnt.so pkinit_clnt.po $(OUTPRE)pkinit_clnt.$(OBJEXT): \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/profile.h \
+ $(COM_ERR_DEPS) $(SRCTOP)/include/k5-int-pkinit.h $(SRCTOP)/include/krb5/preauth_plugin.h \
+ pkinit.h pkinit_accessor.h pkinit_clnt.c pkinit_crypto.h
+pkinit_profile.so pkinit_profile.po $(OUTPRE)pkinit_profile.$(OBJEXT): \
+ $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
+ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
+ $(COM_ERR_DEPS) $(SRCTOP)/include/k5-err.h $(SRCTOP)/include/k5-int-pkinit.h \
+ $(SRCTOP)/include/k5-int.h $(SRCTOP)/include/k5-platform.h \
+ $(SRCTOP)/include/k5-plugin.h $(SRCTOP)/include/k5-thread.h \
+ $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
+ $(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \
+ $(SRCTOP)/include/socket-utils.h pkinit.h pkinit_accessor.h \
+ pkinit_crypto.h pkinit_profile.c
+pkinit_identity.so pkinit_identity.po $(OUTPRE)pkinit_identity.$(OBJEXT): \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/profile.h \
+ $(COM_ERR_DEPS) $(SRCTOP)/include/k5-int-pkinit.h $(SRCTOP)/include/krb5/preauth_plugin.h \
+ pkinit.h pkinit_accessor.h pkinit_crypto.h pkinit_identity.c
+pkinit_matching.so pkinit_matching.po $(OUTPRE)pkinit_matching.$(OBJEXT): \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/profile.h \
+ $(COM_ERR_DEPS) $(SRCTOP)/include/k5-int-pkinit.h $(SRCTOP)/include/krb5.h \
+ $(SRCTOP)/include/krb5/preauth_plugin.h pkinit.h pkinit_accessor.h \
+ pkinit_crypto.h pkinit_matching.c
+pkinit_crypto_openssl.so pkinit_crypto_openssl.po $(OUTPRE)pkinit_crypto_openssl.$(OBJEXT): \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/profile.h \
+ $(COM_ERR_DEPS) $(SRCTOP)/include/k5-int-pkinit.h $(SRCTOP)/include/krb5/preauth_plugin.h \
+ pkinit.h pkinit_accessor.h pkinit_crypto.h pkinit_crypto_openssl.c \
+ pkinit_crypto_openssl.h
diff --git a/src/plugins/preauth/pkinit/README b/src/plugins/preauth/pkinit/README
new file mode 100644
index 0000000..90522a7
--- /dev/null
+++ b/src/plugins/preauth/pkinit/README
@@ -0,0 +1,263 @@
+Building with pkinit enabled:
+=============================
+To build the code you will need OpenSSL headers. To build
+with smartcard support, OpenSC is required. The code can
+currently be built w/o smartcard support by defining
+WITHOUT_PKCS11 in the CFLAGS.
+
+Although you will need OpenSC to build the code, at run time you just need a
+pkcs11 module. It may be possible to build without having OpenSC installed
+by changing the path to the pkcs11 include file and the name of the default
+module. We have not tried this.
+
+Running with pkinit enabled:
+=============================
+In the following descriptions, options specified with "DIR:"
+are assumed to point to an OpenSSL-style hashed CA directory
+where each CA cert is stored in a file "<hash of CA cert>.0".
+We assume that a CRL directory (pkinit_revoke) will contain CRL files
+"<hash of CA cert>.r0". We encourage that users have such
+infrastructure but we will also allow files that contain
+certificates (or crls) in that directory that are not named
+in this style. Basically, for a given directory, the code
+will read each file in the directory and if the file contains
+a certificate (or crl), we will include it.
+
+Configuration for a pkinit-enabled KDC:
+---------------------------------------
+The following pkinit-specific configuration parameters can be specified
+in kdc.conf in either the [kdcdefaults] stanza or the appropriate
+realm entry:
+
+"pkinit_identity"
+ Specifies where to find the KDC's certificate and private key.
+ It is REQUIRED, and can be defined only once. It takes the form:
+
+ FILE:<cert-file-name>[,<key-file-name>]
+
+ If <key-file-name> is not specified, the private key is assumed
+ to be located in the same file with the certificate.
+
+"pkinit_anchors"
+ Specifies where the KDC will find trusted root CA certficates
+ to verify client certificates it receives. This parameter is
+ REQUIRED and may be specified multiple times. It can take the
+ forms:
+
+ FILE:<ca-bundle-file-name>
+ DIR:<openssl-style-hashed-directory>
+
+"pkinit_pool"
+ Specifies where the KDC will find other (intermediate) CA
+ certificates it can use to build trust paths to the trusted
+ roots defined in "pkinit_anchors", while verifying client
+ certificates. This parameter is OPTIONAL and may be specified
+ multiple times. It can take the same forms as "pkinit_anchors".
+
+"pkinit_revoke"
+ Specifies where CRL information can be found to be used when
+ verifying client certificates. This parameter is OPTIONAL and
+ may by specified multiple times. It can take the same forms
+ as "pkinit_anchors". XXX???XXX
+
+"pkinit_dh_min_bits"
+ Specifies the minimum number of bits the KDC is willing to accept
+ for a client's Diffie-Hellman key. This is an OPTIONAL integer
+ value. The default is 1024.
+
+
+Configuration for pkinit-enabled client:
+----------------------------------------
+The following pkinit-specific configuration parameters can be specified
+in krb5.conf in the [libdefaults] stanza. They may be specified as
+"global" libdefaults or specified by realm within the [libdefaults]
+stanza.
+
+i.e.
+ [libdefaults]
+ pkinit_anchors = DIR:/etc/ssl/trusted-anchors
+ pkinit_win2k = false
+
+ EXAMPLE.COM = {
+ pkinit_win2k = true
+ pkinit_win2k_require_binding = true
+ pkinit_anchors = FILE:/etc/ssl/example.com.cas
+ }
+
+"pkinit_identity"
+ See the discussion for this option in the KDC description above.
+ For the client, this is OPTIONAL. The client can use either
+ of the following to specify its certificate and private key
+ locations:
+
+ FILE:<cert-file-name>[,<key-file-name>]
+
+ PKCS11:[module_name=]<module-name>[:slotid=<slot-id>]
+ [:token=<token-label>][:certid=<cert-id>]
+ [:certlabel=<cert-label>]
+
+"pkinit_anchors"
+ See the discussion for this option in the KDC description above.
+ For the client, this option is considered OPTIONAL, however it
+ is needed in practice to verify a KDC's certificate.
+
+"pkinit_pool"
+ See the discussion for these options in the KDC descriptions above.
+
+"pkinit_revoke"
+ See the discussion for these options in the KDC descriptions above.
+
+"pkinit_win2k"
+ This BOOLEAN option specifies whether the target realm is assumed
+ to support only the "old" version of the protocol. The default
+ is false.
+
+"pkinit_win2k_require_binding"
+ If this BOOLEAN option is set to true, it expects that the target
+ KDC is patched to return a reply with a checksum rather than a
+ nonce. The default is false.
+
+"pkinit_require_eku"
+ This BOOLEAN specifies whether the client insists that the KDC
+ certificate contain the appropriate Extended Key Usage value.
+ The default is true.
+
+"pkinit_require_krbtgt_otherName"
+ This BOOLEAN specifies whether the client insists that the KDC
+ certificate contain the appropriate SubjectAlternativeName
+ value. The default is true.
+
+"pkinit_require_hostname_match"
+ This BOOLEAN specifies whether the client insists that the
+ hostname within the SubjectAlternativeName must match the
+ hostname the client believes it is talking to. The default
+ is false.
+
+
+Command-line options for kinit:
+-------------------------------
+Command-line options may be passed via the -X option to kinit.
+The following values are currently accepted:
+
+X509_user_identity=<value>
+ Where <value> has the same format as the "pkinit_identity"
+ config option described above.
+
+X509_anchors=<value>
+ Where <value> has the same format as the "pkinit_anchors"
+ config option described above.
+
+flag_RSA_PROTOCOL
+ This boolean specifies that the RSA protocol (rather than
+ the Diffie-Hellman protocol) should be used while authenticating
+ with the KDC.
+
+
+For a pkinit-enabled client using smartcards:
+---------------------------------------------
+1. Install a pkcs11 module (smartcard enabled or otherwise).
+ We use OpenSC but any module that implements pkcs11 should work.
+ There are some instructions for setting up OpenSC at
+ http://www.citi.umich.edu/projects/pkinit/smartcard_setup.html
+2. set the location of your trusted cas either by setting the
+ appropriate configuration file parameter or by specifying
+ "-X X509_anchors=<value>" on the command line.
+3. Select a pkcs11 module. The default is opensc-pkcs11.so. To use a
+ different one, use the "module_name=" option (see below).
+4. Select a pkcs11 slot/token. If you only have one with a token available,
+ that one will be used. Otherwise use the "slotid=" option. Or you can
+ specify a token label with the "token=" option and pkinit will attempt to
+ locate that token.
+5. Select a client certificate to use. You can specify a certificate ID
+ with "certid=" or a certificate label with "certlabel=". If multiple
+ certs fit the selection criteria, the first one with the appropriate
+ capabilities will be used.
+6. Options can be set either via the "pkcs11_identity" krb5.conf
+ parameter or via the -X parameter to kinit.
+ These two examples should produce the same result:
+
+ pkinit_identity=PKCS11:module_name=/usr/local/VendorX/lib/libpkcs11.so:slotid=1:certid=4
+
+ kinit -X X509_user_identity=PKCS11:module_name=/usr/local/VendorX/lib/libpkcs11.so:slotid=1:certid=4
+
+
+For a pkinit-enabled client using the filesystem for Cert and Key:
+------------------------------------------------------------------
+1. verify that the proper krb5.conf options are set
+2. Specify the certificate and private key locations either via the
+ pkinit_identity config option, or via the "-X X509_user_identity="
+ command line option.
+ These two examples should produce the same result:
+
+ pkinit_identity=FILE:/home/bubba/ssl/foo.crt,/home/bubba/ssl/foo.key
+
+ kinit -X X509_user_identity=FILE:/home/bubba/ssl/foo.crt,/home/bubba/ssl/foo.key
+
+
+Testing
+=======
+We have tested the client code against our server, Heimdal server,
+and Windows 2003 servers, but not yet against a Vista/Longhorn server.
+
+We have not yet been able to test a Windows client against our server.
+
+Most of our smartcard testing has been with Cryptoflex.
+
+Known Issues
+============
+- The client principal must currently have the REQUIRES_PREAUTH attribute
+ set to cause the use of pkinit
+- Some error reporting is incomplete
+
+
+Information about using CRLs
+============================
+N.B. The following describes a "pkinit_require_crl_checking" option
+which currently doesn't actually exist. The current behavior is
+as if this option were always set to false.
+
+We assume that the user will acquire needed CRLs and place them
+in a local directory (using OpenSSL style) or a single file. During
+verification of a certificate for each CA, the code looks for a CRL
+issued by that CA. If a match is found for the certificate in a CRL,
+verification fails. If the certificate being verified is not listed
+in a CRL, or there is no CRL present for its issuing CA, and
+pkinit_require_crl_checking is false, then verification succeeds.
+However, if pkinit_require_crl_checking is true and there is no
+CRL for the issuing CA, then verification fails.
+
+Basically, pkinit_require_crl_checking should be set to true if the
+policy is such that up-to-date CRLs must be present for every CA.
+
+
+===============
+NOT IMPLEMENTED
+===============
+The following KDC options allowed by Heimdal are NOT CURRENTLY IMPLEMENTED:
+
+"pkinit_kdc_ocsp"
+ Specifies where the KDC will find information to use the Online
+ Certificate Status Protocol while verifying client certificates.
+ This parameter is OPTIONAL and there is no default.
+
+"pkinit_mappings_file"
+ Specifies where the KDC will find information to use to map the
+ DN information in a certificate to a KDC principal. This would
+ be used when the client certificate does not contain the pkinit-SAN
+ information defined by RFC 4556.
+ This parameter is OPTIONAL and there is no default.
+
+"pkinit_allow_proxy_certificate"
+ Specifies whether the KDC should accept proxy client certificates
+ for authentication. This is an OPTIONAL boolean parameter. The
+ default is false.
+
+"pkinit_principal_in_certificate"
+ Specifies whether the KDC should obtain the client principal name
+ from the SubjectAlternativeName in the certificate presented
+ for authentication. This is an OPTIONAL boolean parameter. The
+ default is true.
+
+pkinit_identity=PKCS11:[module_name=]<module-name>[:slotid=<slot-id>]
+ [:token=<token-label>][:certid=<cert-id>]
+ [:certlabel=<cert-label>]
diff --git a/src/plugins/preauth/pkinit/README.developers b/src/plugins/preauth/pkinit/README.developers
new file mode 100644
index 0000000..e095327
--- /dev/null
+++ b/src/plugins/preauth/pkinit/README.developers
@@ -0,0 +1,18 @@
+Experimental features:
+1. If you want trustedCertifiers to be sent by the client, then set
+X509_CA_BUNDLE to a ca-bundle file.
+2. If you want to make our KDC act like a draft9 KDC, then modify pkinit_src.c
+file. there is an "#if 0" for "supported_server_pa_types". if you change "if 0"
+to "if 1", then the kdc will become draft9-only KDC.
+3. If you like more debugging output, add "-DDEBUG" to CFLAGS and recompile
+the code.
+4. If you are debugging ASN1 encoding, add "-DDEBUG_ASN1" to CFLAGS and
+recompile the code. After running, you'll get DER encoded structures stored
+in /tmp. For example, /tmp/client_as_req will contains DER encoding of the
+pkinit part of the AS-REQ.
+5. Prior to having config options that manage EKU/SAN/CRL checking, you can
+modify pkinit_lib.c in function pkinit_lib_init(), set
+ plgctx->require_eku = 1 -- will require presence of EKU in certs
+ plgctx->require_san = 1 -- will require presence of SAN in KDC's cert
+ plgctx->require_crl_checking = 1 -- will require presence of CRLs to
+ verify every certificate
diff --git a/src/plugins/preauth/pkinit/configure.in b/src/plugins/preauth/pkinit/configure.in
new file mode 100644
index 0000000..5819289
--- /dev/null
+++ b/src/plugins/preauth/pkinit/configure.in
@@ -0,0 +1,19 @@
+K5_AC_INIT(configure.in)
+enable_shared=yes
+build_dynobj=yes
+CONFIG_RULES
+AC_CHECK_HEADERS(unistd.h)
+AC_TYPE_MODE_T
+AC_TYPE_OFF_T
+
+AC_CHECK_FUNCS()
+
+# XXX This is incorrect, but should cause -lcrypto to be included by default
+AC_CHECK_LIB(crypto, PKCS7_get_signer_info)
+
+KRB5_RUN_FLAGS
+dnl The following is for check...
+KRB5_BUILD_PROGRAM
+KRB5_BUILD_LIBOBJS
+KRB5_BUILD_LIBRARY
+V5_AC_OUTPUT_MAKEFILE
diff --git a/src/plugins/preauth/pkinit/pkcs11.h b/src/plugins/preauth/pkinit/pkcs11.h
new file mode 100644
index 0000000..7d18f23
--- /dev/null
+++ b/src/plugins/preauth/pkinit/pkcs11.h
@@ -0,0 +1,1357 @@
+/* pkcs11.h
+ Copyright 2006 g10 Code GmbH
+ Copyright 2006 Andreas Jellinghaus
+
+ This file is free software; as a special exception the author gives
+ unlimited permission to copy and/or distribute it, with or without
+ modifications, as long as this notice is preserved.
+
+ This file is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY, to the extent permitted by law; without even
+ the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE. */
+
+/* Please submit changes back to the Scute project at
+ http://www.scute.org/ (or send them to marcus@g10code.com), so that
+ they can be picked up by other projects from there as well. */
+
+/* This file is a modified implementation of the PKCS #11 standard by
+ RSA Security Inc. It is mostly a drop-in replacement, with the
+ following change:
+
+ This header file does not require any macro definitions by the user
+ (like CK_DEFINE_FUNCTION etc). In fact, it defines those macros
+ for you (if useful, some are missing, let me know if you need
+ more).
+
+ There is an additional API available that does comply better to the
+ GNU coding standard. It can be switched on by defining
+ CRYPTOKI_GNU before including this header file. For this, the
+ following changes are made to the specification:
+
+ All structure types are changed to a "struct ck_foo" where CK_FOO
+ is the type name in PKCS #11.
+
+ All non-structure types are changed to ck_foo_t where CK_FOO is the
+ lowercase version of the type name in PKCS #11. The basic types
+ (CK_ULONG et al.) are removed without substitute.
+
+ All members of structures are modified in the following way: Type
+ indication prefixes are removed, and underscore characters are
+ inserted before words. Then the result is lowercased.
+
+ Note that function names are still in the original case, as they
+ need for ABI compatibility.
+
+ CK_FALSE, CK_TRUE and NULL_PTR are removed without substitute. Use
+ <stdbool.h>.
+
+ If CRYPTOKI_COMPAT is defined before including this header file,
+ then none of the API changes above take place, and the API is the
+ one defined by the PKCS #11 standard. */
+
+#ifndef PKCS11_H
+#define PKCS11_H 1
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* The version of cryptoki we implement. The revision is changed with
+ each modification of this file. If you do not use the "official"
+ version of this file, please consider deleting the revision macro
+ (you may use a macro with a different name to keep track of your
+ versions). */
+#define CRYPTOKI_VERSION_MAJOR 2
+#define CRYPTOKI_VERSION_MINOR 20
+#define CRYPTOKI_VERSION_REVISION 6
+
+
+/* Compatibility interface is default, unless CRYPTOKI_GNU is
+ given. */
+#ifndef CRYPTOKI_GNU
+#ifndef CRYPTOKI_COMPAT
+#define CRYPTOKI_COMPAT 1
+#endif
+#endif
+
+/* System dependencies. */
+
+#if defined(_WIN32) || defined(CRYPTOKI_FORCE_WIN32)
+
+/* There is a matching pop below. */
+#pragma pack(push, cryptoki, 1)
+
+#ifdef CRYPTOKI_EXPORTS
+#define CK_SPEC __declspec(dllexport)
+#else
+#define CK_SPEC __declspec(dllimport)
+#endif
+
+#else
+
+#define CK_SPEC
+
+#endif
+
+
+#ifdef CRYPTOKI_COMPAT
+ /* If we are in compatibility mode, switch all exposed names to the
+ PKCS #11 variant. There are corresponding #undefs below. */
+
+#define ck_flags_t CK_FLAGS
+#define ck_version _CK_VERSION
+
+#define ck_info _CK_INFO
+#define cryptoki_version cryptokiVersion
+#define manufacturer_id manufacturerID
+#define library_description libraryDescription
+#define library_version libraryVersion
+
+#define ck_notification_t CK_NOTIFICATION
+#define ck_slot_id_t CK_SLOT_ID
+
+#define ck_slot_info _CK_SLOT_INFO
+#define slot_description slotDescription
+#define hardware_version hardwareVersion
+#define firmware_version firmwareVersion
+
+#define ck_token_info _CK_TOKEN_INFO
+#define serial_number serialNumber
+#define max_session_count ulMaxSessionCount
+#define session_count ulSessionCount
+#define max_rw_session_count ulMaxRwSessionCount
+#define rw_session_count ulRwSessionCount
+#define max_pin_len ulMaxPinLen
+#define min_pin_len ulMinPinLen
+#define total_public_memory ulTotalPublicMemory
+#define free_public_memory ulFreePublicMemory
+#define total_private_memory ulTotalPrivateMemory
+#define free_private_memory ulFreePrivateMemory
+#define utc_time utcTime
+
+#define ck_session_handle_t CK_SESSION_HANDLE
+#define ck_user_type_t CK_USER_TYPE
+#define ck_state_t CK_STATE
+
+#define ck_session_info _CK_SESSION_INFO
+#define slot_id slotID
+#define device_error ulDeviceError
+
+#define ck_object_handle_t CK_OBJECT_HANDLE
+#define ck_object_class_t CK_OBJECT_CLASS
+#define ck_hw_feature_type_t CK_HW_FEATURE_TYPE
+#define ck_key_type_t CK_KEY_TYPE
+#define ck_certificate_type_t CK_CERTIFICATE_TYPE
+#define ck_attribute_type_t CK_ATTRIBUTE_TYPE
+
+#define ck_attribute _CK_ATTRIBUTE
+#define value pValue
+#define value_len ulValueLen
+
+#define ck_date _CK_DATE
+
+#define ck_mechanism_type_t CK_MECHANISM_TYPE
+
+#define ck_mechanism _CK_MECHANISM
+#define parameter pParameter
+#define parameter_len ulParameterLen
+
+#define ck_mechanism_info _CK_MECHANISM_INFO
+#define min_key_size ulMinKeySize
+#define max_key_size ulMaxKeySize
+
+#define ck_rv_t CK_RV
+#define ck_notify_t CK_NOTIFY
+
+#define ck_function_list _CK_FUNCTION_LIST
+
+#define ck_createmutex_t CK_CREATEMUTEX
+#define ck_destroymutex_t CK_DESTROYMUTEX
+#define ck_lockmutex_t CK_LOCKMUTEX
+#define ck_unlockmutex_t CK_UNLOCKMUTEX
+
+#define ck_c_initialize_args _CK_C_INITIALIZE_ARGS
+#define create_mutex CreateMutex
+#define destroy_mutex DestroyMutex
+#define lock_mutex LockMutex
+#define unlock_mutex UnlockMutex
+#define reserved pReserved
+
+#endif /* CRYPTOKI_COMPAT */
+
+
+
+typedef unsigned long ck_flags_t;
+
+struct ck_version
+{
+ unsigned char major;
+ unsigned char minor;
+};
+
+
+struct ck_info
+{
+ struct ck_version cryptoki_version;
+ unsigned char manufacturer_id[32];
+ ck_flags_t flags;
+ unsigned char library_description[32];
+ struct ck_version library_version;
+};
+
+
+typedef unsigned long ck_notification_t;
+
+#define CKN_SURRENDER (0)
+
+
+typedef unsigned long ck_slot_id_t;
+
+
+struct ck_slot_info
+{
+ unsigned char slot_description[64];
+ unsigned char manufacturer_id[32];
+ ck_flags_t flags;
+ struct ck_version hardware_version;
+ struct ck_version firmware_version;
+};
+
+
+#define CKF_TOKEN_PRESENT (1 << 0)
+#define CKF_REMOVABLE_DEVICE (1 << 1)
+#define CKF_HW_SLOT (1 << 2)
+#define CKF_ARRAY_ATTRIBUTE (1 << 30)
+
+
+struct ck_token_info
+{
+ unsigned char label[32];
+ unsigned char manufacturer_id[32];
+ unsigned char model[16];
+ unsigned char serial_number[16];
+ ck_flags_t flags;
+ unsigned long max_session_count;
+ unsigned long session_count;
+ unsigned long max_rw_session_count;
+ unsigned long rw_session_count;
+ unsigned long max_pin_len;
+ unsigned long min_pin_len;
+ unsigned long total_public_memory;
+ unsigned long free_public_memory;
+ unsigned long total_private_memory;
+ unsigned long free_private_memory;
+ struct ck_version hardware_version;
+ struct ck_version firmware_version;
+ unsigned char utc_time[16];
+};
+
+
+#define CKF_RNG (1 << 0)
+#define CKF_WRITE_PROTECTED (1 << 1)
+#define CKF_LOGIN_REQUIRED (1 << 2)
+#define CKF_USER_PIN_INITIALIZED (1 << 3)
+#define CKF_RESTORE_KEY_NOT_NEEDED (1 << 5)
+#define CKF_CLOCK_ON_TOKEN (1 << 6)
+#define CKF_PROTECTED_AUTHENTICATION_PATH (1 << 8)
+#define CKF_DUAL_CRYPTO_OPERATIONS (1 << 9)
+#define CKF_TOKEN_INITIALIZED (1 << 10)
+#define CKF_SECONDARY_AUTHENTICATION (1 << 11)
+#define CKF_USER_PIN_COUNT_LOW (1 << 16)
+#define CKF_USER_PIN_FINAL_TRY (1 << 17)
+#define CKF_USER_PIN_LOCKED (1 << 18)
+#define CKF_USER_PIN_TO_BE_CHANGED (1 << 19)
+#define CKF_SO_PIN_COUNT_LOW (1 << 20)
+#define CKF_SO_PIN_FINAL_TRY (1 << 21)
+#define CKF_SO_PIN_LOCKED (1 << 22)
+#define CKF_SO_PIN_TO_BE_CHANGED (1 << 23)
+
+#define CK_UNAVAILABLE_INFORMATION ((unsigned long) -1)
+#define CK_EFFECTIVELY_INFINITE (0)
+
+
+typedef unsigned long ck_session_handle_t;
+
+#define CK_INVALID_HANDLE (0)
+
+
+typedef unsigned long ck_user_type_t;
+
+#define CKU_SO (0)
+#define CKU_USER (1)
+#define CKU_CONTEXT_SPECIFIC (2)
+
+
+typedef unsigned long ck_state_t;
+
+#define CKS_RO_PUBLIC_SESSION (0)
+#define CKS_RO_USER_FUNCTIONS (1)
+#define CKS_RW_PUBLIC_SESSION (2)
+#define CKS_RW_USER_FUNCTIONS (3)
+#define CKS_RW_SO_FUNCTIONS (4)
+
+
+struct ck_session_info
+{
+ ck_slot_id_t slot_id;
+ ck_state_t state;
+ ck_flags_t flags;
+ unsigned long device_error;
+};
+
+#define CKF_RW_SESSION (1 << 1)
+#define CKF_SERIAL_SESSION (1 << 2)
+
+
+typedef unsigned long ck_object_handle_t;
+
+
+typedef unsigned long ck_object_class_t;
+
+#define CKO_DATA (0)
+#define CKO_CERTIFICATE (1)
+#define CKO_PUBLIC_KEY (2)
+#define CKO_PRIVATE_KEY (3)
+#define CKO_SECRET_KEY (4)
+#define CKO_HW_FEATURE (5)
+#define CKO_DOMAIN_PARAMETERS (6)
+#define CKO_MECHANISM (7)
+#define CKO_VENDOR_DEFINED ((unsigned long) (1 << 31))
+
+
+typedef unsigned long ck_hw_feature_type_t;
+
+#define CKH_MONOTONIC_COUNTER (1)
+#define CKH_CLOCK (2)
+#define CKH_USER_INTERFACE (3)
+#define CKH_VENDOR_DEFINED ((unsigned long) (1 << 31))
+
+
+typedef unsigned long ck_key_type_t;
+
+#define CKK_RSA (0)
+#define CKK_DSA (1)
+#define CKK_DH (2)
+#define CKK_ECDSA (3)
+#define CKK_EC (3)
+#define CKK_X9_42_DH (4)
+#define CKK_KEA (5)
+#define CKK_GENERIC_SECRET (0x10)
+#define CKK_RC2 (0x11)
+#define CKK_RC4 (0x12)
+#define CKK_DES (0x13)
+#define CKK_DES2 (0x14)
+#define CKK_DES3 (0x15)
+#define CKK_CAST (0x16)
+#define CKK_CAST3 (0x17)
+#define CKK_CAST128 (0x18)
+#define CKK_RC5 (0x19)
+#define CKK_IDEA (0x1a)
+#define CKK_SKIPJACK (0x1b)
+#define CKK_BATON (0x1c)
+#define CKK_JUNIPER (0x1d)
+#define CKK_CDMF (0x1e)
+#define CKK_AES (0x1f)
+#define CKK_BLOWFISH (0x20)
+#define CKK_TWOFISH (0x21)
+#define CKK_VENDOR_DEFINED ((unsigned long) (1 << 31))
+
+
+typedef unsigned long ck_certificate_type_t;
+
+#define CKC_X_509 (0)
+#define CKC_X_509_ATTR_CERT (1)
+#define CKC_WTLS (2)
+#define CKC_VENDOR_DEFINED ((unsigned long) (1 << 31))
+
+
+typedef unsigned long ck_attribute_type_t;
+
+#define CKA_CLASS (0)
+#define CKA_TOKEN (1)
+#define CKA_PRIVATE (2)
+#define CKA_LABEL (3)
+#define CKA_APPLICATION (0x10)
+#define CKA_VALUE (0x11)
+#define CKA_OBJECT_ID (0x12)
+#define CKA_CERTIFICATE_TYPE (0x80)
+#define CKA_ISSUER (0x81)
+#define CKA_SERIAL_NUMBER (0x82)
+#define CKA_AC_ISSUER (0x83)
+#define CKA_OWNER (0x84)
+#define CKA_ATTR_TYPES (0x85)
+#define CKA_TRUSTED (0x86)
+#define CKA_CERTIFICATE_CATEGORY (0x87)
+#define CKA_JAVA_MIDP_SECURITY_DOMAIN (0x88)
+#define CKA_URL (0x89)
+#define CKA_HASH_OF_SUBJECT_PUBLIC_KEY (0x8a)
+#define CKA_HASH_OF_ISSUER_PUBLIC_KEY (0x8b)
+#define CKA_CHECK_VALUE (0x90)
+#define CKA_KEY_TYPE (0x100)
+#define CKA_SUBJECT (0x101)
+#define CKA_ID (0x102)
+#define CKA_SENSITIVE (0x103)
+#define CKA_ENCRYPT (0x104)
+#define CKA_DECRYPT (0x105)
+#define CKA_WRAP (0x106)
+#define CKA_UNWRAP (0x107)
+#define CKA_SIGN (0x108)
+#define CKA_SIGN_RECOVER (0x109)
+#define CKA_VERIFY (0x10a)
+#define CKA_VERIFY_RECOVER (0x10b)
+#define CKA_DERIVE (0x10c)
+#define CKA_START_DATE (0x110)
+#define CKA_END_DATE (0x111)
+#define CKA_MODULUS (0x120)
+#define CKA_MODULUS_BITS (0x121)
+#define CKA_PUBLIC_EXPONENT (0x122)
+#define CKA_PRIVATE_EXPONENT (0x123)
+#define CKA_PRIME_1 (0x124)
+#define CKA_PRIME_2 (0x125)
+#define CKA_EXPONENT_1 (0x126)
+#define CKA_EXPONENT_2 (0x127)
+#define CKA_COEFFICIENT (0x128)
+#define CKA_PRIME (0x130)
+#define CKA_SUBPRIME (0x131)
+#define CKA_BASE (0x132)
+#define CKA_PRIME_BITS (0x133)
+#define CKA_SUB_PRIME_BITS (0x134)
+#define CKA_VALUE_BITS (0x160)
+#define CKA_VALUE_LEN (0x161)
+#define CKA_EXTRACTABLE (0x162)
+#define CKA_LOCAL (0x163)
+#define CKA_NEVER_EXTRACTABLE (0x164)
+#define CKA_ALWAYS_SENSITIVE (0x165)
+#define CKA_KEY_GEN_MECHANISM (0x166)
+#define CKA_MODIFIABLE (0x170)
+#define CKA_ECDSA_PARAMS (0x180)
+#define CKA_EC_PARAMS (0x180)
+#define CKA_EC_POINT (0x181)
+#define CKA_SECONDARY_AUTH (0x200)
+#define CKA_AUTH_PIN_FLAGS (0x201)
+#define CKA_ALWAYS_AUTHENTICATE (0x202)
+#define CKA_WRAP_WITH_TRUSTED (0x210)
+#define CKA_HW_FEATURE_TYPE (0x300)
+#define CKA_RESET_ON_INIT (0x301)
+#define CKA_HAS_RESET (0x302)
+#define CKA_PIXEL_X (0x400)
+#define CKA_PIXEL_Y (0x401)
+#define CKA_RESOLUTION (0x402)
+#define CKA_CHAR_ROWS (0x403)
+#define CKA_CHAR_COLUMNS (0x404)
+#define CKA_COLOR (0x405)
+#define CKA_BITS_PER_PIXEL (0x406)
+#define CKA_CHAR_SETS (0x480)
+#define CKA_ENCODING_METHODS (0x481)
+#define CKA_MIME_TYPES (0x482)
+#define CKA_MECHANISM_TYPE (0x500)
+#define CKA_REQUIRED_CMS_ATTRIBUTES (0x501)
+#define CKA_DEFAULT_CMS_ATTRIBUTES (0x502)
+#define CKA_SUPPORTED_CMS_ATTRIBUTES (0x503)
+#define CKA_WRAP_TEMPLATE (CKF_ARRAY_ATTRIBUTE | 0x211)
+#define CKA_UNWRAP_TEMPLATE (CKF_ARRAY_ATTRIBUTE | 0x212)
+#define CKA_ALLOWED_MECHANISMS (CKF_ARRAY_ATTRIBUTE | 0x600)
+#define CKA_VENDOR_DEFINED ((unsigned long) (1 << 31))
+
+
+struct ck_attribute
+{
+ ck_attribute_type_t type;
+ void *value;
+ unsigned long value_len;
+};
+
+
+struct ck_date
+{
+ unsigned char year[4];
+ unsigned char month[2];
+ unsigned char day[2];
+};
+
+
+typedef unsigned long ck_mechanism_type_t;
+
+#define CKM_RSA_PKCS_KEY_PAIR_GEN (0)
+#define CKM_RSA_PKCS (1)
+#define CKM_RSA_9796 (2)
+#define CKM_RSA_X_509 (3)
+#define CKM_MD2_RSA_PKCS (4)
+#define CKM_MD5_RSA_PKCS (5)
+#define CKM_SHA1_RSA_PKCS (6)
+#define CKM_RIPEMD128_RSA_PKCS (7)
+#define CKM_RIPEMD160_RSA_PKCS (8)
+#define CKM_RSA_PKCS_OAEP (9)
+#define CKM_RSA_X9_31_KEY_PAIR_GEN (0xa)
+#define CKM_RSA_X9_31 (0xb)
+#define CKM_SHA1_RSA_X9_31 (0xc)
+#define CKM_RSA_PKCS_PSS (0xd)
+#define CKM_SHA1_RSA_PKCS_PSS (0xe)
+#define CKM_DSA_KEY_PAIR_GEN (0x10)
+#define CKM_DSA (0x11)
+#define CKM_DSA_SHA1 (0x12)
+#define CKM_DH_PKCS_KEY_PAIR_GEN (0x20)
+#define CKM_DH_PKCS_DERIVE (0x21)
+#define CKM_X9_42_DH_KEY_PAIR_GEN (0x30)
+#define CKM_X9_42_DH_DERIVE (0x31)
+#define CKM_X9_42_DH_HYBRID_DERIVE (0x32)
+#define CKM_X9_42_MQV_DERIVE (0x33)
+#define CKM_SHA256_RSA_PKCS (0x40)
+#define CKM_SHA384_RSA_PKCS (0x41)
+#define CKM_SHA512_RSA_PKCS (0x42)
+#define CKM_SHA256_RSA_PKCS_PSS (0x43)
+#define CKM_SHA384_RSA_PKCS_PSS (0x44)
+#define CKM_SHA512_RSA_PKCS_PSS (0x45)
+#define CKM_RC2_KEY_GEN (0x100)
+#define CKM_RC2_ECB (0x101)
+#define CKM_RC2_CBC (0x102)
+#define CKM_RC2_MAC (0x103)
+#define CKM_RC2_MAC_GENERAL (0x104)
+#define CKM_RC2_CBC_PAD (0x105)
+#define CKM_RC4_KEY_GEN (0x110)
+#define CKM_RC4 (0x111)
+#define CKM_DES_KEY_GEN (0x120)
+#define CKM_DES_ECB (0x121)
+#define CKM_DES_CBC (0x122)
+#define CKM_DES_MAC (0x123)
+#define CKM_DES_MAC_GENERAL (0x124)
+#define CKM_DES_CBC_PAD (0x125)
+#define CKM_DES2_KEY_GEN (0x130)
+#define CKM_DES3_KEY_GEN (0x131)
+#define CKM_DES3_ECB (0x132)
+#define CKM_DES3_CBC (0x133)
+#define CKM_DES3_MAC (0x134)
+#define CKM_DES3_MAC_GENERAL (0x135)
+#define CKM_DES3_CBC_PAD (0x136)
+#define CKM_CDMF_KEY_GEN (0x140)
+#define CKM_CDMF_ECB (0x141)
+#define CKM_CDMF_CBC (0x142)
+#define CKM_CDMF_MAC (0x143)
+#define CKM_CDMF_MAC_GENERAL (0x144)
+#define CKM_CDMF_CBC_PAD (0x145)
+#define CKM_MD2 (0x200)
+#define CKM_MD2_HMAC (0x201)
+#define CKM_MD2_HMAC_GENERAL (0x202)
+#define CKM_MD5 (0x210)
+#define CKM_MD5_HMAC (0x211)
+#define CKM_MD5_HMAC_GENERAL (0x212)
+#define CKM_SHA_1 (0x220)
+#define CKM_SHA_1_HMAC (0x221)
+#define CKM_SHA_1_HMAC_GENERAL (0x222)
+#define CKM_RIPEMD128 (0x230)
+#define CKM_RIPEMD128_HMAC (0x231)
+#define CKM_RIPEMD128_HMAC_GENERAL (0x232)
+#define CKM_RIPEMD160 (0x240)
+#define CKM_RIPEMD160_HMAC (0x241)
+#define CKM_RIPEMD160_HMAC_GENERAL (0x242)
+#define CKM_SHA256 (0x250)
+#define CKM_SHA256_HMAC (0x251)
+#define CKM_SHA256_HMAC_GENERAL (0x252)
+#define CKM_SHA384 (0x260)
+#define CKM_SHA384_HMAC (0x261)
+#define CKM_SHA384_HMAC_GENERAL (0x262)
+#define CKM_SHA512 (0x270)
+#define CKM_SHA512_HMAC (0x271)
+#define CKM_SHA512_HMAC_GENERAL (0x272)
+#define CKM_CAST_KEY_GEN (0x300)
+#define CKM_CAST_ECB (0x301)
+#define CKM_CAST_CBC (0x302)
+#define CKM_CAST_MAC (0x303)
+#define CKM_CAST_MAC_GENERAL (0x304)
+#define CKM_CAST_CBC_PAD (0x305)
+#define CKM_CAST3_KEY_GEN (0x310)
+#define CKM_CAST3_ECB (0x311)
+#define CKM_CAST3_CBC (0x312)
+#define CKM_CAST3_MAC (0x313)
+#define CKM_CAST3_MAC_GENERAL (0x314)
+#define CKM_CAST3_CBC_PAD (0x315)
+#define CKM_CAST5_KEY_GEN (0x320)
+#define CKM_CAST128_KEY_GEN (0x320)
+#define CKM_CAST5_ECB (0x321)
+#define CKM_CAST128_ECB (0x321)
+#define CKM_CAST5_CBC (0x322)
+#define CKM_CAST128_CBC (0x322)
+#define CKM_CAST5_MAC (0x323)
+#define CKM_CAST128_MAC (0x323)
+#define CKM_CAST5_MAC_GENERAL (0x324)
+#define CKM_CAST128_MAC_GENERAL (0x324)
+#define CKM_CAST5_CBC_PAD (0x325)
+#define CKM_CAST128_CBC_PAD (0x325)
+#define CKM_RC5_KEY_GEN (0x330)
+#define CKM_RC5_ECB (0x331)
+#define CKM_RC5_CBC (0x332)
+#define CKM_RC5_MAC (0x333)
+#define CKM_RC5_MAC_GENERAL (0x334)
+#define CKM_RC5_CBC_PAD (0x335)
+#define CKM_IDEA_KEY_GEN (0x340)
+#define CKM_IDEA_ECB (0x341)
+#define CKM_IDEA_CBC (0x342)
+#define CKM_IDEA_MAC (0x343)
+#define CKM_IDEA_MAC_GENERAL (0x344)
+#define CKM_IDEA_CBC_PAD (0x345)
+#define CKM_GENERIC_SECRET_KEY_GEN (0x350)
+#define CKM_CONCATENATE_BASE_AND_KEY (0x360)
+#define CKM_CONCATENATE_BASE_AND_DATA (0x362)
+#define CKM_CONCATENATE_DATA_AND_BASE (0x363)
+#define CKM_XOR_BASE_AND_DATA (0x364)
+#define CKM_EXTRACT_KEY_FROM_KEY (0x365)
+#define CKM_SSL3_PRE_MASTER_KEY_GEN (0x370)
+#define CKM_SSL3_MASTER_KEY_DERIVE (0x371)
+#define CKM_SSL3_KEY_AND_MAC_DERIVE (0x372)
+#define CKM_SSL3_MASTER_KEY_DERIVE_DH (0x373)
+#define CKM_TLS_PRE_MASTER_KEY_GEN (0x374)
+#define CKM_TLS_MASTER_KEY_DERIVE (0x375)
+#define CKM_TLS_KEY_AND_MAC_DERIVE (0x376)
+#define CKM_TLS_MASTER_KEY_DERIVE_DH (0x377)
+#define CKM_SSL3_MD5_MAC (0x380)
+#define CKM_SSL3_SHA1_MAC (0x381)
+#define CKM_MD5_KEY_DERIVATION (0x390)
+#define CKM_MD2_KEY_DERIVATION (0x391)
+#define CKM_SHA1_KEY_DERIVATION (0x392)
+#define CKM_PBE_MD2_DES_CBC (0x3a0)
+#define CKM_PBE_MD5_DES_CBC (0x3a1)
+#define CKM_PBE_MD5_CAST_CBC (0x3a2)
+#define CKM_PBE_MD5_CAST3_CBC (0x3a3)
+#define CKM_PBE_MD5_CAST5_CBC (0x3a4)
+#define CKM_PBE_MD5_CAST128_CBC (0x3a4)
+#define CKM_PBE_SHA1_CAST5_CBC (0x3a5)
+#define CKM_PBE_SHA1_CAST128_CBC (0x3a5)
+#define CKM_PBE_SHA1_RC4_128 (0x3a6)
+#define CKM_PBE_SHA1_RC4_40 (0x3a7)
+#define CKM_PBE_SHA1_DES3_EDE_CBC (0x3a8)
+#define CKM_PBE_SHA1_DES2_EDE_CBC (0x3a9)
+#define CKM_PBE_SHA1_RC2_128_CBC (0x3aa)
+#define CKM_PBE_SHA1_RC2_40_CBC (0x3ab)
+#define CKM_PKCS5_PBKD2 (0x3b0)
+#define CKM_PBA_SHA1_WITH_SHA1_HMAC (0x3c0)
+#define CKM_KEY_WRAP_LYNKS (0x400)
+#define CKM_KEY_WRAP_SET_OAEP (0x401)
+#define CKM_SKIPJACK_KEY_GEN (0x1000)
+#define CKM_SKIPJACK_ECB64 (0x1001)
+#define CKM_SKIPJACK_CBC64 (0x1002)
+#define CKM_SKIPJACK_OFB64 (0x1003)
+#define CKM_SKIPJACK_CFB64 (0x1004)
+#define CKM_SKIPJACK_CFB32 (0x1005)
+#define CKM_SKIPJACK_CFB16 (0x1006)
+#define CKM_SKIPJACK_CFB8 (0x1007)
+#define CKM_SKIPJACK_WRAP (0x1008)
+#define CKM_SKIPJACK_PRIVATE_WRAP (0x1009)
+#define CKM_SKIPJACK_RELAYX (0x100a)
+#define CKM_KEA_KEY_PAIR_GEN (0x1010)
+#define CKM_KEA_KEY_DERIVE (0x1011)
+#define CKM_FORTEZZA_TIMESTAMP (0x1020)
+#define CKM_BATON_KEY_GEN (0x1030)
+#define CKM_BATON_ECB128 (0x1031)
+#define CKM_BATON_ECB96 (0x1032)
+#define CKM_BATON_CBC128 (0x1033)
+#define CKM_BATON_COUNTER (0x1034)
+#define CKM_BATON_SHUFFLE (0x1035)
+#define CKM_BATON_WRAP (0x1036)
+#define CKM_ECDSA_KEY_PAIR_GEN (0x1040)
+#define CKM_EC_KEY_PAIR_GEN (0x1040)
+#define CKM_ECDSA (0x1041)
+#define CKM_ECDSA_SHA1 (0x1042)
+#define CKM_ECDH1_DERIVE (0x1050)
+#define CKM_ECDH1_COFACTOR_DERIVE (0x1051)
+#define CKM_ECMQV_DERIVE (0x1052)
+#define CKM_JUNIPER_KEY_GEN (0x1060)
+#define CKM_JUNIPER_ECB128 (0x1061)
+#define CKM_JUNIPER_CBC128 (0x1062)
+#define CKM_JUNIPER_COUNTER (0x1063)
+#define CKM_JUNIPER_SHUFFLE (0x1064)
+#define CKM_JUNIPER_WRAP (0x1065)
+#define CKM_FASTHASH (0x1070)
+#define CKM_AES_KEY_GEN (0x1080)
+#define CKM_AES_ECB (0x1081)
+#define CKM_AES_CBC (0x1082)
+#define CKM_AES_MAC (0x1083)
+#define CKM_AES_MAC_GENERAL (0x1084)
+#define CKM_AES_CBC_PAD (0x1085)
+#define CKM_DSA_PARAMETER_GEN (0x2000)
+#define CKM_DH_PKCS_PARAMETER_GEN (0x2001)
+#define CKM_X9_42_DH_PARAMETER_GEN (0x2002)
+#define CKM_VENDOR_DEFINED ((unsigned long) (1 << 31))
+
+
+struct ck_mechanism
+{
+ ck_mechanism_type_t mechanism;
+ void *parameter;
+ unsigned long parameter_len;
+};
+
+
+struct ck_mechanism_info
+{
+ unsigned long min_key_size;
+ unsigned long max_key_size;
+ ck_flags_t flags;
+};
+
+#define CKF_HW (1 << 0)
+#define CKF_ENCRYPT (1 << 8)
+#define CKF_DECRYPT (1 << 9)
+#define CKF_DIGEST (1 << 10)
+#define CKF_SIGN (1 << 11)
+#define CKF_SIGN_RECOVER (1 << 12)
+#define CKF_VERIFY (1 << 13)
+#define CKF_VERIFY_RECOVER (1 << 14)
+#define CKF_GENERATE (1 << 15)
+#define CKF_GENERATE_KEY_PAIR (1 << 16)
+#define CKF_WRAP (1 << 17)
+#define CKF_UNWRAP (1 << 18)
+#define CKF_DERIVE (1 << 19)
+#define CKF_EXTENSION ((unsigned long) (1 << 31))
+
+
+/* Flags for C_WaitForSlotEvent. */
+#define CKF_DONT_BLOCK (1)
+
+
+typedef unsigned long ck_rv_t;
+
+
+typedef ck_rv_t (*ck_notify_t) (ck_session_handle_t session,
+ ck_notification_t event, void *application);
+
+/* Forward reference. */
+struct ck_function_list;
+
+#define _CK_DECLARE_FUNCTION(name, args) \
+typedef ck_rv_t (*CK_ ## name) args; \
+ck_rv_t CK_SPEC name args
+
+_CK_DECLARE_FUNCTION (C_Initialize, (void *init_args));
+_CK_DECLARE_FUNCTION (C_Finalize, (void *reserved));
+_CK_DECLARE_FUNCTION (C_GetInfo, (struct ck_info *info));
+_CK_DECLARE_FUNCTION (C_GetFunctionList,
+ (struct ck_function_list **function_list));
+
+_CK_DECLARE_FUNCTION (C_GetSlotList,
+ (unsigned char token_present, ck_slot_id_t *slot_list,
+ unsigned long *count));
+_CK_DECLARE_FUNCTION (C_GetSlotInfo,
+ (ck_slot_id_t slot_id, struct ck_slot_info *info));
+_CK_DECLARE_FUNCTION (C_GetTokenInfo,
+ (ck_slot_id_t slot_id, struct ck_token_info *info));
+_CK_DECLARE_FUNCTION (C_WaitForSlotEvent,
+ (ck_flags_t flags, ck_slot_id_t *slot, void *reserved));
+_CK_DECLARE_FUNCTION (C_GetMechanismList,
+ (ck_slot_id_t slot_id,
+ ck_mechanism_type_t *mechanism_list,
+ unsigned long *count));
+_CK_DECLARE_FUNCTION (C_GetMechanismInfo,
+ (ck_slot_id_t slot_id, ck_mechanism_type_t type,
+ struct ck_mechanism_info *info));
+_CK_DECLARE_FUNCTION (C_InitToken,
+ (ck_slot_id_t slot_id, unsigned char *pin,
+ unsigned long pin_len, unsigned char *label));
+_CK_DECLARE_FUNCTION (C_InitPIN,
+ (ck_session_handle_t session, unsigned char *pin,
+ unsigned long pin_len));
+_CK_DECLARE_FUNCTION (C_SetPIN,
+ (ck_session_handle_t session, unsigned char *old_pin,
+ unsigned long old_len, unsigned char *new_pin,
+ unsigned long new_len));
+
+_CK_DECLARE_FUNCTION (C_OpenSession,
+ (ck_slot_id_t slot_id, ck_flags_t flags,
+ void *application, ck_notify_t notify,
+ ck_session_handle_t *session));
+_CK_DECLARE_FUNCTION (C_CloseSession, (ck_session_handle_t session));
+_CK_DECLARE_FUNCTION (C_CloseAllSessions, (ck_slot_id_t slot_id));
+_CK_DECLARE_FUNCTION (C_GetSessionInfo,
+ (ck_session_handle_t session,
+ struct ck_session_info *info));
+_CK_DECLARE_FUNCTION (C_GetOperationState,
+ (ck_session_handle_t session,
+ unsigned char *operation_state,
+ unsigned long *operation_state_len));
+_CK_DECLARE_FUNCTION (C_SetOperationState,
+ (ck_session_handle_t session,
+ unsigned char *operation_state,
+ unsigned long operation_state_len,
+ ck_object_handle_t encryption_key,
+ ck_object_handle_t authentiation_key));
+_CK_DECLARE_FUNCTION (C_Login,
+ (ck_session_handle_t session, ck_user_type_t user_type,
+ unsigned char *pin, unsigned long pin_len));
+_CK_DECLARE_FUNCTION (C_Logout, (ck_session_handle_t session));
+
+_CK_DECLARE_FUNCTION (C_CreateObject,
+ (ck_session_handle_t session,
+ struct ck_attribute *templ,
+ unsigned long count, ck_object_handle_t *object));
+_CK_DECLARE_FUNCTION (C_CopyObject,
+ (ck_session_handle_t session, ck_object_handle_t object,
+ struct ck_attribute *templ, unsigned long count,
+ ck_object_handle_t *new_object));
+_CK_DECLARE_FUNCTION (C_DestroyObject,
+ (ck_session_handle_t session,
+ ck_object_handle_t object));
+_CK_DECLARE_FUNCTION (C_GetObjectSize,
+ (ck_session_handle_t session,
+ ck_object_handle_t object,
+ unsigned long *size));
+_CK_DECLARE_FUNCTION (C_GetAttributeValue,
+ (ck_session_handle_t session,
+ ck_object_handle_t object,
+ struct ck_attribute *templ,
+ unsigned long count));
+_CK_DECLARE_FUNCTION (C_SetAttributeValue,
+ (ck_session_handle_t session,
+ ck_object_handle_t object,
+ struct ck_attribute *templ,
+ unsigned long count));
+_CK_DECLARE_FUNCTION (C_FindObjectsInit,
+ (ck_session_handle_t session,
+ struct ck_attribute *templ,
+ unsigned long count));
+_CK_DECLARE_FUNCTION (C_FindObjects,
+ (ck_session_handle_t session,
+ ck_object_handle_t *object,
+ unsigned long max_object_count,
+ unsigned long *object_count));
+_CK_DECLARE_FUNCTION (C_FindObjectsFinal,
+ (ck_session_handle_t session));
+
+_CK_DECLARE_FUNCTION (C_EncryptInit,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t key));
+_CK_DECLARE_FUNCTION (C_Encrypt,
+ (ck_session_handle_t session,
+ unsigned char *data, unsigned long data_len,
+ unsigned char *encrypted_data,
+ unsigned long *encrypted_data_len));
+_CK_DECLARE_FUNCTION (C_EncryptUpdate,
+ (ck_session_handle_t session,
+ unsigned char *part, unsigned long part_len,
+ unsigned char *encrypted_part,
+ unsigned long *encrypted_part_len));
+_CK_DECLARE_FUNCTION (C_EncryptFinal,
+ (ck_session_handle_t session,
+ unsigned char *last_encrypted_part,
+ unsigned long *last_encrypted_part_len));
+
+_CK_DECLARE_FUNCTION (C_DecryptInit,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t key));
+_CK_DECLARE_FUNCTION (C_Decrypt,
+ (ck_session_handle_t session,
+ unsigned char *encrypted_data,
+ unsigned long encrypted_data_len,
+ unsigned char *data, unsigned long *data_len));
+_CK_DECLARE_FUNCTION (C_DecryptUpdate,
+ (ck_session_handle_t session,
+ unsigned char *encrypted_part,
+ unsigned long encrypted_part_len,
+ unsigned char *part, unsigned long *part_len));
+_CK_DECLARE_FUNCTION (C_DecryptFinal,
+ (ck_session_handle_t session,
+ unsigned char *last_part,
+ unsigned long *last_part_len));
+
+_CK_DECLARE_FUNCTION (C_DigestInit,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism));
+_CK_DECLARE_FUNCTION (C_Digest,
+ (ck_session_handle_t session,
+ unsigned char *data, unsigned long data_len,
+ unsigned char *digest,
+ unsigned long *digest_len));
+_CK_DECLARE_FUNCTION (C_DigestUpdate,
+ (ck_session_handle_t session,
+ unsigned char *part, unsigned long part_len));
+_CK_DECLARE_FUNCTION (C_DigestKey,
+ (ck_session_handle_t session, ck_object_handle_t key));
+_CK_DECLARE_FUNCTION (C_DigestFinal,
+ (ck_session_handle_t session,
+ unsigned char *digest,
+ unsigned long *digest_len));
+
+_CK_DECLARE_FUNCTION (C_SignInit,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t key));
+_CK_DECLARE_FUNCTION (C_Sign,
+ (ck_session_handle_t session,
+ unsigned char *data, unsigned long data_len,
+ unsigned char *signature,
+ unsigned long *signature_len));
+_CK_DECLARE_FUNCTION (C_SignUpdate,
+ (ck_session_handle_t session,
+ unsigned char *part, unsigned long part_len));
+_CK_DECLARE_FUNCTION (C_SignFinal,
+ (ck_session_handle_t session,
+ unsigned char *signature,
+ unsigned long *signature_len));
+_CK_DECLARE_FUNCTION (C_SignRecoverInit,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t key));
+_CK_DECLARE_FUNCTION (C_SignRecover,
+ (ck_session_handle_t session,
+ unsigned char *data, unsigned long data_len,
+ unsigned char *signature,
+ unsigned long *signature_len));
+
+_CK_DECLARE_FUNCTION (C_VerifyInit,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t key));
+_CK_DECLARE_FUNCTION (C_Verify,
+ (ck_session_handle_t session,
+ unsigned char *data, unsigned long data_len,
+ unsigned char *signature,
+ unsigned long signature_len));
+_CK_DECLARE_FUNCTION (C_VerifyUpdate,
+ (ck_session_handle_t session,
+ unsigned char *part, unsigned long part_len));
+_CK_DECLARE_FUNCTION (C_VerifyFinal,
+ (ck_session_handle_t session,
+ unsigned char *signature,
+ unsigned long signature_len));
+_CK_DECLARE_FUNCTION (C_VerifyRecoverInit,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t key));
+_CK_DECLARE_FUNCTION (C_VerifyRecover,
+ (ck_session_handle_t session,
+ unsigned char *signature,
+ unsigned long signature_len,
+ unsigned char *data,
+ unsigned long *data_len));
+
+_CK_DECLARE_FUNCTION (C_DigestEncryptUpdate,
+ (ck_session_handle_t session,
+ unsigned char *part, unsigned long part_len,
+ unsigned char *encrypted_part,
+ unsigned long *encrypted_part_len));
+_CK_DECLARE_FUNCTION (C_DecryptDigestUpdate,
+ (ck_session_handle_t session,
+ unsigned char *encrypted_part,
+ unsigned long encrypted_part_len,
+ unsigned char *part,
+ unsigned long *part_len));
+_CK_DECLARE_FUNCTION (C_SignEncryptUpdate,
+ (ck_session_handle_t session,
+ unsigned char *part, unsigned long part_len,
+ unsigned char *encrypted_part,
+ unsigned long *encrypted_part_len));
+_CK_DECLARE_FUNCTION (C_DecryptVerifyUpdate,
+ (ck_session_handle_t session,
+ unsigned char *encrypted_part,
+ unsigned long encrypted_part_len,
+ unsigned char *part,
+ unsigned long *part_len));
+
+_CK_DECLARE_FUNCTION (C_GenerateKey,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ struct ck_attribute *templ,
+ unsigned long count,
+ ck_object_handle_t *key));
+_CK_DECLARE_FUNCTION (C_GenerateKeyPair,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ struct ck_attribute *public_key_template,
+ unsigned long public_key_attribute_count,
+ struct ck_attribute *private_key_template,
+ unsigned long private_key_attribute_count,
+ ck_object_handle_t *public_key,
+ ck_object_handle_t *private_key));
+_CK_DECLARE_FUNCTION (C_WrapKey,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t wrapping_key,
+ ck_object_handle_t key,
+ unsigned char *wrapped_key,
+ unsigned long *wrapped_key_len));
+_CK_DECLARE_FUNCTION (C_UnwrapKey,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t unwrapping_key,
+ unsigned char *wrapped_key,
+ unsigned long wrapped_key_len,
+ struct ck_attribute *templ,
+ unsigned long attribute_count,
+ ck_object_handle_t *key));
+_CK_DECLARE_FUNCTION (C_DeriveKey,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t base_key,
+ struct ck_attribute *templ,
+ unsigned long attribute_count,
+ ck_object_handle_t *key));
+
+_CK_DECLARE_FUNCTION (C_SeedRandom,
+ (ck_session_handle_t session, unsigned char *seed,
+ unsigned long seed_len));
+_CK_DECLARE_FUNCTION (C_GenerateRandom,
+ (ck_session_handle_t session,
+ unsigned char *random_data,
+ unsigned long random_len));
+
+_CK_DECLARE_FUNCTION (C_GetFunctionStatus, (ck_session_handle_t session));
+_CK_DECLARE_FUNCTION (C_CancelFunction, (ck_session_handle_t session));
+
+
+struct ck_function_list
+{
+ struct ck_version version;
+ CK_C_Initialize C_Initialize;
+ CK_C_Finalize C_Finalize;
+ CK_C_GetInfo C_GetInfo;
+ CK_C_GetFunctionList C_GetFunctionList;
+ CK_C_GetSlotList C_GetSlotList;
+ CK_C_GetSlotInfo C_GetSlotInfo;
+ CK_C_GetTokenInfo C_GetTokenInfo;
+ CK_C_GetMechanismList C_GetMechanismList;
+ CK_C_GetMechanismInfo C_GetMechanismInfo;
+ CK_C_InitToken C_InitToken;
+ CK_C_InitPIN C_InitPIN;
+ CK_C_SetPIN C_SetPIN;
+ CK_C_OpenSession C_OpenSession;
+ CK_C_CloseSession C_CloseSession;
+ CK_C_CloseAllSessions C_CloseAllSessions;
+ CK_C_GetSessionInfo C_GetSessionInfo;
+ CK_C_GetOperationState C_GetOperationState;
+ CK_C_SetOperationState C_SetOperationState;
+ CK_C_Login C_Login;
+ CK_C_Logout C_Logout;
+ CK_C_CreateObject C_CreateObject;
+ CK_C_CopyObject C_CopyObject;
+ CK_C_DestroyObject C_DestroyObject;
+ CK_C_GetObjectSize C_GetObjectSize;
+ CK_C_GetAttributeValue C_GetAttributeValue;
+ CK_C_SetAttributeValue C_SetAttributeValue;
+ CK_C_FindObjectsInit C_FindObjectsInit;
+ CK_C_FindObjects C_FindObjects;
+ CK_C_FindObjectsFinal C_FindObjectsFinal;
+ CK_C_EncryptInit C_EncryptInit;
+ CK_C_Encrypt C_Encrypt;
+ CK_C_EncryptUpdate C_EncryptUpdate;
+ CK_C_EncryptFinal C_EncryptFinal;
+ CK_C_DecryptInit C_DecryptInit;
+ CK_C_Decrypt C_Decrypt;
+ CK_C_DecryptUpdate C_DecryptUpdate;
+ CK_C_DecryptFinal C_DecryptFinal;
+ CK_C_DigestInit C_DigestInit;
+ CK_C_Digest C_Digest;
+ CK_C_DigestUpdate C_DigestUpdate;
+ CK_C_DigestKey C_DigestKey;
+ CK_C_DigestFinal C_DigestFinal;
+ CK_C_SignInit C_SignInit;
+ CK_C_Sign C_Sign;
+ CK_C_SignUpdate C_SignUpdate;
+ CK_C_SignFinal C_SignFinal;
+ CK_C_SignRecoverInit C_SignRecoverInit;
+ CK_C_SignRecover C_SignRecover;
+ CK_C_VerifyInit C_VerifyInit;
+ CK_C_Verify C_Verify;
+ CK_C_VerifyUpdate C_VerifyUpdate;
+ CK_C_VerifyFinal C_VerifyFinal;
+ CK_C_VerifyRecoverInit C_VerifyRecoverInit;
+ CK_C_VerifyRecover C_VerifyRecover;
+ CK_C_DigestEncryptUpdate C_DigestEncryptUpdate;
+ CK_C_DecryptDigestUpdate C_DecryptDigestUpdate;
+ CK_C_SignEncryptUpdate C_SignEncryptUpdate;
+ CK_C_DecryptVerifyUpdate C_DecryptVerifyUpdate;
+ CK_C_GenerateKey C_GenerateKey;
+ CK_C_GenerateKeyPair C_GenerateKeyPair;
+ CK_C_WrapKey C_WrapKey;
+ CK_C_UnwrapKey C_UnwrapKey;
+ CK_C_DeriveKey C_DeriveKey;
+ CK_C_SeedRandom C_SeedRandom;
+ CK_C_GenerateRandom C_GenerateRandom;
+ CK_C_GetFunctionStatus C_GetFunctionStatus;
+ CK_C_CancelFunction C_CancelFunction;
+ CK_C_WaitForSlotEvent C_WaitForSlotEvent;
+};
+
+
+typedef ck_rv_t (*ck_createmutex_t) (void **mutex);
+typedef ck_rv_t (*ck_destroymutex_t) (void *mutex);
+typedef ck_rv_t (*ck_lockmutex_t) (void *mutex);
+typedef ck_rv_t (*ck_unlockmutex_t) (void *mutex);
+
+
+struct ck_c_initialize_args
+{
+ ck_createmutex_t create_mutex;
+ ck_destroymutex_t destroy_mutex;
+ ck_lockmutex_t lock_mutex;
+ ck_unlockmutex_t unlock_mutex;
+ ck_flags_t flags;
+ void *reserved;
+};
+
+
+#define CKF_LIBRARY_CANT_CREATE_OS_THREADS (1 << 0)
+#define CKF_OS_LOCKING_OK (1 << 1)
+
+#define CKR_OK (0)
+#define CKR_CANCEL (1)
+#define CKR_HOST_MEMORY (2)
+#define CKR_SLOT_ID_INVALID (3)
+#define CKR_GENERAL_ERROR (5)
+#define CKR_FUNCTION_FAILED (6)
+#define CKR_ARGUMENTS_BAD (7)
+#define CKR_NO_EVENT (8)
+#define CKR_NEED_TO_CREATE_THREADS (9)
+#define CKR_CANT_LOCK (0xa)
+#define CKR_ATTRIBUTE_READ_ONLY (0x10)
+#define CKR_ATTRIBUTE_SENSITIVE (0x11)
+#define CKR_ATTRIBUTE_TYPE_INVALID (0x12)
+#define CKR_ATTRIBUTE_VALUE_INVALID (0x13)
+#define CKR_DATA_INVALID (0x20)
+#define CKR_DATA_LEN_RANGE (0x21)
+#define CKR_DEVICE_ERROR (0x30)
+#define CKR_DEVICE_MEMORY (0x31)
+#define CKR_DEVICE_REMOVED (0x32)
+#define CKR_ENCRYPTED_DATA_INVALID (0x40)
+#define CKR_ENCRYPTED_DATA_LEN_RANGE (0x41)
+#define CKR_FUNCTION_CANCELED (0x50)
+#define CKR_FUNCTION_NOT_PARALLEL (0x51)
+#define CKR_FUNCTION_NOT_SUPPORTED (0x54)
+#define CKR_KEY_HANDLE_INVALID (0x60)
+#define CKR_KEY_SIZE_RANGE (0x62)
+#define CKR_KEY_TYPE_INCONSISTENT (0x63)
+#define CKR_KEY_NOT_NEEDED (0x64)
+#define CKR_KEY_CHANGED (0x65)
+#define CKR_KEY_NEEDED (0x66)
+#define CKR_KEY_INDIGESTIBLE (0x67)
+#define CKR_KEY_FUNCTION_NOT_PERMITTED (0x68)
+#define CKR_KEY_NOT_WRAPPABLE (0x69)
+#define CKR_KEY_UNEXTRACTABLE (0x6a)
+#define CKR_MECHANISM_INVALID (0x70)
+#define CKR_MECHANISM_PARAM_INVALID (0x71)
+#define CKR_OBJECT_HANDLE_INVALID (0x82)
+#define CKR_OPERATION_ACTIVE (0x90)
+#define CKR_OPERATION_NOT_INITIALIZED (0x91)
+#define CKR_PIN_INCORRECT (0xa0)
+#define CKR_PIN_INVALID (0xa1)
+#define CKR_PIN_LEN_RANGE (0xa2)
+#define CKR_PIN_EXPIRED (0xa3)
+#define CKR_PIN_LOCKED (0xa4)
+#define CKR_SESSION_CLOSED (0xb0)
+#define CKR_SESSION_COUNT (0xb1)
+#define CKR_SESSION_HANDLE_INVALID (0xb3)
+#define CKR_SESSION_PARALLEL_NOT_SUPPORTED (0xb4)
+#define CKR_SESSION_READ_ONLY (0xb5)
+#define CKR_SESSION_EXISTS (0xb6)
+#define CKR_SESSION_READ_ONLY_EXISTS (0xb7)
+#define CKR_SESSION_READ_WRITE_SO_EXISTS (0xb8)
+#define CKR_SIGNATURE_INVALID (0xc0)
+#define CKR_SIGNATURE_LEN_RANGE (0xc1)
+#define CKR_TEMPLATE_INCOMPLETE (0xd0)
+#define CKR_TEMPLATE_INCONSISTENT (0xd1)
+#define CKR_TOKEN_NOT_PRESENT (0xe0)
+#define CKR_TOKEN_NOT_RECOGNIZED (0xe1)
+#define CKR_TOKEN_WRITE_PROTECTED (0xe2)
+#define CKR_UNWRAPPING_KEY_HANDLE_INVALID (0xf0)
+#define CKR_UNWRAPPING_KEY_SIZE_RANGE (0xf1)
+#define CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT (0xf2)
+#define CKR_USER_ALREADY_LOGGED_IN (0x100)
+#define CKR_USER_NOT_LOGGED_IN (0x101)
+#define CKR_USER_PIN_NOT_INITIALIZED (0x102)
+#define CKR_USER_TYPE_INVALID (0x103)
+#define CKR_USER_ANOTHER_ALREADY_LOGGED_IN (0x104)
+#define CKR_USER_TOO_MANY_TYPES (0x105)
+#define CKR_WRAPPED_KEY_INVALID (0x110)
+#define CKR_WRAPPED_KEY_LEN_RANGE (0x112)
+#define CKR_WRAPPING_KEY_HANDLE_INVALID (0x113)
+#define CKR_WRAPPING_KEY_SIZE_RANGE (0x114)
+#define CKR_WRAPPING_KEY_TYPE_INCONSISTENT (0x115)
+#define CKR_RANDOM_SEED_NOT_SUPPORTED (0x120)
+#define CKR_RANDOM_NO_RNG (0x121)
+#define CKR_DOMAIN_PARAMS_INVALID (0x130)
+#define CKR_BUFFER_TOO_SMALL (0x150)
+#define CKR_SAVED_STATE_INVALID (0x160)
+#define CKR_INFORMATION_SENSITIVE (0x170)
+#define CKR_STATE_UNSAVEABLE (0x180)
+#define CKR_CRYPTOKI_NOT_INITIALIZED (0x190)
+#define CKR_CRYPTOKI_ALREADY_INITIALIZED (0x191)
+#define CKR_MUTEX_BAD (0x1a0)
+#define CKR_MUTEX_NOT_LOCKED (0x1a1)
+#define CKR_FUNCTION_REJECTED (0x200)
+#define CKR_VENDOR_DEFINED ((unsigned long) (1 << 31))
+
+
+
+/* Compatibility layer. */
+
+#ifdef CRYPTOKI_COMPAT
+
+#undef CK_DEFINE_FUNCTION
+#define CK_DEFINE_FUNCTION(retval, name) retval CK_SPEC name
+
+/* For NULL. */
+#include <stddef.h>
+
+typedef unsigned char CK_BYTE;
+typedef unsigned char CK_CHAR;
+typedef unsigned char CK_UTF8CHAR;
+typedef unsigned char CK_BBOOL;
+typedef unsigned long int CK_ULONG;
+typedef long int CK_LONG;
+typedef CK_BYTE *CK_BYTE_PTR;
+typedef CK_CHAR *CK_CHAR_PTR;
+typedef CK_UTF8CHAR *CK_UTF8CHAR_PTR;
+typedef CK_ULONG *CK_ULONG_PTR;
+typedef void *CK_VOID_PTR;
+typedef void **CK_VOID_PTR_PTR;
+#define CK_FALSE 0
+#define CK_TRUE 1
+#ifndef CK_DISABLE_TRUE_FALSE
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+#endif
+
+typedef struct ck_version CK_VERSION;
+typedef struct ck_version *CK_VERSION_PTR;
+
+typedef struct ck_info CK_INFO;
+typedef struct ck_info *CK_INFO_PTR;
+
+typedef ck_slot_id_t *CK_SLOT_ID_PTR;
+
+typedef struct ck_slot_info CK_SLOT_INFO;
+typedef struct ck_slot_info *CK_SLOT_INFO_PTR;
+
+typedef struct ck_token_info CK_TOKEN_INFO;
+typedef struct ck_token_info *CK_TOKEN_INFO_PTR;
+
+typedef ck_session_handle_t *CK_SESSION_HANDLE_PTR;
+
+typedef struct ck_session_info CK_SESSION_INFO;
+typedef struct ck_session_info *CK_SESSION_INFO_PTR;
+
+typedef ck_object_handle_t *CK_OBJECT_HANDLE_PTR;
+
+typedef ck_object_class_t *CK_OBJECT_CLASS_PTR;
+
+typedef struct ck_attribute CK_ATTRIBUTE;
+typedef struct ck_attribute *CK_ATTRIBUTE_PTR;
+
+typedef struct ck_date CK_DATE;
+typedef struct ck_date *CK_DATE_PTR;
+
+typedef ck_mechanism_type_t *CK_MECHANISM_TYPE_PTR;
+
+typedef struct ck_mechanism CK_MECHANISM;
+typedef struct ck_mechanism *CK_MECHANISM_PTR;
+
+typedef struct ck_mechanism_info CK_MECHANISM_INFO;
+typedef struct ck_mechanism_info *CK_MECHANISM_INFO_PTR;
+
+typedef struct ck_function_list CK_FUNCTION_LIST;
+typedef struct ck_function_list *CK_FUNCTION_LIST_PTR;
+typedef struct ck_function_list **CK_FUNCTION_LIST_PTR_PTR;
+
+typedef struct ck_c_initialize_args CK_C_INITIALIZE_ARGS;
+typedef struct ck_c_initialize_args *CK_C_INITIALIZE_ARGS_PTR;
+
+#define NULL_PTR NULL
+
+/* Delete the helper macros defined at the top of the file. */
+#undef ck_flags_t
+#undef ck_version
+
+#undef ck_info
+#undef cryptoki_version
+#undef manufacturer_id
+#undef library_description
+#undef library_version
+
+#undef ck_notification_t
+#undef ck_slot_id_t
+
+#undef ck_slot_info
+#undef slot_description
+#undef hardware_version
+#undef firmware_version
+
+#undef ck_token_info
+#undef serial_number
+#undef max_session_count
+#undef session_count
+#undef max_rw_session_count
+#undef rw_session_count
+#undef max_pin_len
+#undef min_pin_len
+#undef total_public_memory
+#undef free_public_memory
+#undef total_private_memory
+#undef free_private_memory
+#undef utc_time
+
+#undef ck_session_handle_t
+#undef ck_user_type_t
+#undef ck_state_t
+
+#undef ck_session_info
+#undef slot_id
+#undef device_error
+
+#undef ck_object_handle_t
+#undef ck_object_class_t
+#undef ck_hw_feature_type_t
+#undef ck_key_type_t
+#undef ck_certificate_type_t
+#undef ck_attribute_type_t
+
+#undef ck_attribute
+#undef value
+#undef value_len
+
+#undef ck_date
+
+#undef ck_mechanism_type_t
+
+#undef ck_mechanism
+#undef parameter
+#undef parameter_len
+
+#undef ck_mechanism_info
+#undef min_key_size
+#undef max_key_size
+
+#undef ck_rv_t
+#undef ck_notify_t
+
+#undef ck_function_list
+
+#undef ck_createmutex_t
+#undef ck_destroymutex_t
+#undef ck_lockmutex_t
+#undef ck_unlockmutex_t
+
+#undef ck_c_initialize_args
+#undef create_mutex
+#undef destroy_mutex
+#undef lock_mutex
+#undef unlock_mutex
+#undef reserved
+
+#endif /* CRYPTOKI_COMPAT */
+
+
+/* System dependencies. */
+#if defined(_WIN32) || defined(CRYPTOKI_FORCE_WIN32)
+#pragma pack(pop, cryptoki)
+#endif
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* PKCS11_H */
diff --git a/src/plugins/preauth/pkinit/pkinit.exports b/src/plugins/preauth/pkinit/pkinit.exports
new file mode 100644
index 0000000..98e96c3
--- /dev/null
+++ b/src/plugins/preauth/pkinit/pkinit.exports
@@ -0,0 +1,2 @@
+preauthentication_client_1
+preauthentication_server_1
diff --git a/src/plugins/preauth/pkinit/pkinit.h b/src/plugins/preauth/pkinit/pkinit.h
new file mode 100644
index 0000000..e18962e
--- /dev/null
+++ b/src/plugins/preauth/pkinit/pkinit.h
@@ -0,0 +1,354 @@
+/*
+ * COPYRIGHT (C) 2006,2007
+ * THE REGENTS OF THE UNIVERSITY OF MICHIGAN
+ * ALL RIGHTS RESERVED
+ *
+ * Permission is granted to use, copy, create derivative works
+ * and redistribute this software and such derivative works
+ * for any purpose, so long as the name of The University of
+ * Michigan is not used in any advertising or publicity
+ * pertaining to the use of distribution of this software
+ * without specific, written prior authorization. If the
+ * above copyright notice or any other identification of the
+ * University of Michigan is included in any copy of any
+ * portion of this software, then the disclaimer below must
+ * also be included.
+ *
+ * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
+ * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
+ * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
+ * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
+ * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
+ * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGES.
+ */
+
+#ifndef _PKINIT_H
+#define _PKINIT_H
+
+#include <krb5/krb5.h>
+#include <krb5/preauth_plugin.h>
+#include <k5-int-pkinit.h>
+#include <profile.h>
+#include "pkinit_accessor.h"
+
+/*
+ * It is anticipated that all the special checks currently
+ * required when talking to a Longhorn server will go away
+ * by the time it is officially released and all references
+ * to the longhorn global can be removed and any code
+ * #ifdef'd with LONGHORN_BETA_COMPAT can be removed.
+ * And this #define!
+ */
+#define LONGHORN_BETA_COMPAT 1
+#ifdef LONGHORN_BETA_COMPAT
+extern int longhorn; /* XXX Talking to a Longhorn server? */
+#endif
+
+
+#ifndef WITHOUT_PKCS11
+#include "pkcs11.h"
+
+#define PKCS11_MODNAME "opensc-pkcs11.so"
+#define PK_SIGLEN_GUESS 1000
+#define PK_NOSLOT 999999
+#endif
+
+#define DH_PROTOCOL 1
+#define RSA_PROTOCOL 2
+
+#define TD_TRUSTED_CERTIFIERS 104
+#define TD_INVALID_CERTIFICATES 105
+#define TD_DH_PARAMETERS 109
+
+#define PKINIT_CTX_MAGIC 0x05551212
+#define PKINIT_REQ_CTX_MAGIC 0xdeadbeef
+
+#define PKINIT_DEFAULT_DH_MIN_BITS 2048
+
+/* Make pkiDebug(fmt,...) print, or not. */
+#ifdef DEBUG
+#define pkiDebug printf
+#else
+/* Still evaluates for side effects. */
+static inline void pkiDebug (const char *fmt, ...) { }
+/* This is better if the compiler doesn't inline variadic functions
+ well, but gcc will warn about "left-hand operand of comma
+ expression has no effect". Still evaluates for side effects. */
+/* #define pkiDebug (void) */
+#endif
+
+/* Solaris compiler doesn't grok __FUNCTION__
+ * hack for now. Fix all the uses eventually. */
+#define __FUNCTION__ __func__
+
+/* Macros to deal with converting between various data types... */
+#define PADATA_TO_KRB5DATA(pad, k5d) \
+ (k5d)->length = (pad)->length; (k5d)->data = (char *)(pad)->contents;
+#define OCTETDATA_TO_KRB5DATA(octd, k5d) \
+ (k5d)->length = (octd)->length; (k5d)->data = (char *)(octd)->data;
+
+extern const krb5_octet_data dh_oid;
+
+/*
+ * notes about crypto contexts:
+ *
+ * the basic idea is that there are crypto contexts that live at
+ * both the plugin level and request level. the identity context (that
+ * keeps info about your own certs and such) is separate because
+ * it is needed at different levels for the kdc and and the client.
+ * (the kdc's identity is at the plugin level, the client's identity
+ * information could change per-request.)
+ * the identity context is meant to have the entity's cert,
+ * a list of trusted and intermediate cas, a list of crls, and any
+ * pkcs11 information. the req context is meant to have the
+ * received certificate and the DH related information. the plugin
+ * context is meant to have global crypto information, i.e., OIDs
+ * and constant DH parameter information.
+ */
+
+/*
+ * plugin crypto context should keep plugin common information,
+ * eg., OIDs, known DHparams
+ */
+typedef struct _pkinit_plg_crypto_context *pkinit_plg_crypto_context;
+
+/*
+ * request crypto context should keep reqyest common information,
+ * eg., received credentials, DH parameters of this request
+ */
+typedef struct _pkinit_req_crypto_context *pkinit_req_crypto_context;
+
+/*
+ * identity context should keep information about credentials
+ * for the request, eg., my credentials, trusted ca certs,
+ * intermediate ca certs, crls, pkcs11 info
+ */
+typedef struct _pkinit_identity_crypto_context *pkinit_identity_crypto_context;
+
+/*
+ * this structure keeps information about the config options
+ */
+typedef struct _pkinit_plg_opts {
+ int require_eku; /* require EKU checking (default is true) */
+ int accept_secondary_eku;/* accept secondary EKU (default is false) */
+ int allow_upn; /* allow UPN-SAN instead of pkinit-SAN */
+ int dh_or_rsa; /* selects DH or RSA based pkinit */
+ int require_crl_checking; /* require CRL for a CA (default is false) */
+ int dh_min_bits; /* minimum DH modulus size allowed */
+} pkinit_plg_opts;
+
+/*
+ * this structure keeps options used for a given request
+ */
+typedef struct _pkinit_req_opts {
+ int require_eku;
+ int accept_secondary_eku;
+ int allow_upn;
+ int dh_or_rsa;
+ int require_crl_checking;
+ int dh_size; /* initial request DH modulus size (default=1024) */
+ int require_hostname_match;
+ int win2k_target;
+ int win2k_require_cksum;
+} pkinit_req_opts;
+
+/*
+ * information about identity from config file or command line
+ */
+
+#define PKINIT_ID_OPT_USER_IDENTITY 1
+#define PKINIT_ID_OPT_ANCHOR_CAS 2
+#define PKINIT_ID_OPT_INTERMEDIATE_CAS 3
+#define PKINIT_ID_OPT_CRLS 4
+#define PKINIT_ID_OPT_OCSP 5
+#define PKINIT_ID_OPT_DN_MAPPING 6 /* XXX ? */
+
+typedef struct _pkinit_identity_opts {
+ char *identity;
+ char **identity_alt;
+ char **anchors;
+ char **intermediates;
+ char **crls;
+ char *ocsp;
+ char *dn_mapping_file;
+ int idtype;
+ char *cert_filename;
+ char *key_filename;
+#ifndef WITHOUT_PKCS11
+ char *p11_module_name;
+ CK_SLOT_ID slotid;
+ char *token_label;
+ char *cert_id_string;
+ char *cert_label;
+#endif
+} pkinit_identity_opts;
+
+
+/*
+ * Client's plugin context
+ */
+struct _pkinit_context {
+ int magic;
+ pkinit_plg_crypto_context cryptoctx;
+ pkinit_plg_opts *opts;
+ pkinit_identity_opts *idopts;
+};
+typedef struct _pkinit_context *pkinit_context;
+
+/*
+ * Client's per-request context
+ */
+struct _pkinit_req_context {
+ int magic;
+ pkinit_req_crypto_context cryptoctx;
+ pkinit_req_opts *opts;
+ pkinit_identity_crypto_context idctx;
+ pkinit_identity_opts *idopts;
+ krb5_preauthtype pa_type;
+};
+typedef struct _pkinit_kdc_context *pkinit_kdc_context;
+
+/*
+ * KDC's (per-realm) plugin context
+ */
+struct _pkinit_kdc_context {
+ int magic;
+ pkinit_plg_crypto_context cryptoctx;
+ pkinit_plg_opts *opts;
+ pkinit_identity_crypto_context idctx;
+ pkinit_identity_opts *idopts;
+ char *realmname;
+ unsigned int realmname_len;
+};
+typedef struct _pkinit_req_context *pkinit_req_context;
+
+/*
+ * KDC's per-request context
+ */
+struct _pkinit_kdc_req_context {
+ int magic;
+ pkinit_req_crypto_context cryptoctx;
+ krb5_auth_pack *rcv_auth_pack;
+ krb5_auth_pack_draft9 *rcv_auth_pack9;
+ krb5_preauthtype pa_type;
+};
+typedef struct _pkinit_kdc_req_context *pkinit_kdc_req_context;
+
+/*
+ * Functions in pkinit_lib.c
+ */
+
+krb5_error_code pkinit_init_req_opts(pkinit_req_opts **);
+void pkinit_fini_req_opts(pkinit_req_opts *);
+
+krb5_error_code pkinit_init_plg_opts(pkinit_plg_opts **);
+void pkinit_fini_plg_opts(pkinit_plg_opts *);
+
+krb5_error_code pkinit_init_identity_opts(pkinit_identity_opts **idopts);
+void pkinit_fini_identity_opts(pkinit_identity_opts *idopts);
+krb5_error_code pkinit_dup_identity_opts(pkinit_identity_opts *src_opts,
+ pkinit_identity_opts **dest_opts);
+
+/*
+ * Functions in pkinit_identity.c
+ */
+char * idtype2string(int idtype);
+char * catype2string(int catype);
+
+krb5_error_code pkinit_identity_initialize
+ (krb5_context context, /* IN */
+ pkinit_plg_crypto_context plg_cryptoctx, /* IN */
+ pkinit_req_crypto_context req_cryptoctx, /* IN */
+ pkinit_identity_opts *idopts, /* IN */
+ pkinit_identity_crypto_context id_cryptoctx, /* IN/OUT */
+ int do_matching, /* IN */
+ krb5_principal princ); /* IN (optional) */
+
+krb5_error_code pkinit_cert_matching
+ (krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ krb5_principal princ);
+
+/*
+ * initialization and free functions
+ */
+void init_krb5_pa_pk_as_req(krb5_pa_pk_as_req **in);
+void init_krb5_pa_pk_as_req_draft9(krb5_pa_pk_as_req_draft9 **in);
+void init_krb5_reply_key_pack(krb5_reply_key_pack **in);
+void init_krb5_reply_key_pack_draft9(krb5_reply_key_pack_draft9 **in);
+
+void init_krb5_auth_pack(krb5_auth_pack **in);
+void init_krb5_auth_pack_draft9(krb5_auth_pack_draft9 **in);
+void init_krb5_pa_pk_as_rep(krb5_pa_pk_as_rep **in);
+void init_krb5_pa_pk_as_rep_draft9(krb5_pa_pk_as_rep_draft9 **in);
+void init_krb5_typed_data(krb5_typed_data **in);
+void init_krb5_subject_pk_info(krb5_subject_pk_info **in);
+
+void free_krb5_pa_pk_as_req(krb5_pa_pk_as_req **in);
+void free_krb5_pa_pk_as_req_draft9(krb5_pa_pk_as_req_draft9 **in);
+void free_krb5_reply_key_pack(krb5_reply_key_pack **in);
+void free_krb5_reply_key_pack_draft9(krb5_reply_key_pack_draft9 **in);
+void free_krb5_auth_pack(krb5_auth_pack **in);
+void free_krb5_auth_pack_draft9(krb5_context, krb5_auth_pack_draft9 **in);
+void free_krb5_pa_pk_as_rep(krb5_pa_pk_as_rep **in);
+void free_krb5_pa_pk_as_rep_draft9(krb5_pa_pk_as_rep_draft9 **in);
+void free_krb5_external_principal_identifier(krb5_external_principal_identifier ***in);
+void free_krb5_trusted_ca(krb5_trusted_ca ***in);
+void free_krb5_typed_data(krb5_typed_data ***in);
+void free_krb5_algorithm_identifiers(krb5_algorithm_identifier ***in);
+void free_krb5_algorithm_identifier(krb5_algorithm_identifier *in);
+void free_krb5_kdc_dh_key_info(krb5_kdc_dh_key_info **in);
+void free_krb5_subject_pk_info(krb5_subject_pk_info **in);
+krb5_error_code pkinit_copy_krb5_octet_data(krb5_octet_data *dst, const krb5_octet_data *src);
+
+
+/*
+ * Functions in pkinit_profile.c
+ */
+krb5_error_code pkinit_kdcdefault_strings
+ (krb5_context context, const char *realmname, const char *option,
+ char ***ret_value);
+krb5_error_code pkinit_kdcdefault_string
+ (krb5_context context, const char *realmname, const char *option,
+ char **ret_value);
+krb5_error_code pkinit_kdcdefault_boolean
+ (krb5_context context, const char *realmname, const char *option,
+ int default_value, int *ret_value);
+krb5_error_code pkinit_kdcdefault_integer
+ (krb5_context context, const char *realmname, const char *option,
+ int default_value, int *ret_value);
+
+
+krb5_error_code pkinit_libdefault_strings
+ (krb5_context context, const krb5_data *realm,
+ const char *option, char ***ret_value);
+krb5_error_code pkinit_libdefault_string
+ (krb5_context context, const krb5_data *realm,
+ const char *option, char **ret_value);
+krb5_error_code pkinit_libdefault_boolean
+ (krb5_context context, const krb5_data *realm, const char *option,
+ int default_value, int *ret_value);
+krb5_error_code pkinit_libdefault_integer
+ (krb5_context context, const krb5_data *realm, const char *option,
+ int default_value, int *ret_value);
+
+/*
+ * debugging functions
+ */
+void print_buffer(unsigned char *, unsigned int);
+void print_buffer_bin(unsigned char *, unsigned int, char *);
+
+/*
+ * Now get crypto function declarations
+ */
+#include "pkinit_crypto.h"
+
+#endif /* _PKINIT_H */
diff --git a/src/plugins/preauth/pkinit/pkinit_accessor.c b/src/plugins/preauth/pkinit/pkinit_accessor.c
new file mode 100644
index 0000000..e954ca3
--- /dev/null
+++ b/src/plugins/preauth/pkinit/pkinit_accessor.c
@@ -0,0 +1,118 @@
+/*
+ * COPYRIGHT (C) 2006,2007
+ * THE REGENTS OF THE UNIVERSITY OF MICHIGAN
+ * ALL RIGHTS RESERVED
+ *
+ * Permission is granted to use, copy, create derivative works
+ * and redistribute this software and such derivative works
+ * for any purpose, so long as the name of The University of
+ * Michigan is not used in any advertising or publicity
+ * pertaining to the use of distribution of this software
+ * without specific, written prior authorization. If the
+ * above copyright notice or any other identification of the
+ * University of Michigan is included in any copy of any
+ * portion of this software, then the disclaimer below must
+ * also be included.
+ *
+ * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
+ * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
+ * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
+ * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
+ * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
+ * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGES.
+ */
+
+#include <k5-int.h>
+#include "pkinit_accessor.h"
+
+#define DEF_FUNC_PTRS(type) \
+krb5_error_code (*k5int_encode_##type)(const type *, krb5_data **); \
+krb5_error_code (*k5int_decode_##type)(const krb5_data *, type **)
+
+#define DEF_FUNC_PTRS_ARRAY(type) \
+krb5_error_code (*k5int_encode_##type)(const type **, krb5_data **); \
+krb5_error_code (*k5int_decode_##type)(const krb5_data *, type ***)
+
+DEF_FUNC_PTRS(krb5_auth_pack);
+DEF_FUNC_PTRS(krb5_auth_pack_draft9);
+DEF_FUNC_PTRS(krb5_kdc_dh_key_info);
+DEF_FUNC_PTRS(krb5_pa_pk_as_rep);
+DEF_FUNC_PTRS(krb5_pa_pk_as_rep_draft9);
+DEF_FUNC_PTRS(krb5_pa_pk_as_req);
+DEF_FUNC_PTRS(krb5_pa_pk_as_req_draft9);
+DEF_FUNC_PTRS(krb5_reply_key_pack);
+DEF_FUNC_PTRS(krb5_reply_key_pack_draft9);
+DEF_FUNC_PTRS_ARRAY(krb5_typed_data);
+
+/* special cases... */
+krb5_error_code (*k5int_decode_krb5_principal_name)
+ (const krb5_data *, krb5_principal_data **);
+
+krb5_error_code (*k5int_encode_krb5_td_dh_parameters)
+ (const krb5_algorithm_identifier **, krb5_data **code);
+krb5_error_code (*k5int_decode_krb5_td_dh_parameters)
+ (const krb5_data *, krb5_algorithm_identifier ***);
+
+krb5_error_code (*k5int_encode_krb5_td_trusted_certifiers)
+ (const krb5_external_principal_identifier **, krb5_data **code);
+krb5_error_code (*k5int_decode_krb5_td_trusted_certifiers)
+ (const krb5_data *, krb5_external_principal_identifier ***);
+
+krb5_error_code (*k5int_decode_krb5_as_req)
+ (const krb5_data *output, krb5_kdc_req **rep);
+krb5_error_code (*k5int_encode_krb5_kdc_req_body)
+ (const krb5_kdc_req *rep, krb5_data **code);
+void KRB5_CALLCONV (*k5int_krb5_free_kdc_req)
+ (krb5_context, krb5_kdc_req * );
+void (*k5int_set_prompt_types)
+ (krb5_context, krb5_prompt_type *);
+krb5_error_code (*k5int_encode_krb5_authdata_elt)
+ (const krb5_authdata *rep, krb5_data **code);
+
+
+
+/*
+ * Grab internal function pointers from the krb5int_accessor
+ * structure and make them available
+ */
+krb5_error_code
+pkinit_accessor_init(void)
+{
+ krb5_error_code retval;
+ krb5int_access k5int;
+
+ retval = krb5int_accessor(&k5int, KRB5INT_ACCESS_VERSION);
+ if (retval)
+ return retval;
+#define SET_PTRS(type) \
+k5int_encode_##type = k5int.encode_##type; \
+k5int_decode_##type = k5int.decode_##type;
+
+ SET_PTRS(krb5_auth_pack);
+ SET_PTRS(krb5_auth_pack_draft9);
+ SET_PTRS(krb5_kdc_dh_key_info);
+ SET_PTRS(krb5_pa_pk_as_rep);
+ SET_PTRS(krb5_pa_pk_as_rep_draft9);
+ SET_PTRS(krb5_pa_pk_as_req);
+ SET_PTRS(krb5_pa_pk_as_req_draft9);
+ SET_PTRS(krb5_reply_key_pack);
+ SET_PTRS(krb5_reply_key_pack_draft9);
+ SET_PTRS(krb5_td_dh_parameters);
+ SET_PTRS(krb5_td_trusted_certifiers);
+ SET_PTRS(krb5_typed_data);
+
+ /* special cases... */
+ k5int_decode_krb5_principal_name = k5int.decode_krb5_principal_name;
+ k5int_decode_krb5_as_req = k5int.decode_krb5_as_req;
+ k5int_encode_krb5_kdc_req_body = k5int.encode_krb5_kdc_req_body;
+ k5int_krb5_free_kdc_req = k5int.krb5_free_kdc_req;
+ k5int_set_prompt_types = k5int.krb5int_set_prompt_types;
+ k5int_encode_krb5_authdata_elt = k5int.encode_krb5_authdata_elt;
+ return 0;
+}
diff --git a/src/plugins/preauth/pkinit/pkinit_accessor.h b/src/plugins/preauth/pkinit/pkinit_accessor.h
new file mode 100644
index 0000000..ba82533
--- /dev/null
+++ b/src/plugins/preauth/pkinit/pkinit_accessor.h
@@ -0,0 +1,83 @@
+/*
+ * COPYRIGHT (C) 2006,2007
+ * THE REGENTS OF THE UNIVERSITY OF MICHIGAN
+ * ALL RIGHTS RESERVED
+ *
+ * Permission is granted to use, copy, create derivative works
+ * and redistribute this software and such derivative works
+ * for any purpose, so long as the name of The University of
+ * Michigan is not used in any advertising or publicity
+ * pertaining to the use of distribution of this software
+ * without specific, written prior authorization. If the
+ * above copyright notice or any other identification of the
+ * University of Michigan is included in any copy of any
+ * portion of this software, then the disclaimer below must
+ * also be included.
+ *
+ * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
+ * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
+ * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
+ * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
+ * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
+ * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGES.
+ */
+
+#ifndef _PKINIT_ACCESSOR_H
+#define _PKINIT_ACCESSOR_H
+
+/*
+ * Function prototypes
+ */
+krb5_error_code pkinit_accessor_init(void);
+
+#define DEF_EXT_FUNC_PTRS(type) \
+extern krb5_error_code (*k5int_encode_##type)(const type *, krb5_data **); \
+extern krb5_error_code (*k5int_decode_##type)(const krb5_data *, type **)
+
+#define DEF_EXT_FUNC_PTRS_ARRAY(type) \
+extern krb5_error_code (*k5int_encode_##type)(const type **, krb5_data **); \
+extern krb5_error_code (*k5int_decode_##type)(const krb5_data *, type ***)
+
+DEF_EXT_FUNC_PTRS(krb5_auth_pack);
+DEF_EXT_FUNC_PTRS(krb5_auth_pack_draft9);
+DEF_EXT_FUNC_PTRS(krb5_kdc_dh_key_info);
+DEF_EXT_FUNC_PTRS(krb5_pa_pk_as_rep);
+DEF_EXT_FUNC_PTRS(krb5_pa_pk_as_rep_draft9);
+DEF_EXT_FUNC_PTRS(krb5_pa_pk_as_req);
+DEF_EXT_FUNC_PTRS(krb5_pa_pk_as_req_draft9);
+DEF_EXT_FUNC_PTRS(krb5_reply_key_pack);
+DEF_EXT_FUNC_PTRS(krb5_reply_key_pack_draft9);
+DEF_EXT_FUNC_PTRS_ARRAY(krb5_typed_data);
+
+/* special cases... */
+extern krb5_error_code (*k5int_decode_krb5_principal_name)
+ (const krb5_data *, krb5_principal_data **);
+
+extern krb5_error_code (*k5int_encode_krb5_td_dh_parameters)
+ (const krb5_algorithm_identifier **, krb5_data **code);
+extern krb5_error_code (*k5int_decode_krb5_td_dh_parameters)
+ (const krb5_data *, krb5_algorithm_identifier ***);
+
+extern krb5_error_code (*k5int_encode_krb5_td_trusted_certifiers)
+ (const krb5_external_principal_identifier **, krb5_data **code);
+extern krb5_error_code (*k5int_decode_krb5_td_trusted_certifiers)
+ (const krb5_data *, krb5_external_principal_identifier ***);
+
+extern krb5_error_code (*k5int_decode_krb5_as_req)
+ (const krb5_data *output, krb5_kdc_req **rep);
+extern krb5_error_code (*k5int_encode_krb5_kdc_req_body)
+ (const krb5_kdc_req *rep, krb5_data **code);
+extern void KRB5_CALLCONV (*k5int_krb5_free_kdc_req)
+ (krb5_context, krb5_kdc_req * );
+extern void (*k5int_set_prompt_types)
+ (krb5_context, krb5_prompt_type *);
+extern krb5_error_code (*k5int_encode_krb5_authdata_elt)
+ (const krb5_authdata *rep, krb5_data **code);
+
+#endif /* _PKINIT_ACCESSOR_H */
diff --git a/src/plugins/preauth/pkinit/pkinit_clnt.c b/src/plugins/preauth/pkinit/pkinit_clnt.c
new file mode 100644
index 0000000..0d6da4d
--- /dev/null
+++ b/src/plugins/preauth/pkinit/pkinit_clnt.c
@@ -0,0 +1,1492 @@
+/*
+ * COPYRIGHT (C) 2006,2007
+ * THE REGENTS OF THE UNIVERSITY OF MICHIGAN
+ * ALL RIGHTS RESERVED
+ *
+ * Permission is granted to use, copy, create derivative works
+ * and redistribute this software and such derivative works
+ * for any purpose, so long as the name of The University of
+ * Michigan is not used in any advertising or publicity
+ * pertaining to the use of distribution of this software
+ * without specific, written prior authorization. If the
+ * above copyright notice or any other identification of the
+ * University of Michigan is included in any copy of any
+ * portion of this software, then the disclaimer below must
+ * also be included.
+ *
+ * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
+ * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
+ * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
+ * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
+ * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
+ * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGES.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <dlfcn.h>
+#include <sys/stat.h>
+
+#include "pkinit.h"
+
+#ifdef LONGHORN_BETA_COMPAT
+/*
+ * It is anticipated that all the special checks currently
+ * required when talking to a Longhorn server will go away
+ * by the time it is officially released and all references
+ * to the longhorn global can be removed and any code
+ * #ifdef'd with LONGHORN_BETA_COMPAT can be removed.
+ *
+ * Current testing (20070620) is against a patched Beta 3
+ * version of Longhorn. Most, if not all, problems should
+ * be fixed in SP1 of Longhorn.
+ */
+int longhorn = 0; /* Talking to a Longhorn server? */
+#endif
+
+krb5_error_code pkinit_client_process
+ (krb5_context context, void *plugin_context, void *request_context,
+ krb5_get_init_creds_opt *gic_opt,
+ preauth_get_client_data_proc get_data_proc,
+ struct _krb5_preauth_client_rock *rock,
+ krb5_kdc_req * request, krb5_data *encoded_request_body,
+ krb5_data *encoded_previous_request, krb5_pa_data *in_padata,
+ krb5_prompter_fct prompter, void *prompter_data,
+ preauth_get_as_key_proc gak_fct, void *gak_data,
+ krb5_data * salt, krb5_data * s2kparams,
+ krb5_keyblock * as_key, krb5_pa_data *** out_padata);
+
+krb5_error_code pkinit_client_tryagain
+ (krb5_context context, void *plugin_context, void *request_context,
+ krb5_get_init_creds_opt *gic_opt,
+ preauth_get_client_data_proc get_data_proc,
+ struct _krb5_preauth_client_rock *rock,
+ krb5_kdc_req * request, krb5_data *encoded_request_body,
+ krb5_data *encoded_previous_request,
+ krb5_pa_data *in_padata, krb5_error *err_reply,
+ krb5_prompter_fct prompter, void *prompter_data,
+ preauth_get_as_key_proc gak_fct, void *gak_data,
+ krb5_data * salt, krb5_data * s2kparams,
+ krb5_keyblock * as_key, krb5_pa_data *** out_padata);
+
+void pkinit_client_req_init
+ (krb5_context contex, void *plugin_context, void **request_context);
+
+void pkinit_client_req_fini
+ (krb5_context context, void *plugin_context, void *request_context);
+
+krb5_error_code pa_pkinit_gen_req
+ (krb5_context context, pkinit_context plgctx,
+ pkinit_req_context reqctx, krb5_kdc_req * request,
+ krb5_pa_data * in_padata, krb5_pa_data *** out_padata,
+ krb5_prompter_fct prompter, void *prompter_data,
+ krb5_get_init_creds_opt *gic_opt);
+
+krb5_error_code pkinit_as_req_create
+ (krb5_context context, pkinit_context plgctx,
+ pkinit_req_context reqctx, krb5_timestamp ctsec,
+ krb5_int32 cusec, krb5_ui_4 nonce,
+ const krb5_checksum * cksum, krb5_principal server,
+ krb5_data ** as_req);
+
+krb5_error_code pkinit_as_rep_parse
+ (krb5_context context, pkinit_context plgctx,
+ pkinit_req_context reqctx, krb5_preauthtype pa_type,
+ krb5_kdc_req * request, const krb5_data * as_rep,
+ krb5_keyblock * key_block, krb5_enctype etype, krb5_data *);
+
+krb5_error_code pa_pkinit_parse_rep
+ (krb5_context context, pkinit_context plgctx,
+ pkinit_req_context reqcxt, krb5_kdc_req * request,
+ krb5_pa_data * in_padata, krb5_enctype etype,
+ krb5_keyblock * as_key, krb5_data *);
+
+static int pkinit_client_plugin_init(krb5_context context, void **blob);
+static void pkinit_client_plugin_fini(krb5_context context, void *blob);
+
+krb5_error_code
+pa_pkinit_gen_req(krb5_context context,
+ pkinit_context plgctx,
+ pkinit_req_context reqctx,
+ krb5_kdc_req * request,
+ krb5_pa_data * in_padata,
+ krb5_pa_data *** out_padata,
+ krb5_prompter_fct prompter,
+ void *prompter_data,
+ krb5_get_init_creds_opt *gic_opt)
+{
+
+ krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ krb5_data *out_data = NULL;
+ krb5_timestamp ctsec = 0;
+ krb5_int32 cusec = 0;
+ krb5_ui_4 nonce = 0;
+ krb5_checksum cksum;
+ krb5_data *der_req = NULL;
+ krb5_pa_data **return_pa_data = NULL;
+
+ cksum.contents = NULL;
+ reqctx->pa_type = in_padata->pa_type;
+
+ pkiDebug("kdc_options = 0x%x till = %d\n",
+ request->kdc_options, request->till);
+ /* If we don't have a client, we're done */
+ if (request->client == NULL) {
+ pkiDebug("No request->client; aborting PKINIT\n");
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+
+ retval = pkinit_get_kdc_cert(context, plgctx->cryptoctx, reqctx->cryptoctx,
+ reqctx->idctx, request->server);
+ if (retval) {
+ pkiDebug("pkinit_get_kdc_cert returned %d\n", retval);
+ goto cleanup;
+ }
+
+ /* checksum of the encoded KDC-REQ-BODY */
+ retval = k5int_encode_krb5_kdc_req_body(request, &der_req);
+ if (retval) {
+ pkiDebug("encode_krb5_kdc_req_body returned %d\n", (int) retval);
+ goto cleanup;
+ }
+
+ retval = krb5_c_make_checksum(context, CKSUMTYPE_NIST_SHA, NULL, 0,
+ der_req, &cksum);
+ if (retval)
+ goto cleanup;
+#ifdef DEBUG_CKSUM
+ pkiDebug("calculating checksum on buf size (%d)\n", der_req->length);
+ print_buffer(der_req->data, der_req->length);
+#endif
+
+ retval = krb5_us_timeofday(context, &ctsec, &cusec);
+ if (retval)
+ goto cleanup;
+
+ /* XXX PKINIT RFC says that nonce in PKAuthenticator doesn't have be the
+ * same as in the AS_REQ. However, if we pick a different nonce, then we
+ * need to remember that info when AS_REP is returned. I'm choosing to
+ * reuse the AS_REQ nonce.
+ */
+ nonce = request->nonce;
+
+ retval = pkinit_as_req_create(context, plgctx, reqctx, ctsec, cusec,
+ nonce, &cksum, request->server, &out_data);
+ if (retval || !out_data->length) {
+ pkiDebug("error %d on pkinit_as_req_create; aborting PKINIT\n",
+ (int) retval);
+ goto cleanup;
+ }
+ retval = ENOMEM;
+ /*
+ * The most we'll return is two pa_data, normally just one.
+ * We need to make room for the NULL terminator.
+ */
+ return_pa_data = (krb5_pa_data **) malloc(3 * sizeof(krb5_pa_data *));
+ if (return_pa_data == NULL)
+ goto cleanup;
+
+ return_pa_data[1] = NULL; /* in case of an early trip to cleanup */
+ return_pa_data[2] = NULL; /* Terminate the list */
+
+ return_pa_data[0] = (krb5_pa_data *) malloc(sizeof(krb5_pa_data));
+ if (return_pa_data[0] == NULL)
+ goto cleanup;
+
+ return_pa_data[1] = (krb5_pa_data *) malloc(sizeof(krb5_pa_data));
+ if (return_pa_data[1] == NULL)
+ goto cleanup;
+
+ return_pa_data[0]->magic = KV5M_PA_DATA;
+
+ if (in_padata->pa_type == KRB5_PADATA_PK_AS_REQ_OLD)
+ return_pa_data[0]->pa_type = KRB5_PADATA_PK_AS_REP_OLD;
+ else
+ return_pa_data[0]->pa_type = in_padata->pa_type;
+ return_pa_data[0]->length = out_data->length;
+ return_pa_data[0]->contents = (krb5_octet *) out_data->data;
+
+#ifdef LONGHORN_BETA_COMPAT
+ /*
+ * LH Beta 3 requires the extra pa-data, even for RFC requests,
+ * in order to get the Checksum rather than a Nonce in the reply.
+ * This can be removed when LH SP1 is released.
+ */
+ if ((return_pa_data[0]->pa_type == KRB5_PADATA_PK_AS_REP_OLD
+ && reqctx->opts->win2k_require_cksum) || (longhorn == 1)) {
+#else
+ if ((return_pa_data[0]->pa_type == KRB5_PADATA_PK_AS_REP_OLD
+ && reqctx->opts->win2k_require_cksum)) {
+#endif
+ return_pa_data[1]->pa_type = 132;
+ return_pa_data[1]->length = 0;
+ return_pa_data[1]->contents = NULL;
+ } else {
+ free(return_pa_data[1]);
+ return_pa_data[1] = NULL; /* Move the list terminator */
+ }
+ *out_padata = return_pa_data;
+ retval = 0;
+
+ cleanup:
+ if (der_req != NULL)
+ krb5_free_data(context, der_req);
+
+ if (out_data != NULL)
+ free(out_data);
+
+ if (retval) {
+ if (return_pa_data) {
+ if (return_pa_data[0] != NULL)
+ free(return_pa_data[0]);
+ if (return_pa_data[1] != NULL)
+ free(return_pa_data[1]);
+ free(return_pa_data);
+ }
+ if (out_data) {
+ free(out_data->data);
+ free(out_data);
+ }
+ }
+ return retval;
+}
+
+krb5_error_code
+pkinit_as_req_create(krb5_context context,
+ pkinit_context plgctx,
+ pkinit_req_context reqctx,
+ krb5_timestamp ctsec,
+ krb5_int32 cusec,
+ krb5_ui_4 nonce,
+ const krb5_checksum * cksum,
+ krb5_principal server,
+ krb5_data ** as_req)
+{
+ krb5_error_code retval = ENOMEM;
+ krb5_subject_pk_info *info = NULL;
+ krb5_data *coded_auth_pack = NULL;
+ krb5_auth_pack *auth_pack = NULL;
+ krb5_pa_pk_as_req *req = NULL;
+ krb5_auth_pack_draft9 *auth_pack9 = NULL;
+ krb5_pa_pk_as_req_draft9 *req9 = NULL;
+ int protocol = reqctx->opts->dh_or_rsa;
+
+ pkiDebug("pkinit_as_req_create pa_type = %d\n", reqctx->pa_type);
+
+ /* Create the authpack */
+ switch((int)reqctx->pa_type) {
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ protocol = RSA_PROTOCOL;
+ init_krb5_auth_pack_draft9(&auth_pack9);
+ if (auth_pack9 == NULL)
+ goto cleanup;
+ auth_pack9->pkAuthenticator.ctime = ctsec;
+ auth_pack9->pkAuthenticator.cusec = cusec;
+ auth_pack9->pkAuthenticator.nonce = nonce;
+ auth_pack9->pkAuthenticator.kdcName = server;
+ auth_pack9->pkAuthenticator.kdcRealm.magic = 0;
+ auth_pack9->pkAuthenticator.kdcRealm.data =
+ (unsigned char *)server->realm.data;
+ auth_pack9->pkAuthenticator.kdcRealm.length = server->realm.length;
+ free(cksum->contents);
+ break;
+ case KRB5_PADATA_PK_AS_REQ:
+ init_krb5_subject_pk_info(&info);
+ if (info == NULL)
+ goto cleanup;
+ init_krb5_auth_pack(&auth_pack);
+ if (auth_pack == NULL)
+ goto cleanup;
+ auth_pack->pkAuthenticator.ctime = ctsec;
+ auth_pack->pkAuthenticator.cusec = cusec;
+ auth_pack->pkAuthenticator.nonce = nonce;
+ auth_pack->pkAuthenticator.paChecksum = *cksum;
+ auth_pack->clientDHNonce.length = 0;
+ auth_pack->clientPublicValue = info;
+
+ /* add List of CMS algorithms */
+ retval = create_krb5_supportedCMSTypes(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, reqctx->idctx,
+ &auth_pack->supportedCMSTypes);
+ if (retval)
+ goto cleanup;
+ break;
+ default:
+ pkiDebug("as_req: unrecognized pa_type = %d\n",
+ (int)reqctx->pa_type);
+ retval = -1;
+ goto cleanup;
+ }
+
+ switch(protocol) {
+ case DH_PROTOCOL:
+ pkiDebug("as_req: DH key transport algorithm\n");
+ retval = pkinit_copy_krb5_octet_data(&info->algorithm.algorithm, &dh_oid);
+ if (retval) {
+ pkiDebug("failed to copy dh_oid\n");
+ goto cleanup;
+ }
+
+ /* create client-side DH keys */
+ if ((retval = client_create_dh(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, reqctx->idctx, reqctx->opts->dh_size,
+ &info->algorithm.parameters.data,
+ &info->algorithm.parameters.length,
+ &info->subjectPublicKey.data,
+ &info->subjectPublicKey.length)) != 0) {
+ pkiDebug("failed to create dh parameters\n");
+ goto cleanup;
+ }
+ break;
+ case RSA_PROTOCOL:
+ pkiDebug("as_req: RSA key transport algorithm\n");
+ switch((int)reqctx->pa_type) {
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ auth_pack9->clientPublicValue = NULL;
+ break;
+ case KRB5_PADATA_PK_AS_REQ:
+ free_krb5_subject_pk_info(&info);
+ auth_pack->clientPublicValue = NULL;
+ break;
+ }
+ break;
+ default:
+ pkiDebug("as_req: unknown key transport protocol %d\n",
+ protocol);
+ retval = -1;
+ goto cleanup;
+ }
+
+ /* Encode the authpack */
+ switch((int)reqctx->pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ retval = k5int_encode_krb5_auth_pack(auth_pack, &coded_auth_pack);
+ break;
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ retval = k5int_encode_krb5_auth_pack_draft9(auth_pack9,
+ &coded_auth_pack);
+ break;
+ }
+ if (retval) {
+ pkiDebug("failed to encode the AuthPack %d\n", retval);
+ goto cleanup;
+ }
+#ifdef DEBUG_ASN1
+ print_buffer_bin((unsigned char *)coded_auth_pack->data,
+ coded_auth_pack->length,
+ "/tmp/client_auth_pack");
+#endif
+
+ /* create PKCS7 object from authpack */
+ switch((int)reqctx->pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ init_krb5_pa_pk_as_req(&req);
+ if (req == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ retval = cms_signeddata_create(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, reqctx->idctx, CMS_SIGN_CLIENT, 1,
+ (unsigned char *)coded_auth_pack->data, coded_auth_pack->length,
+ &req->signedAuthPack.data, &req->signedAuthPack.length);
+#ifdef DEBUG_ASN1
+ print_buffer_bin((unsigned char *)req->signedAuthPack.data,
+ req->signedAuthPack.length,
+ "/tmp/client_signed_data");
+#endif
+ break;
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ init_krb5_pa_pk_as_req_draft9(&req9);
+ if (req9 == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ retval = cms_signeddata_create(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, reqctx->idctx, CMS_SIGN_DRAFT9, 1,
+ (unsigned char *)coded_auth_pack->data, coded_auth_pack->length,
+ &req9->signedAuthPack.data, &req9->signedAuthPack.length);
+ break;
+#ifdef DEBUG_ASN1
+ print_buffer_bin((unsigned char *)req9->signedAuthPack.data,
+ req9->signedAuthPack.length,
+ "/tmp/client_signed_data_draft9");
+#endif
+ }
+ krb5_free_data(context, coded_auth_pack);
+ if (retval) {
+ pkiDebug("failed to create pkcs7 signed data\n");
+ goto cleanup;
+ }
+
+ /* create a list of trusted CAs */
+ switch((int)reqctx->pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ retval = create_krb5_trustedCertifiers(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, reqctx->idctx, &req->trustedCertifiers);
+ if (retval)
+ goto cleanup;
+ retval = create_issuerAndSerial(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, reqctx->idctx, &req->kdcPkId.data,
+ &req->kdcPkId.length);
+ if (retval)
+ goto cleanup;
+
+ /* Encode the as-req */
+ retval = k5int_encode_krb5_pa_pk_as_req(req, as_req);
+ break;
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+#if 0
+ /* W2K3 KDC doesn't like this */
+ retval = create_krb5_trustedCas(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, reqctx->idctx, 1, &req9->trustedCertifiers);
+ if (retval)
+ goto cleanup;
+
+#endif
+ retval = create_issuerAndSerial(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, reqctx->idctx, &req9->kdcCert.data,
+ &req9->kdcCert.length);
+ if (retval)
+ goto cleanup;
+ /* Encode the as-req */
+ retval = k5int_encode_krb5_pa_pk_as_req_draft9(req9, as_req);
+ break;
+ }
+#ifdef DEBUG_ASN1
+ if (!retval)
+ print_buffer_bin((unsigned char *)(*as_req)->data, (*as_req)->length,
+ "/tmp/client_as_req");
+#endif
+
+cleanup:
+ switch((int)reqctx->pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ free_krb5_auth_pack(&auth_pack);
+ free_krb5_pa_pk_as_req(&req);
+ break;
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ free_krb5_pa_pk_as_req_draft9(&req9);
+ free(auth_pack9);
+ break;
+ }
+
+
+ pkiDebug("pkinit_as_req_create retval=%d\n", (int) retval);
+
+ return retval;
+}
+
+krb5_error_code
+pa_pkinit_parse_rep(krb5_context context,
+ pkinit_context plgctx,
+ pkinit_req_context reqctx,
+ krb5_kdc_req * request,
+ krb5_pa_data * in_padata,
+ krb5_enctype etype,
+ krb5_keyblock * as_key,
+ krb5_data *encoded_request)
+{
+ krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ krb5_data asRep = { 0, 0, NULL};
+
+ /*
+ * One way or the other - success or failure - no other PA systems can
+ * work if the server sent us a PKINIT reply, since only we know how to
+ * decrypt the key.
+ */
+ if ((in_padata == NULL) || (in_padata->length == 0)) {
+ pkiDebug("pa_pkinit_parse_rep: no in_padata\n");
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+
+ asRep.data = (char *) in_padata->contents;
+ asRep.length = in_padata->length;
+
+ retval =
+ pkinit_as_rep_parse(context, plgctx, reqctx, in_padata->pa_type,
+ request, &asRep, as_key, etype, encoded_request);
+ if (retval) {
+ pkiDebug("pkinit_as_rep_parse returned %d (%s)\n",
+ retval, error_message(retval));
+ goto cleanup;
+ }
+
+ retval = 0;
+
+cleanup:
+
+ return retval;
+}
+
+static krb5_error_code
+verify_kdc_san(krb5_context context,
+ pkinit_context plgctx,
+ pkinit_req_context reqctx,
+ krb5_principal kdcprinc,
+ int *valid_san,
+ int *need_eku_checking)
+{
+ krb5_error_code retval;
+ char **certhosts = NULL, **cfghosts = NULL;
+ krb5_principal *princs = NULL;
+ unsigned char ***get_dns;
+ int i, j;
+
+ *valid_san = 0;
+ *need_eku_checking = 1;
+
+ retval = pkinit_libdefault_strings(context,
+ krb5_princ_realm(context, kdcprinc),
+ "pkinit_kdc_hostname",
+ &cfghosts);
+ if (retval || cfghosts == NULL) {
+ pkiDebug("%s: No pkinit_kdc_hostname values found in config file\n",
+ __FUNCTION__);
+ get_dns = NULL;
+ } else {
+ pkiDebug("%s: pkinit_kdc_hostname values found in config file\n",
+ __FUNCTION__);
+ get_dns = (unsigned char ***)&certhosts;
+ }
+
+ retval = crypto_retrieve_cert_sans(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, reqctx->idctx,
+ &princs, NULL, get_dns);
+ if (retval) {
+ pkiDebug("%s: error from retrieve_certificate_sans()\n", __FUNCTION__);
+ retval = KRB5KDC_ERR_KDC_NAME_MISMATCH;
+ goto out;
+ }
+#if 0
+ retval = call_san_checking_plugins(context, plgctx, reqctx, idctx,
+ princs, hosts, &plugin_decision,
+ need_eku_checking);
+ pkiDebug("%s: call_san_checking_plugins() returned retval %d\n",
+ __FUNCTION__);
+ if (retval) {
+ retval = KRB5KDC_ERR_KDC_NAME_MISMATCH;
+ goto out;
+ }
+ pkiDebug("%s: call_san_checking_plugins() returned decision %d and "
+ "need_eku_checking %d\n",
+ __FUNCTION__, plugin_decision, *need_eku_checking);
+ if (plugin_decision != NO_DECISION) {
+ retval = plugin_decision;
+ goto out;
+ }
+#endif
+
+ pkiDebug("%s: Checking pkinit sans\n", __FUNCTION__);
+ for (i = 0; princs != NULL && princs[i] != NULL; i++) {
+ if (krb5_principal_compare(context, princs[i], kdcprinc)) {
+ pkiDebug("%s: pkinit san match found\n", __FUNCTION__);
+ *valid_san = 1;
+ *need_eku_checking = 0;
+ retval = 0;
+ goto out;
+ }
+ }
+ pkiDebug("%s: no pkinit san match found\n", __FUNCTION__);
+
+ if (certhosts == NULL) {
+ pkiDebug("%s: no certhosts (or we wouldn't accept them anyway)\n",
+ __FUNCTION__);
+ retval = KRB5KDC_ERR_KDC_NAME_MISMATCH;
+ goto out;
+ }
+
+ for (i = 0; certhosts[i] != NULL; i++) {
+ for (j = 0; cfghosts != NULL && cfghosts[j] != NULL; j++) {
+ pkiDebug("%s: comparing cert name '%s' with config name '%s'\n",
+ __FUNCTION__, certhosts[i], cfghosts[j]);
+ if (strcmp(certhosts[i], cfghosts[j]) == 0) {
+ pkiDebug("%s: we have a dnsName match\n", __FUNCTION__);
+ *valid_san = 1;
+ retval = 0;
+ goto out;
+ }
+ }
+ }
+ pkiDebug("%s: no dnsName san match found\n", __FUNCTION__);
+
+ /* We found no match */
+ retval = 0;
+
+out:
+ if (princs != NULL) {
+ for (i = 0; princs[i] != NULL; i++)
+ krb5_free_principal(context, princs[i]);
+ free(princs);
+ }
+ if (certhosts != NULL) {
+ for (i = 0; certhosts[i] != NULL; i++)
+ free(certhosts[i]);
+ free(certhosts);
+ }
+ if (cfghosts != NULL)
+ profile_free_list(cfghosts);
+
+ pkiDebug("%s: returning retval %d, valid_san %d, need_eku_checking %d\n",
+ __FUNCTION__, retval, *valid_san, *need_eku_checking);
+ return retval;
+}
+
+static krb5_error_code
+verify_kdc_eku(krb5_context context,
+ pkinit_context plgctx,
+ pkinit_req_context reqctx,
+ int *eku_accepted)
+{
+ krb5_error_code retval;
+
+ *eku_accepted = 0;
+
+ if (reqctx->opts->require_eku == 0) {
+ pkiDebug("%s: configuration requests no EKU checking\n", __FUNCTION__);
+ *eku_accepted = 1;
+ retval = 0;
+ goto out;
+ }
+ retval = crypto_check_cert_eku(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, reqctx->idctx,
+ 1, /* kdc cert */
+ reqctx->opts->accept_secondary_eku,
+ eku_accepted);
+ if (retval) {
+ pkiDebug("%s: Error from crypto_check_cert_eku %d (%s)\n",
+ __FUNCTION__, retval, error_message(retval));
+ goto out;
+ }
+
+out:
+ pkiDebug("%s: returning retval %d, eku_accepted %d\n",
+ __FUNCTION__, retval, *eku_accepted);
+ return retval;
+}
+
+/*
+ * Parse PA-PK-AS-REP message. Optionally evaluates the message's
+ * certificate chain.
+ * Optionally returns various components.
+ */
+krb5_error_code
+pkinit_as_rep_parse(krb5_context context,
+ pkinit_context plgctx,
+ pkinit_req_context reqctx,
+ krb5_preauthtype pa_type,
+ krb5_kdc_req *request,
+ const krb5_data *as_rep,
+ krb5_keyblock *key_block,
+ krb5_enctype etype,
+ krb5_data *encoded_request)
+{
+ krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ krb5_pa_pk_as_rep *kdc_reply = NULL;
+ krb5_kdc_dh_key_info *kdc_dh = NULL;
+ krb5_reply_key_pack *key_pack = NULL;
+ krb5_reply_key_pack_draft9 *key_pack9 = NULL;
+ krb5_octet_data dh_data = { 0, 0, NULL };
+ unsigned char *client_key = NULL, *kdc_hostname = NULL;
+ unsigned int client_key_len = 0;
+ krb5_checksum cksum = {0, 0, 0, NULL};
+ krb5_data k5data;
+ int valid_san = 0;
+ int valid_eku = 0;
+ int need_eku_checking = 1;
+
+ assert((as_rep != NULL) && (key_block != NULL));
+
+#ifdef DEBUG_ASN1
+ print_buffer_bin((unsigned char *)as_rep->data, as_rep->length,
+ "/tmp/client_as_rep");
+#endif
+
+ if ((retval = k5int_decode_krb5_pa_pk_as_rep(as_rep, &kdc_reply))) {
+ pkiDebug("decode_krb5_as_rep failed %d\n", retval);
+ return retval;
+ }
+
+ switch(kdc_reply->choice) {
+ case choice_pa_pk_as_rep_dhInfo:
+ pkiDebug("as_rep: DH key transport algorithm\n");
+#ifdef DEBUG_ASN1
+ print_buffer_bin(kdc_reply->u.dh_Info.dhSignedData.data,
+ kdc_reply->u.dh_Info.dhSignedData.length, "/tmp/client_kdc_signeddata");
+#endif
+ if ((retval = cms_signeddata_verify(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, reqctx->idctx, CMS_SIGN_SERVER,
+ reqctx->opts->require_crl_checking,
+ kdc_reply->u.dh_Info.dhSignedData.data,
+ kdc_reply->u.dh_Info.dhSignedData.length,
+ &dh_data.data, &dh_data.length, NULL, NULL)) != 0) {
+ pkiDebug("failed to verify pkcs7 signed data\n");
+ goto cleanup;
+ }
+
+ break;
+ case choice_pa_pk_as_rep_encKeyPack:
+ pkiDebug("as_rep: RSA key transport algorithm\n");
+ if ((retval = cms_envelopeddata_verify(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, reqctx->idctx, pa_type,
+ reqctx->opts->require_crl_checking,
+ kdc_reply->u.encKeyPack.data,
+ kdc_reply->u.encKeyPack.length,
+ &dh_data.data, &dh_data.length)) != 0) {
+ pkiDebug("failed to verify pkcs7 enveloped data\n");
+ goto cleanup;
+ }
+ break;
+ default:
+ pkiDebug("unknown as_rep type %d\n", kdc_reply->choice);
+ retval = -1;
+ goto cleanup;
+ }
+
+ retval = verify_kdc_san(context, plgctx, reqctx, request->server,
+ &valid_san, &need_eku_checking);
+ if (retval)
+ goto cleanup;
+ if (!valid_san) {
+ pkiDebug("%s: did not find an acceptable SAN in KDC certificate\n",
+ __FUNCTION__);
+ retval = KRB5KDC_ERR_KDC_NAME_MISMATCH;
+ goto cleanup;
+ }
+
+ if (need_eku_checking) {
+ retval = verify_kdc_eku(context, plgctx, reqctx,
+ &valid_eku);
+ if (retval)
+ goto cleanup;
+ if (!valid_eku) {
+ pkiDebug("%s: did not find an acceptable EKU in KDC certificate\n",
+ __FUNCTION__);
+ retval = KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE;
+ goto cleanup;
+ }
+ } else
+ pkiDebug("%s: skipping EKU check\n", __FUNCTION__);
+
+ OCTETDATA_TO_KRB5DATA(&dh_data, &k5data);
+
+ switch(kdc_reply->choice) {
+ case choice_pa_pk_as_rep_dhInfo:
+#ifdef DEBUG_ASN1
+ print_buffer_bin(dh_data.data, dh_data.length,
+ "/tmp/client_dh_key");
+#endif
+ if ((retval = k5int_decode_krb5_kdc_dh_key_info(&k5data,
+ &kdc_dh)) != 0) {
+ pkiDebug("failed to decode kdc_dh_key_info\n");
+ goto cleanup;
+ }
+
+ /* client after KDC reply */
+ if ((retval = client_process_dh(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, reqctx->idctx,
+ kdc_dh->subjectPublicKey.data,
+ kdc_dh->subjectPublicKey.length,
+ &client_key, &client_key_len)) != 0) {
+ pkiDebug("failed to process dh params\n");
+ goto cleanup;
+ }
+
+ retval = pkinit_octetstring2key(context, etype, client_key,
+ client_key_len, key_block);
+ if (retval) {
+ pkiDebug("failed to create key pkinit_octetstring2key %s\n",
+ error_message(retval));
+ goto cleanup;
+ }
+
+ break;
+ case choice_pa_pk_as_rep_encKeyPack:
+#ifdef DEBUG_ASN1
+ print_buffer_bin(dh_data.data, dh_data.length,
+ "/tmp/client_key_pack");
+#endif
+ if ((retval = k5int_decode_krb5_reply_key_pack(&k5data,
+ &key_pack)) != 0) {
+ pkiDebug("failed to decode reply_key_pack\n");
+#ifdef LONGHORN_BETA_COMPAT
+ /*
+ * LH Beta 3 requires the extra pa-data, even for RFC requests,
+ * in order to get the Checksum rather than a Nonce in the reply.
+ * This can be removed when LH SP1 is released.
+ */
+ if (pa_type == KRB5_PADATA_PK_AS_REP && longhorn == 0)
+#else
+ if (pa_type == KRB5_PADATA_PK_AS_REP)
+#endif
+ goto cleanup;
+ else {
+ if ((retval =
+ k5int_decode_krb5_reply_key_pack_draft9(&k5data,
+ &key_pack9)) != 0) {
+ pkiDebug("failed to decode reply_key_pack_draft9\n");
+ goto cleanup;
+ }
+ pkiDebug("decode reply_key_pack_draft9\n");
+ if (key_pack9->nonce != request->nonce) {
+ pkiDebug("nonce in AS_REP=%d doesn't match AS_REQ=%d\n", key_pack9->nonce, request->nonce);
+ retval = -1;
+ goto cleanup;
+ }
+ krb5_copy_keyblock_contents(context, &key_pack9->replyKey,
+ key_block);
+ break;
+ }
+ }
+ /*
+ * This is hack but Windows sends back SHA1 checksum
+ * with checksum type of 14. There is currently no
+ * checksum type of 14 defined.
+ */
+ if (key_pack->asChecksum.checksum_type == 14)
+ key_pack->asChecksum.checksum_type = CKSUMTYPE_NIST_SHA;
+ retval = krb5_c_make_checksum(context,
+ key_pack->asChecksum.checksum_type,
+ &key_pack->replyKey,
+ KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
+ encoded_request, &cksum);
+ if (retval) {
+ pkiDebug("failed to make a checksum\n");
+ goto cleanup;
+ }
+
+ if ((cksum.length != key_pack->asChecksum.length) ||
+ memcmp(cksum.contents, key_pack->asChecksum.contents,
+ cksum.length)) {
+ pkiDebug("failed to match the checksums\n");
+#ifdef DEBUG_CKSUM
+ pkiDebug("calculating checksum on buf size (%d)\n",
+ encoded_request->length);
+ print_buffer(encoded_request->data, encoded_request->length);
+ pkiDebug("encrypting key (%d)\n", key_pack->replyKey.length);
+ print_buffer(key_pack->replyKey.contents,
+ key_pack->replyKey.length);
+ pkiDebug("received checksum type=%d size=%d ",
+ key_pack->asChecksum.checksum_type,
+ key_pack->asChecksum.length);
+ print_buffer(key_pack->asChecksum.contents,
+ key_pack->asChecksum.length);
+ pkiDebug("expected checksum type=%d size=%d ",
+ cksum.checksum_type, cksum.length);
+ print_buffer(cksum.contents, cksum.length);
+#endif
+ goto cleanup;
+ } else
+ pkiDebug("checksums match\n");
+
+ krb5_copy_keyblock_contents(context, &key_pack->replyKey,
+ key_block);
+
+ break;
+ default:
+ pkiDebug("unknow as_rep type %d\n", kdc_reply->choice);
+ goto cleanup;
+ }
+
+ retval = 0;
+
+cleanup:
+ if (dh_data.data != NULL)
+ free(dh_data.data);
+ if (client_key != NULL)
+ free(client_key);
+ free_krb5_kdc_dh_key_info(&kdc_dh);
+ free_krb5_pa_pk_as_rep(&kdc_reply);
+
+ if (key_pack != NULL) {
+ free_krb5_reply_key_pack(&key_pack);
+ if (cksum.contents != NULL)
+ free(cksum.contents);
+ }
+ if (key_pack9 != NULL)
+ free_krb5_reply_key_pack_draft9(&key_pack9);
+
+ if (kdc_hostname != NULL)
+ free(kdc_hostname);
+
+ pkiDebug("pkinit_as_rep_parse returning %d (%s)\n",
+ retval, error_message(retval));
+ return retval;
+}
+
+static void
+pkinit_client_profile(krb5_context context,
+ pkinit_context plgctx,
+ pkinit_req_context reqctx,
+ krb5_kdc_req *request)
+{
+ char *eku_string = NULL;
+
+ pkiDebug("pkinit_client_profile %p %p %p %p\n",
+ context, plgctx, reqctx, request);
+
+ pkinit_libdefault_boolean(context, &request->server->realm,
+ "pkinit_win2k",
+ reqctx->opts->win2k_target,
+ &reqctx->opts->win2k_target);
+ pkinit_libdefault_boolean(context, &request->server->realm,
+ "pkinit_win2k_require_binding",
+ reqctx->opts->win2k_require_cksum,
+ &reqctx->opts->win2k_require_cksum);
+ pkinit_libdefault_boolean(context, &request->server->realm,
+ "pkinit_require_crl_checking",
+ reqctx->opts->require_crl_checking,
+ &reqctx->opts->require_crl_checking);
+ pkinit_libdefault_integer(context, &request->server->realm,
+ "pkinit_dh_min_bits",
+ reqctx->opts->dh_size,
+ &reqctx->opts->dh_size);
+ if (reqctx->opts->dh_size != 1024 && reqctx->opts->dh_size != 2048
+ && reqctx->opts->dh_size != 4096) {
+ pkiDebug("%s: invalid value (%d) for pkinit_dh_min_bits, "
+ "using default value (%d) instead\n", __FUNCTION__,
+ reqctx->opts->dh_size, PKINIT_DEFAULT_DH_MIN_BITS);
+ reqctx->opts->dh_size = PKINIT_DEFAULT_DH_MIN_BITS;
+ }
+ pkinit_libdefault_string(context, &request->server->realm,
+ "pkinit_eku_checking",
+ &eku_string);
+ if (eku_string != NULL) {
+ if (strcasecmp(eku_string, "kpKDC") == 0) {
+ reqctx->opts->require_eku = 1;
+ reqctx->opts->accept_secondary_eku = 0;
+ } else if (strcasecmp(eku_string, "kpServerAuth") == 0) {
+ reqctx->opts->require_eku = 1;
+ reqctx->opts->accept_secondary_eku = 1;
+ } else if (strcasecmp(eku_string, "none") == 0) {
+ reqctx->opts->require_eku = 0;
+ reqctx->opts->accept_secondary_eku = 0;
+ } else {
+ pkiDebug("%s: Invalid value for pkinit_eku_checking: '%s'\n",
+ __FUNCTION__, eku_string);
+ }
+ free(eku_string);
+ }
+#ifdef LONGHORN_BETA_COMPAT
+ /* Temporarily just set global flag from config file */
+ pkinit_libdefault_boolean(context, &request->server->realm,
+ "pkinit_longhorn",
+ 0,
+ &longhorn);
+#endif
+
+ /* Only process anchors here if they were not specified on command line */
+ if (reqctx->idopts->anchors == NULL)
+ pkinit_libdefault_strings(context, &request->server->realm,
+ "pkinit_anchors",
+ &reqctx->idopts->anchors);
+ pkinit_libdefault_strings(context, &request->server->realm,
+ "pkinit_pool",
+ &reqctx->idopts->intermediates);
+ pkinit_libdefault_strings(context, &request->server->realm,
+ "pkinit_revoke",
+ &reqctx->idopts->crls);
+ pkinit_libdefault_strings(context, &request->server->realm,
+ "pkinit_identities",
+ &reqctx->idopts->identity_alt);
+}
+
+krb5_error_code
+pkinit_client_process(krb5_context context,
+ void *plugin_context,
+ void *request_context,
+ krb5_get_init_creds_opt *gic_opt,
+ preauth_get_client_data_proc get_data_proc,
+ struct _krb5_preauth_client_rock *rock,
+ krb5_kdc_req *request,
+ krb5_data *encoded_request_body,
+ krb5_data *encoded_previous_request,
+ krb5_pa_data *in_padata,
+ krb5_prompter_fct prompter,
+ void *prompter_data,
+ preauth_get_as_key_proc gak_fct,
+ void *gak_data,
+ krb5_data *salt,
+ krb5_data *s2kparams,
+ krb5_keyblock *as_key,
+ krb5_pa_data ***out_padata)
+{
+ krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ krb5_enctype enctype = -1;
+ krb5_data *cdata = NULL;
+ int processing_request = 0;
+ pkinit_context plgctx = (pkinit_context)plugin_context;
+ pkinit_req_context reqctx = (pkinit_req_context)request_context;
+
+ pkiDebug("pkinit_client_process %p %p %p %p\n",
+ context, plgctx, reqctx, request);
+
+ if (plgctx == NULL || reqctx == NULL)
+ return EINVAL;
+
+ switch ((int) in_padata->pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ pkiDebug("processing KRB5_PADATA_PK_AS_REQ\n");
+ processing_request = 1;
+ break;
+
+ case KRB5_PADATA_PK_AS_REP:
+ pkiDebug("processing KRB5_PADATA_PK_AS_REP\n");
+ break;
+ case KRB5_PADATA_PK_AS_REP_OLD:
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ if (in_padata->length == 0) {
+ pkiDebug("processing KRB5_PADATA_PK_AS_REQ_OLD\n");
+ in_padata->pa_type = KRB5_PADATA_PK_AS_REQ_OLD;
+ processing_request = 1;
+ } else {
+ pkiDebug("processing KRB5_PADATA_PK_AS_REP_OLD\n");
+ in_padata->pa_type = KRB5_PADATA_PK_AS_REP_OLD;
+ }
+ break;
+ default:
+ pkiDebug("unrecognized patype = %d for PKINIT\n",
+ in_padata->pa_type);
+ return EINVAL;
+ }
+
+ if (processing_request) {
+ pkinit_client_profile(context, plgctx, reqctx, request);
+ pkinit_identity_set_prompter(reqctx->idctx, prompter, prompter_data);
+ retval = pkinit_identity_initialize(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, reqctx->idopts,
+ reqctx->idctx, 1, request->client);
+ if (retval) {
+ pkiDebug("pkinit_identity_initialize returned %d (%s)\n",
+ retval, error_message(retval));
+ return retval;
+ }
+ retval = pa_pkinit_gen_req(context, plgctx, reqctx, request,
+ in_padata, out_padata, prompter,
+ prompter_data, gic_opt);
+ } else {
+ /*
+ * Get the enctype of the reply.
+ */
+ retval = (*get_data_proc)(context, rock,
+ krb5plugin_preauth_client_get_etype, &cdata);
+ if (retval) {
+ pkiDebug("get_data_proc returned %d (%s)\n",
+ retval, error_message(retval));
+ return retval;
+ }
+ enctype = *((krb5_enctype *)cdata->data);
+ (*get_data_proc)(context, rock,
+ krb5plugin_preauth_client_free_etype, &cdata);
+ retval = pa_pkinit_parse_rep(context, plgctx, reqctx, request,
+ in_padata, enctype, as_key,
+ encoded_previous_request);
+ }
+
+ pkiDebug("pkinit_client_process: returning %d (%s)\n",
+ retval, error_message(retval));
+ return retval;
+}
+
+krb5_error_code
+pkinit_client_tryagain(krb5_context context,
+ void *plugin_context,
+ void *request_context,
+ krb5_get_init_creds_opt *gic_opt,
+ preauth_get_client_data_proc get_data_proc,
+ struct _krb5_preauth_client_rock *rock,
+ krb5_kdc_req *request,
+ krb5_data *encoded_request_body,
+ krb5_data *encoded_previous_request,
+ krb5_pa_data *in_padata,
+ krb5_error *err_reply,
+ krb5_prompter_fct prompter,
+ void *prompter_data,
+ preauth_get_as_key_proc gak_fct,
+ void *gak_data,
+ krb5_data *salt,
+ krb5_data *s2kparams,
+ krb5_keyblock *as_key,
+ krb5_pa_data ***out_padata)
+{
+ krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ pkinit_context plgctx = (pkinit_context)plugin_context;
+ pkinit_req_context reqctx = (pkinit_req_context)request_context;
+ krb5_typed_data **typed_data = NULL;
+ krb5_data scratch;
+ krb5_external_principal_identifier **krb5_trusted_certifiers = NULL;
+ krb5_algorithm_identifier **algId = NULL;
+ int do_again = 0;
+
+ pkiDebug("pkinit_client_tryagain %p %p %p %p\n",
+ context, plgctx, reqctx, request);
+
+ if (reqctx->pa_type != in_padata->pa_type)
+ return retval;
+
+#ifdef DEBUG_ASN1
+ print_buffer_bin((unsigned char *)err_reply->e_data.data,
+ err_reply->e_data.length, "/tmp/client_edata");
+#endif
+ retval = k5int_decode_krb5_typed_data(&err_reply->e_data, &typed_data);
+ if (retval) {
+ pkiDebug("decode_krb5_typed_data failed\n");
+ goto cleanup;
+ }
+#ifdef DEBUG_ASN1
+ print_buffer_bin(typed_data[0]->data, typed_data[0]->length,
+ "/tmp/client_typed_data");
+#endif
+ OCTETDATA_TO_KRB5DATA(typed_data[0], &scratch);
+
+ switch(typed_data[0]->type) {
+ case TD_TRUSTED_CERTIFIERS:
+ case TD_INVALID_CERTIFICATES:
+ retval = k5int_decode_krb5_td_trusted_certifiers(&scratch,
+ &krb5_trusted_certifiers);
+ if (retval) {
+ pkiDebug("failed to decode sequence of trusted certifiers\n");
+ goto cleanup;
+ }
+ retval = pkinit_process_td_trusted_certifiers(context,
+ plgctx->cryptoctx, reqctx->cryptoctx, reqctx->idctx,
+ krb5_trusted_certifiers, typed_data[0]->type);
+ if (!retval)
+ do_again = 1;
+ break;
+ case TD_DH_PARAMETERS:
+ retval = k5int_decode_krb5_td_dh_parameters(&scratch, &algId);
+ if (retval) {
+ pkiDebug("failed to decode td_dh_parameters\n");
+ goto cleanup;
+ }
+ retval = pkinit_process_td_dh_params(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, reqctx->idctx, algId,
+ &reqctx->opts->dh_size);
+ if (!retval)
+ do_again = 1;
+ break;
+ default:
+ break;
+ }
+
+ if (do_again) {
+ retval = pa_pkinit_gen_req(context, plgctx, reqctx, request, in_padata,
+ out_padata, prompter, prompter_data, gic_opt);
+ if (retval)
+ goto cleanup;
+ }
+
+ retval = 0;
+cleanup:
+ if (krb5_trusted_certifiers != NULL)
+ free_krb5_external_principal_identifier(&krb5_trusted_certifiers);
+
+ if (typed_data != NULL)
+ free_krb5_typed_data(&typed_data);
+
+ if (algId != NULL)
+ free_krb5_algorithm_identifiers(&algId);
+
+ pkiDebug("pkinit_client_tryagain: returning %d (%s)\n",
+ retval, error_message(retval));
+ return retval;
+}
+
+static int
+pkinit_client_get_flags(krb5_context kcontext, krb5_preauthtype patype)
+{
+ return PA_REAL;
+}
+
+static krb5_preauthtype supported_client_pa_types[] = {
+ KRB5_PADATA_PK_AS_REP,
+ KRB5_PADATA_PK_AS_REQ,
+ KRB5_PADATA_PK_AS_REP_OLD,
+ KRB5_PADATA_PK_AS_REQ_OLD,
+ 0
+};
+
+void
+pkinit_client_req_init(krb5_context context,
+ void *plugin_context,
+ void **request_context)
+{
+ krb5_error_code retval = ENOMEM;
+ struct _pkinit_req_context *reqctx = NULL;
+ struct _pkinit_context *plgctx = (struct _pkinit_context *)plugin_context;
+
+ *request_context = NULL;
+
+ reqctx = (struct _pkinit_req_context *) malloc(sizeof(*reqctx));
+ if (reqctx == NULL)
+ return;
+ memset(reqctx, 0, sizeof(*reqctx));
+
+ reqctx->magic = PKINIT_REQ_CTX_MAGIC;
+ reqctx->cryptoctx = NULL;
+ reqctx->opts = NULL;
+ reqctx->idctx = NULL;
+ reqctx->idopts = NULL;
+
+ retval = pkinit_init_req_opts(&reqctx->opts);
+ if (retval)
+ goto cleanup;
+
+ reqctx->opts->require_eku = plgctx->opts->require_eku;
+ reqctx->opts->accept_secondary_eku = plgctx->opts->accept_secondary_eku;
+ reqctx->opts->dh_or_rsa = plgctx->opts->dh_or_rsa;
+ reqctx->opts->allow_upn = plgctx->opts->allow_upn;
+ reqctx->opts->require_crl_checking = plgctx->opts->require_crl_checking;
+
+ retval = pkinit_init_req_crypto(&reqctx->cryptoctx);
+ if (retval)
+ goto cleanup;
+
+ retval = pkinit_init_identity_crypto(&reqctx->idctx);
+ if (retval)
+ goto cleanup;
+
+ retval = pkinit_dup_identity_opts(plgctx->idopts, &reqctx->idopts);
+ if (retval)
+ goto cleanup;
+
+ *request_context = (void *) reqctx;
+ pkiDebug("%s: returning reqctx at %p\n", __FUNCTION__, reqctx);
+
+cleanup:
+ if (retval) {
+ if (reqctx->idctx != NULL)
+ pkinit_fini_identity_crypto(reqctx->idctx);
+ if (reqctx->cryptoctx != NULL)
+ pkinit_fini_req_crypto(reqctx->cryptoctx);
+ if (reqctx->opts != NULL)
+ pkinit_fini_req_opts(reqctx->opts);
+ if (reqctx->idopts != NULL)
+ pkinit_fini_identity_opts(reqctx->idopts);
+ free(reqctx);
+ }
+
+ return;
+}
+
+void
+pkinit_client_req_fini(krb5_context context,
+ void *plugin_context,
+ void *request_context)
+{
+ struct _pkinit_req_context *reqctx =
+ (struct _pkinit_req_context *)request_context;
+
+ pkiDebug("%s: received reqctx at %p\n", __FUNCTION__, reqctx);
+ if (reqctx == NULL)
+ return;
+ if (reqctx->magic != PKINIT_REQ_CTX_MAGIC) {
+ pkiDebug("%s: Bad magic value (%x) in req ctx\n",
+ __FUNCTION__, reqctx->magic);
+ return;
+ }
+ if (reqctx->opts != NULL)
+ pkinit_fini_req_opts(reqctx->opts);
+
+ if (reqctx->cryptoctx != NULL)
+ pkinit_fini_req_crypto(reqctx->cryptoctx);
+
+ if (reqctx->idctx != NULL)
+ pkinit_fini_identity_crypto(reqctx->idctx);
+
+ if (reqctx->idopts != NULL)
+ pkinit_fini_identity_opts(reqctx->idopts);
+
+ free(reqctx);
+ return;
+}
+
+static void
+pkinit_fini_client_profile(krb5_context context, pkinit_context plgctx)
+{
+ /* This should clean up anything allocated in pkinit_init_client_profile */
+}
+
+static krb5_error_code
+pkinit_init_client_profile(krb5_context context, pkinit_context plgctx)
+{
+ return 0;
+}
+
+static int
+pkinit_client_plugin_init(krb5_context context, void **blob)
+{
+ krb5_error_code retval = ENOMEM;
+ struct _pkinit_context *ctx = NULL;
+
+ ctx = (struct _pkinit_context *)calloc(1, sizeof(*ctx));
+ if (ctx == NULL)
+ return ENOMEM;
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->magic = PKINIT_CTX_MAGIC;
+ ctx->opts = NULL;
+ ctx->cryptoctx = NULL;
+ ctx->idopts = NULL;
+
+ retval = pkinit_accessor_init();
+ if (retval)
+ goto errout;
+
+ retval = pkinit_init_plg_opts(&ctx->opts);
+ if (retval)
+ goto errout;
+
+ retval = pkinit_init_plg_crypto(&ctx->cryptoctx);
+ if (retval)
+ goto errout;
+
+ retval = pkinit_init_identity_opts(&ctx->idopts);
+ if (retval)
+ goto errout;
+
+ retval = pkinit_init_client_profile(context, ctx);
+ if (retval)
+ goto errout;
+
+ *blob = ctx;
+
+ pkiDebug("%s: returning plgctx at %p\n", __FUNCTION__, ctx);
+
+errout:
+ if (retval)
+ pkinit_client_plugin_fini(context, ctx);
+
+ return retval;
+}
+
+static void
+pkinit_client_plugin_fini(krb5_context context, void *blob)
+{
+ struct _pkinit_context *ctx = (struct _pkinit_context *)blob;
+
+ if (ctx == NULL || ctx->magic != PKINIT_CTX_MAGIC) {
+ pkiDebug("pkinit_lib_fini: got bad plgctx (%p)!\n", ctx);
+ return;
+ }
+ pkiDebug("%s: got plgctx at %p\n", __FUNCTION__, ctx);
+
+ pkinit_fini_client_profile(context, ctx);
+ pkinit_fini_identity_opts(ctx->idopts);
+ pkinit_fini_plg_crypto(ctx->cryptoctx);
+ pkinit_fini_plg_opts(ctx->opts);
+ free(ctx);
+
+}
+
+static krb5_error_code
+add_string_to_array(krb5_context context, char ***array, const char *addition)
+{
+ char **out = NULL;
+
+ if (*array == NULL) {
+ out = malloc(2 * sizeof(char *));
+ if (out == NULL)
+ return ENOMEM;
+ out[1] = NULL;
+ out[0] = strdup(addition);
+ if (out[0] == NULL) {
+ free(out);
+ return ENOMEM;
+ }
+ } else {
+ int i;
+ char **a = *array;
+ for (i = 0; a[i] != NULL; i++);
+ out = malloc( (i + 2) * sizeof(char *));
+ if (out == NULL)
+ return ENOMEM;
+ for (i = 0; a[i] != NULL; i++) {
+ out[i] = a[i];
+ }
+ out[i++] = strdup(addition);
+ if (out == NULL) {
+ free(out);
+ return ENOMEM;
+ }
+ out[i] = NULL;
+ free(*array);
+ }
+ *array = out;
+
+ return 0;
+}
+static krb5_error_code
+handle_gic_opt(krb5_context context,
+ struct _pkinit_context *plgctx,
+ const char *attr,
+ const char *value)
+{
+ krb5_error_code retval;
+
+ if (strcmp(attr, "X509_user_identity") == 0) {
+ if (plgctx->idopts->identity != NULL) {
+ krb5_set_error_message(context, KRB5_PREAUTH_FAILED,
+ "X509_user_identity can not be given twice\n");
+ return KRB5_PREAUTH_FAILED;
+ }
+ plgctx->idopts->identity = strdup(value);
+ if (plgctx->idopts->identity == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ "Could not duplicate X509_user_identity value\n");
+ return ENOMEM;
+ }
+ } else if (strcmp(attr, "X509_anchors") == 0) {
+ retval = add_string_to_array(context, &plgctx->idopts->anchors, value);
+ if (retval)
+ return retval;
+ } else if (strcmp(attr, "flag_RSA_PROTOCOL") == 0) {
+ if (strcmp(value, "yes") == 0) {
+ pkiDebug("Setting flag to use RSA_PROTOCOL\n");
+ plgctx->opts->dh_or_rsa = RSA_PROTOCOL;
+ }
+ }
+ return 0;
+}
+
+static krb5_error_code
+pkinit_client_gic_opt(krb5_context context,
+ void *plugin_context,
+ krb5_get_init_creds_opt *gic_opt,
+ const char *attr,
+ const char *value)
+{
+ krb5_error_code retval;
+ struct _pkinit_context *plgctx = (struct _pkinit_context *)plugin_context;
+
+ pkiDebug("(pkinit) received '%s' = '%s'\n", attr, value);
+ retval = handle_gic_opt(context, plgctx, attr, value);
+ if (retval)
+ return retval;
+
+ return 0;
+}
+
+struct krb5plugin_preauth_client_ftable_v1 preauthentication_client_1 = {
+ "pkinit", /* name */
+ supported_client_pa_types, /* pa_type_list */
+ NULL, /* enctype_list */
+ pkinit_client_plugin_init, /* (*init) */
+ pkinit_client_plugin_fini, /* (*fini) */
+ pkinit_client_get_flags, /* (*flags) */
+ pkinit_client_req_init, /* (*client_req_init) */
+ pkinit_client_req_fini, /* (*client_req_fini) */
+ pkinit_client_process, /* (*process) */
+ pkinit_client_tryagain, /* (*tryagain) */
+ pkinit_client_gic_opt /* (*gic_opt) */
+};
diff --git a/src/plugins/preauth/pkinit/pkinit_crypto.h b/src/plugins/preauth/pkinit/pkinit_crypto.h
new file mode 100644
index 0000000..779c08c
--- /dev/null
+++ b/src/plugins/preauth/pkinit/pkinit_crypto.h
@@ -0,0 +1,623 @@
+/*
+ * COPYRIGHT (C) 2007
+ * THE REGENTS OF THE UNIVERSITY OF MICHIGAN
+ * ALL RIGHTS RESERVED
+ *
+ * Permission is granted to use, copy, create derivative works
+ * and redistribute this software and such derivative works
+ * for any purpose, so long as the name of The University of
+ * Michigan is not used in any advertising or publicity
+ * pertaining to the use of distribution of this software
+ * without specific, written prior authorization. If the
+ * above copyright notice or any other identification of the
+ * University of Michigan is included in any copy of any
+ * portion of this software, then the disclaimer below must
+ * also be included.
+ *
+ * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
+ * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
+ * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
+ * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
+ * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
+ * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGES.
+ */
+
+/*
+ * This header defines the cryptographic interface
+ */
+
+#ifndef _PKINIT_CRYPTO_H
+#define _PKINIT_CRYPTO_H
+
+#include <krb5/krb5.h>
+#include <krb5/preauth_plugin.h>
+#include <k5-int-pkinit.h>
+#include <profile.h>
+#include "pkinit_accessor.h"
+
+/*
+ * these describe the CMS message types
+ */
+enum cms_msg_types {
+ CMS_SIGN_CLIENT,
+ CMS_SIGN_DRAFT9,
+ CMS_SIGN_SERVER,
+ CMS_ENVEL_SERVER
+};
+
+/*
+ * storage types for identity information
+ */
+#define IDTYPE_FILE 1
+#define IDTYPE_DIR 2
+#define IDTYPE_PKCS11 3
+#define IDTYPE_ENVVAR 4
+#define IDTYPE_PKCS12 5
+
+/*
+ * ca/crl types
+ */
+#define CATYPE_ANCHORS 1
+#define CATYPE_INTERMEDIATES 2
+#define CATYPE_CRLS 3
+
+/*
+ * The following represent Key Usage values that we
+ * may care about in a certificate
+ */
+#define PKINIT_KU_DIGITALSIGNATURE 0x80000000
+#define PKINIT_KU_KEYENCIPHERMENT 0x40000000
+
+/*
+ * The following represent Extended Key Usage oid values
+ * that we may care about in a certificate
+ */
+#define PKINIT_EKU_PKINIT 0x80000000
+#define PKINIT_EKU_MSSCLOGIN 0x40000000
+#define PKINIT_EKU_CLIENTAUTH 0x20000000
+#define PKINIT_EKU_EMAILPROTECTION 0x10000000
+
+
+/* Handle to cert, opaque above crypto interface */
+typedef struct _pkinit_cert_info *pkinit_cert_handle;
+
+/* Handle to cert iteration information, opaque above crypto interface */
+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 */
+ unsigned int eku_bits; /* extended key usage information */
+ krb5_principal *sans; /* Null-terminated array of subject alternative
+ name info (pkinit and ms-upn) */
+} pkinit_cert_matching_data;
+
+/*
+ * Functions to initialize and cleanup crypto contexts
+ */
+krb5_error_code pkinit_init_plg_crypto(pkinit_plg_crypto_context *);
+void pkinit_fini_plg_crypto(pkinit_plg_crypto_context);
+
+krb5_error_code pkinit_init_req_crypto(pkinit_req_crypto_context *);
+void pkinit_fini_req_crypto(pkinit_req_crypto_context);
+
+krb5_error_code pkinit_init_identity_crypto(pkinit_identity_crypto_context *);
+void pkinit_fini_identity_crypto(pkinit_identity_crypto_context);
+
+/*
+ * this function creates a CMS message where eContentType is SignedData
+ */
+krb5_error_code cms_signeddata_create
+ (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 cms_msg_type, /* IN
+ specifies CMS_SIGN_CLIENT for client-side CMS message
+ and CMS_SIGN_SERVER for kdc-side */
+ int include_certchain, /* IN
+ specifies where certificates field in SignedData
+ should contain certificate path */
+ unsigned char *auth_pack, /* IN
+ contains DER encoded AuthPack (CMS_SIGN_CLIENT)
+ or DER encoded DHRepInfo (CMS_SIGN_SERVER) */
+ unsigned int auth_pack_len, /* IN
+ contains length of auth_pack */
+ unsigned char **signed_data, /* OUT
+ for CMS_SIGN_CLIENT receives DER encoded
+ SignedAuthPack (CMS_SIGN_CLIENT) or DER
+ encoded DHInfo (CMS_SIGN_SERVER) */
+ unsigned int *signed_data_len); /* OUT
+ receives length of signed_data */
+
+/*
+ * this function verifies a CMS message where eContentType is SignedData
+ */
+krb5_error_code cms_signeddata_verify
+ (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 cms_msg_type, /* IN
+ specifies CMS_SIGN_CLIENT for client-side
+ CMS message and CMS_SIGN_SERVER for kdc-side */
+ int require_crl_checking, /* IN
+ specifies whether CRL checking should be
+ strictly enforced, i.e. if no CRLs available
+ for the CA then fail verification.
+ note, if the value is 0, crls are still
+ checked if present */
+ unsigned char *signed_data, /* IN
+ contains DER encoded SignedAuthPack (CMS_SIGN_CLIENT)
+ or DER encoded DHInfo (CMS_SIGN_SERVER) */
+ unsigned int signed_data_len, /* IN
+ contains length of signed_data*/
+ unsigned char **auth_pack, /* OUT
+ receives DER encoded AuthPack (CMS_SIGN_CLIENT)
+ or DER encoded DHRepInfo (CMS_SIGN_SERVER)*/
+ unsigned int *auth_pack_len, /* OUT
+ receives length of auth_pack */
+ unsigned char **authz_data, /* OUT
+ receives required authorization data that
+ contains the verified certificate chain
+ (only used by the KDC) */
+ unsigned int *authz_data_len); /* OUT
+ receives length of authz_data */
+
+/*
+ * this function creates a CMS message where eContentType is EnvelopedData
+ */
+krb5_error_code cms_envelopeddata_create
+ (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 */
+ krb5_preauthtype pa_type, /* IN */
+ int include_certchain, /* IN
+ specifies whether the certificates field in
+ SignedData should contain certificate path */
+ unsigned char *key_pack, /* IN
+ contains DER encoded ReplyKeyPack */
+ unsigned int key_pack_len, /* IN
+ contains length of key_pack */
+ unsigned char **envel_data, /* OUT
+ receives DER encoded encKeyPack */
+ unsigned int *envel_data_len); /* OUT
+ receives length of envel_data */
+
+/*
+ * this function creates a CMS message where eContentType is EnvelopedData
+ */
+krb5_error_code cms_envelopeddata_verify
+ (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 */
+ krb5_preauthtype pa_type, /* IN */
+ int require_crl_checking, /* IN
+ specifies whether CRL checking should be
+ strictly enforced */
+ unsigned char *envel_data, /* IN
+ contains DER encoded encKeyPack */
+ unsigned int envel_data_len, /* IN
+ contains length of envel_data */
+ unsigned char **signed_data, /* OUT
+ receives ReplyKeyPack */
+ unsigned int *signed_data_len); /* OUT
+ receives length of signed_data */
+
+/*
+ * this function returns SAN information found in the
+ * received certificate. at least one of pkinit_sans,
+ * upn_sans, or kdc_hostnames must be non-NULL.
+ */
+krb5_error_code crypto_retrieve_cert_sans
+ (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 */
+ krb5_principal **pkinit_sans, /* OUT
+ if non-NULL, a null-terminated array of
+ id-pkinit-san values found in the certificate
+ are returned */
+ krb5_principal **upn_sans, /* OUT
+ if non-NULL, a null-terminated array of
+ id-ms-upn-san values found in the certificate
+ are returned */
+ unsigned char ***kdc_hostname); /* OUT
+ if non-NULL, a null-terminated array of
+ dNSName (hostname) SAN values found in the
+ certificate are returned */
+
+/*
+ * this function checks for acceptable key usage values
+ * in the received certificate.
+ *
+ * when checking a received kdc certificate, it looks for
+ * the kpKdc key usage. if allow_secondary_usage is
+ * non-zero, it will also accept kpServerAuth.
+ *
+ * when checking a received user certificate, it looks for
+ * kpClientAuth key usage. if allow_secondary_usage is
+ * non-zero, it will also accept id-ms-sc-logon EKU.
+ *
+ * this function must also assert that the digitalSignature
+ * key usage is consistent.
+ */
+krb5_error_code crypto_check_cert_eku
+ (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 checking_kdc_cert, /* IN
+ specifies if the received certificate is
+ a KDC certificate (non-zero),
+ or a user certificate (zero) */
+ int allow_secondary_usage, /* IN
+ specifies if the secondary key usage
+ should be accepted or not (see above) */
+ int *eku_valid); /* OUT
+ receives non-zero if an acceptable EKU was found */
+
+/*
+ * this functions takes in generated DH secret key and converts
+ * it in to a kerberos session key. it takes into the account the
+ * enc type and then follows the procedure specified in the RFC p 22.
+ */
+krb5_error_code pkinit_octetstring2key
+ (krb5_context context, /* IN */
+ krb5_enctype etype, /* IN
+ specifies the enc type */
+ unsigned char *key, /* IN
+ contains the DH secret key */
+ unsigned int key_len, /* IN
+ contains length of key */
+ krb5_keyblock * krb5key); /* OUT
+ receives kerberos session key */
+
+/*
+ * this function implements clients first part of the DH protocol.
+ * client selects its DH parameters and pub key
+ */
+krb5_error_code client_create_dh
+ (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 dh_size, /* IN
+ specifies the DH modulous, eg 1024, 2048, or 4096 */
+ unsigned char **dh_paramas, /* OUT
+ contains DER encoded DH params */
+ unsigned int *dh_params_len, /* OUT
+ contains length of dh_parmas */
+ unsigned char **dh_pubkey, /* OUT
+ receives DER encoded DH pub key */
+ unsigned int *dh_pubkey_len); /* OUT
+ receives length of dh_pubkey */
+
+/*
+ * this function completes client's the DH protocol. client
+ * processes received DH pub key from the KDC and computes
+ * the DH secret key
+ */
+krb5_error_code client_process_dh
+ (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 */
+ unsigned char *dh_pubkey, /* IN
+ contains client's DER encoded DH pub key */
+ unsigned int dh_pubkey_len, /* IN
+ contains length of dh_pubkey */
+ unsigned char **dh_session_key, /* OUT
+ receives DH secret key */
+ unsigned int *dh_session_key_len); /* OUT
+ receives length of dh_session_key */
+
+/*
+ * this function implements the KDC first part of the DH protocol.
+ * it decodes the client's DH parameters and pub key and checks
+ * if they are acceptable.
+ */
+krb5_error_code server_check_dh
+ (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 */
+ krb5_octet_data *dh_params, /* IN
+ ???? */
+ int minbits); /* IN
+ the mininum number of key bits acceptable */
+
+/*
+ * this function completes the KDC's DH protocol. The KDC generates
+ * its DH pub key and computes the DH secret key
+ */
+krb5_error_code server_process_dh
+ (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 */
+ unsigned char *received_pubkey, /* IN
+ contains client's DER encoded DH pub key */
+ unsigned int received_pub_len, /* IN
+ contains length of received_pubkey */
+ unsigned char **dh_pubkey, /* OUT
+ receives KDC's DER encoded DH pub key */
+ unsigned int *dh_pubkey_len, /* OUT
+ receives length of dh_pubkey */
+ unsigned char **server_key, /* OUT
+ receives DH secret key */
+ unsigned int *server_key_len); /* OUT
+ receives length of server_key */
+
+/*
+ * this functions takes in crypto specific representation of
+ * supportedCMSTypes and creates a list of
+ * krb5_algorithm_identifier
+ */
+krb5_error_code create_krb5_supportedCMSTypes
+ (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 */
+ krb5_algorithm_identifier ***supportedCMSTypes); /* OUT */
+
+/*
+ * this functions takes in crypto specific representation of
+ * trustedCertifiers and creates a list of
+ * krb5_external_principal_identifier
+ */
+krb5_error_code create_krb5_trustedCertifiers
+ (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 */
+ krb5_external_principal_identifier ***trustedCertifiers); /* OUT */
+
+/*
+ * this functions takes in crypto specific representation of
+ * trustedCas (draft9) and creates a list of krb5_trusted_ca (draft9).
+ * draft9 trustedCAs is a CHOICE. we only support choices for
+ * [1] caName and [2] issuerAndSerial. there is no config
+ * option available to select the choice yet. default = 1.
+ */
+krb5_error_code create_krb5_trustedCas
+ (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 flag, /* IN
+ specifies the tag of the CHOICE */
+ krb5_trusted_ca ***trustedCas); /* OUT */
+
+/*
+ * this functions takes in crypto specific representation of the
+ * KDC's certificate and creates a DER encoded kdcPKId
+ */
+krb5_error_code create_issuerAndSerial
+ (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 */
+ unsigned char **kdcId_buf, /* OUT
+ receives DER encoded kdcPKId */
+ unsigned int *kdcId_len); /* OUT
+ receives length of encoded kdcPKId */
+
+/*
+ * process the values from idopts and obtain the cert(s)
+ * specified by those options, populating the id_cryptoctx.
+ */
+krb5_error_code crypto_load_certs
+ (krb5_context context, /* IN */
+ pkinit_plg_crypto_context plg_cryptoctx, /* IN */
+ pkinit_req_crypto_context req_cryptoctx, /* IN */
+ pkinit_identity_opts *idopts, /* IN */
+ pkinit_identity_crypto_context id_cryptoctx, /* IN/OUT */
+ krb5_principal princ); /* IN */
+
+/*
+ * Free up information held from crypto_load_certs()
+ */
+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);
+
+
+/*
+ * Get number of certificates available after crypto_load_certs()
+ */
+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 */
+
+/*
+ * Get certificate matching information
+ */
+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 */
+
+/*
+ * Free certificate information
+ */
+krb5_error_code crypto_cert_free_matching_data
+ (krb5_context context, /* IN */
+ pkinit_cert_matching_data *data); /* IN */
+
+/*
+ * Make the given certificate "the chosen one"
+ */
+krb5_error_code crypto_cert_select
+ (krb5_context context, /* IN */
+ pkinit_cert_matching_data *data); /* IN */
+
+/*
+ * Select the default certificate as "the chosen one"
+ */
+krb5_error_code crypto_cert_select_default
+ (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 */
+
+/*
+ * process the values from idopts and obtain the anchor or
+ * intermediate certificates, or crls specified by idtype,
+ * catype, and id
+ */
+krb5_error_code crypto_load_cas_and_crls
+ (krb5_context context, /* IN */
+ pkinit_plg_crypto_context plg_cryptoctx, /* IN */
+ pkinit_req_crypto_context req_cryptoctx, /* IN */
+ pkinit_identity_opts *idopts, /* IN */
+ pkinit_identity_crypto_context id_cryptoctx, /* IN/OUT */
+ int idtype, /* IN
+ defines the storage type (file, directory, etc) */
+ int catype, /* IN
+ defines the ca type (anchor, intermediate, crls) */
+ char *id); /* IN
+ defines the location (filename, directory name, etc) */
+
+/*
+ * on the client, obtain the kdc's certificate to include
+ * in a request
+ */
+krb5_error_code pkinit_get_kdc_cert
+ (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/OUT */
+ krb5_principal princ); /* IN */
+
+/*
+ * this function creates edata that contains TD-DH-PARAMETERS
+ */
+krb5_error_code pkinit_create_td_dh_parameters
+ (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_plg_opts *opts, /* IN */
+ krb5_data **edata); /* OUT */
+
+/*
+ * this function processes edata that contains TD-DH-PARAMETERS.
+ * the client processes the received acceptable by KDC DH
+ * parameters and picks the first acceptable to it. it matches
+ * them against the known DH parameters.
+ */
+krb5_error_code pkinit_process_td_dh_params
+ (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 */
+ krb5_algorithm_identifier **algId, /* IN */
+ int *new_dh_size); /* OUT
+ receives the new DH modulus to use in the new AS-REQ */
+
+/*
+ * this function creates edata that contains TD-INVALID-CERTIFICATES
+ */
+krb5_error_code pkinit_create_td_invalid_certificate
+ (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 */
+ krb5_data **edata); /* OUT */
+
+/*
+ * this function creates edata that contains TD-TRUSTED-CERTIFIERS
+ */
+krb5_error_code pkinit_create_td_trusted_certifiers
+ (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 */
+ krb5_data **edata); /* OUT */
+
+/*
+ * this function processes edata that contains either
+ * TD-TRUSTED-CERTIFICATES or TD-INVALID-CERTIFICATES.
+ * current implementation only decodes the received message
+ * but does not act on it
+ */
+krb5_error_code pkinit_process_td_trusted_certifiers
+ (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 */
+ krb5_external_principal_identifier **trustedCertifiers, /* IN */
+ int td_type); /* IN */
+
+/*
+ * this function checks if the received kdcPKId matches
+ * the KDC's certificate
+ */
+krb5_error_code pkinit_check_kdc_pkid
+ (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 */
+ unsigned char *pdid_buf, /* IN
+ contains DER encoded kdcPKId */
+ unsigned int pkid_len, /* IN
+ contains length of pdid_buf */
+ int *valid_kdcPkId); /* OUT
+ 1 if kdcPKId matches, otherwise 0 */
+
+krb5_error_code pkinit_identity_set_prompter
+ (pkinit_identity_crypto_context id_cryptoctx, /* IN */
+ krb5_prompter_fct prompter, /* IN */
+ void *prompter_data); /* IN */
+
+#endif /* _PKINIT_CRYPTO_H */
diff --git a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
new file mode 100644
index 0000000..1859b4f
--- /dev/null
+++ b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
@@ -0,0 +1,5612 @@
+/*
+ * COPYRIGHT (C) 2006,2007
+ * THE REGENTS OF THE UNIVERSITY OF MICHIGAN
+ * ALL RIGHTS RESERVED
+ *
+ * Permission is granted to use, copy, create derivative works
+ * and redistribute this software and such derivative works
+ * for any purpose, so long as the name of The University of
+ * Michigan is not used in any advertising or publicity
+ * pertaining to the use of distribution of this software
+ * without specific, written prior authorization. If the
+ * above copyright notice or any other identification of the
+ * University of Michigan is included in any copy of any
+ * portion of this software, then the disclaimer below must
+ * also be included.
+ *
+ * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
+ * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
+ * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
+ * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
+ * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
+ * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGES.
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <unistd.h>
+#include <dirent.h>
+
+/*
+ * Q: What is this SILLYDECRYPT stuff about?
+ * A: When using the ActivCard Linux pkcs11 library (v2.0.1),
+ * the decrypt function fails. By inserting an extra
+ * function call, which serves nothing but to change the
+ * stack, we were able to work around the issue. If the
+ * ActivCard library is fixed in the future, this
+ * definition and related code can be removed.
+ */
+#define SILLYDECRYPT
+
+#include "pkinit_crypto_openssl.h"
+
+static struct pkcs11_errstrings {
+ short code;
+ char *text;
+} pkcs11_errstrings[] = {
+ { 0x0, "ok" },
+ { 0x1, "cancel" },
+ { 0x2, "host memory" },
+ { 0x3, "slot id invalid" },
+ { 0x5, "general error" },
+ { 0x6, "function failed" },
+ { 0x7, "arguments bad" },
+ { 0x8, "no event" },
+ { 0x9, "need to create threads" },
+ { 0xa, "cant lock" },
+ { 0x10, "attribute read only" },
+ { 0x11, "attribute sensitive" },
+ { 0x12, "attribute type invalid" },
+ { 0x13, "attribute value invalid" },
+ { 0x20, "data invalid" },
+ { 0x21, "data len range" },
+ { 0x30, "device error" },
+ { 0x31, "device memory" },
+ { 0x32, "device removed" },
+ { 0x40, "encrypted data invalid" },
+ { 0x41, "encrypted data len range" },
+ { 0x50, "function canceled" },
+ { 0x51, "function not parallel" },
+ { 0x54, "function not supported" },
+ { 0x60, "key handle invalid" },
+ { 0x62, "key size range" },
+ { 0x63, "key type inconsistent" },
+ { 0x64, "key not needed" },
+ { 0x65, "key changed" },
+ { 0x66, "key needed" },
+ { 0x67, "key indigestible" },
+ { 0x68, "key function not permitted" },
+ { 0x69, "key not wrappable" },
+ { 0x6a, "key unextractable" },
+ { 0x70, "mechanism invalid" },
+ { 0x71, "mechanism param invalid" },
+ { 0x82, "object handle invalid" },
+ { 0x90, "operation active" },
+ { 0x91, "operation not initialized" },
+ { 0xa0, "pin incorrect" },
+ { 0xa1, "pin invalid" },
+ { 0xa2, "pin len range" },
+ { 0xa3, "pin expired" },
+ { 0xa4, "pin locked" },
+ { 0xb0, "session closed" },
+ { 0xb1, "session count" },
+ { 0xb3, "session handle invalid" },
+ { 0xb4, "session parallel not supported" },
+ { 0xb5, "session read only" },
+ { 0xb6, "session exists" },
+ { 0xb7, "session read only exists" },
+ { 0xb8, "session read write so exists" },
+ { 0xc0, "signature invalid" },
+ { 0xc1, "signature len range" },
+ { 0xd0, "template incomplete" },
+ { 0xd1, "template inconsistent" },
+ { 0xe0, "token not present" },
+ { 0xe1, "token not recognized" },
+ { 0xe2, "token write protected" },
+ { 0xf0, "unwrapping key handle invalid" },
+ { 0xf1, "unwrapping key size range" },
+ { 0xf2, "unwrapping key type inconsistent" },
+ { 0x100, "user already logged in" },
+ { 0x101, "user not logged in" },
+ { 0x102, "user pin not initialized" },
+ { 0x103, "user type invalid" },
+ { 0x104, "user another already logged in" },
+ { 0x105, "user too many types" },
+ { 0x110, "wrapped key invalid" },
+ { 0x112, "wrapped key len range" },
+ { 0x113, "wrapping key handle invalid" },
+ { 0x114, "wrapping key size range" },
+ { 0x115, "wrapping key type inconsistent" },
+ { 0x120, "random seed not supported" },
+ { 0x121, "random no rng" },
+ { 0x130, "domain params invalid" },
+ { 0x150, "buffer too small" },
+ { 0x160, "saved state invalid" },
+ { 0x170, "information sensitive" },
+ { 0x180, "state unsaveable" },
+ { 0x190, "cryptoki not initialized" },
+ { 0x191, "cryptoki already initialized" },
+ { 0x1a0, "mutex bad" },
+ { 0x1a1, "mutex not locked" },
+ { 0x200, "function rejected" },
+ { -1, NULL }
+};
+
+/* DH parameters */
+unsigned char pkinit_1024_dhprime[128] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+ 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+ 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+ 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+ 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+ 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+ 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+ 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+ 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+ 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+ 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+ 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+ 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+ 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+unsigned char pkinit_2048_dhprime[2048/8] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+ 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+ 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+ 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+ 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+ 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+ 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+ 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+ 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+ 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+ 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+ 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+ 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+ 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+ 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
+ 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
+ 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+ 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
+ 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
+ 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+ 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
+ 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
+ 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
+ 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
+ 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
+ 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
+ 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
+ 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
+ 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
+ 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+unsigned char pkinit_4096_dhprime[4096/8] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+ 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+ 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+ 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+ 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+ 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+ 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+ 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+ 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+ 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+ 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+ 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+ 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+ 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+ 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
+ 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
+ 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+ 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
+ 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
+ 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+ 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
+ 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
+ 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
+ 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
+ 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
+ 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
+ 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
+ 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
+ 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
+ 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D,
+ 0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33,
+ 0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64,
+ 0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A,
+ 0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D,
+ 0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7,
+ 0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7,
+ 0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D,
+ 0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B,
+ 0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64,
+ 0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64,
+ 0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C,
+ 0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C,
+ 0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2,
+ 0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31,
+ 0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E,
+ 0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01,
+ 0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7,
+ 0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26,
+ 0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C,
+ 0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA,
+ 0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8,
+ 0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9,
+ 0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6,
+ 0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D,
+ 0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2,
+ 0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED,
+ 0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF,
+ 0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C,
+ 0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9,
+ 0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1,
+ 0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F,
+ 0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x06, 0x31, 0x99,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+static int pkinit_oids_refs = 0;
+
+krb5_error_code
+pkinit_init_plg_crypto(pkinit_plg_crypto_context *cryptoctx) {
+
+ krb5_error_code retval = ENOMEM;
+ pkinit_plg_crypto_context ctx = NULL;
+
+ /* initialize openssl routines */
+ openssl_init();
+
+ ctx = (pkinit_plg_crypto_context)malloc(sizeof(*ctx));
+ if (ctx == NULL)
+ goto out;
+ memset(ctx, 0, sizeof(*ctx));
+
+ pkiDebug("%s: initializing openssl crypto context at %p\n",
+ __FUNCTION__, ctx);
+ retval = pkinit_init_pkinit_oids(ctx);
+ if (retval)
+ goto out;
+
+ retval = pkinit_init_dh_params(ctx);
+ if (retval)
+ goto out;
+
+ *cryptoctx = ctx;
+
+out:
+ if (retval && ctx != NULL)
+ pkinit_fini_plg_crypto(ctx);
+
+ return retval;
+}
+
+void
+pkinit_fini_plg_crypto(pkinit_plg_crypto_context cryptoctx)
+{
+ pkiDebug("%s: freeing context at %p\n", __FUNCTION__, cryptoctx);
+
+ if (cryptoctx == NULL)
+ return;
+ pkinit_fini_pkinit_oids(cryptoctx);
+ pkinit_fini_dh_params(cryptoctx);
+ free(cryptoctx);
+}
+
+krb5_error_code
+pkinit_init_identity_crypto(pkinit_identity_crypto_context *idctx)
+{
+ krb5_error_code retval = ENOMEM;
+ pkinit_identity_crypto_context ctx = NULL;
+
+ ctx = (pkinit_identity_crypto_context)malloc(sizeof(*ctx));
+ if (ctx == NULL)
+ goto out;
+ memset(ctx, 0, sizeof(*ctx));
+
+ retval = pkinit_init_certs(ctx);
+ if (retval)
+ goto out;
+
+ retval = pkinit_init_pkcs11(ctx);
+ if (retval)
+ goto out;
+
+ pkiDebug("%s: returning ctx at %p\n", __FUNCTION__, ctx);
+ *idctx = ctx;
+
+out:
+ if (retval) {
+ if (ctx)
+ pkinit_fini_identity_crypto(ctx);
+ }
+
+ return retval;
+}
+
+void
+pkinit_fini_identity_crypto(pkinit_identity_crypto_context idctx)
+{
+ if (idctx == NULL)
+ return;
+
+ pkiDebug("%s: freeing ctx at %p\n", __FUNCTION__, idctx);
+ pkinit_fini_certs(idctx);
+ pkinit_fini_pkcs11(idctx);
+ free(idctx);
+}
+
+krb5_error_code
+pkinit_init_req_crypto(pkinit_req_crypto_context *cryptoctx)
+{
+
+ krb5_error_code retval = ENOMEM;
+ pkinit_req_crypto_context ctx = NULL;
+
+ ctx = (pkinit_req_crypto_context)malloc(sizeof(*ctx));
+ if (ctx == NULL)
+ goto out;
+ memset(ctx, 0, sizeof(*ctx));
+
+ ctx->dh = NULL;
+ ctx->received_cert = NULL;
+
+ *cryptoctx = ctx;
+
+ pkiDebug("%s: returning ctx at %p\n", __FUNCTION__, ctx);
+ retval = 0;
+out:
+ if (retval)
+ free(ctx);
+
+ return retval;
+}
+
+void
+pkinit_fini_req_crypto(pkinit_req_crypto_context req_cryptoctx)
+{
+ if (req_cryptoctx == NULL)
+ return;
+
+ pkiDebug("%s: freeing ctx at %p\n", __FUNCTION__, req_cryptoctx);
+ if (req_cryptoctx->dh != NULL)
+ DH_free(req_cryptoctx->dh);
+ if (req_cryptoctx->received_cert != NULL)
+ X509_free(req_cryptoctx->received_cert);
+
+ free(req_cryptoctx);
+}
+
+static krb5_error_code
+pkinit_init_pkinit_oids(pkinit_plg_crypto_context ctx)
+{
+ krb5_error_code retval = ENOMEM;
+ int nid = 0;
+
+ /*
+ * If OpenSSL already knows about the OID, use the
+ * existing definition. Otherwise, create an OID object.
+ */
+ #define CREATE_OBJ_IF_NEEDED(oid, vn, sn, ln) \
+ nid = OBJ_txt2nid(oid); \
+ if (nid == NID_undef) { \
+ nid = OBJ_create(oid, sn, ln); \
+ if (nid == NID_undef) { \
+ pkiDebug("Error creating oid object for '%s'\n", oid); \
+ goto out; \
+ } \
+ } \
+ ctx->vn = OBJ_nid2obj(nid);
+
+ CREATE_OBJ_IF_NEEDED("1.3.6.1.5.2.2", id_pkinit_san,
+ "id-pkinit-san", "KRB5PrincipalName");
+
+ CREATE_OBJ_IF_NEEDED("1.3.6.1.5.2.3.1", id_pkinit_authData,
+ "id-pkinit-authdata", "PKINIT signedAuthPack");
+
+ CREATE_OBJ_IF_NEEDED("1.3.6.1.5.2.3.2", id_pkinit_DHKeyData,
+ "id-pkinit-DHKeyData", "PKINIT dhSignedData");
+
+ CREATE_OBJ_IF_NEEDED("1.3.6.1.5.2.3.3", id_pkinit_rkeyData,
+ "id-pkinit-rkeyData", "PKINIT encKeyPack");
+
+ CREATE_OBJ_IF_NEEDED("1.3.6.1.5.2.3.4", id_pkinit_KPClientAuth,
+ "id-pkinit-KPClientAuth", "PKINIT Client EKU");
+
+ CREATE_OBJ_IF_NEEDED("1.3.6.1.5.2.3.5", id_pkinit_KPKdc,
+ "id-pkinit-KPKdc", "KDC EKU");
+
+#if 0
+ CREATE_OBJ_IF_NEEDED("1.2.840.113549.1.7.1", id_pkinit_authData9,
+ "id-pkcs7-data", "PKCS7 data");
+#else
+ /* See note in pkinit_pkcs7type2oid() */
+ ctx->id_pkinit_authData9 = NULL;
+#endif
+
+ CREATE_OBJ_IF_NEEDED("1.3.6.1.4.1.311.20.2.2", id_ms_kp_sc_logon,
+ "id-ms-kp-sc-logon EKU", "Microsoft SmartCard Login EKU");
+
+ CREATE_OBJ_IF_NEEDED("1.3.6.1.4.1.311.20.2.3", id_ms_san_upn,
+ "id-ms-san-upn", "Microsoft Universal Principal Name");
+
+ CREATE_OBJ_IF_NEEDED("1.3.6.1.5.5.7.3.1", id_kp_serverAuth,
+ "id-kp-serverAuth EKU", "Server Authentication EKU");
+
+ /* Success */
+ retval = 0;
+ pkinit_oids_refs++;
+
+out:
+ return retval;
+}
+
+static krb5_error_code
+get_cert(char *filename, X509 **retcert)
+{
+ X509 *cert = NULL;
+ BIO *tmp = NULL;
+ int code;
+ krb5_error_code retval;
+
+ if (filename == NULL || retcert == NULL)
+ return EINVAL;
+
+ *retcert = NULL;
+
+ tmp = BIO_new(BIO_s_file());
+ if (tmp == NULL)
+ return ENOMEM;
+
+ code = BIO_read_filename(tmp, filename);
+ if (code == 0) {
+ retval = errno;
+ goto cleanup;
+ }
+
+ cert = (X509 *) PEM_read_bio_X509(tmp, NULL, NULL, NULL);
+ if (cert == NULL) {
+ retval = EIO;
+ pkiDebug("failed to read certificate from %s\n", filename);
+ goto cleanup;
+ }
+ *retcert = cert;
+ retval = 0;
+cleanup:
+ if (tmp != NULL)
+ BIO_free(tmp);
+ return retval;
+}
+
+static krb5_error_code
+get_key(char *filename, EVP_PKEY **retkey)
+{
+ EVP_PKEY *pkey = NULL;
+ BIO *tmp = NULL;
+ int code;
+ krb5_error_code retval;
+
+ if (filename == NULL || retkey == NULL)
+ return EINVAL;
+
+ tmp = BIO_new(BIO_s_file());
+ if (tmp == NULL)
+ return ENOMEM;
+
+ code = BIO_read_filename(tmp, filename);
+ if (code == 0) {
+ retval = errno;
+ goto cleanup;
+ }
+ pkey = (EVP_PKEY *) PEM_read_bio_PrivateKey(tmp, NULL, NULL, NULL);
+ if (pkey == NULL) {
+ retval = EIO;
+ pkiDebug("failed to read private key from %s\n", filename);
+ goto cleanup;
+ }
+ *retkey = pkey;
+ retval = 0;
+cleanup:
+ if (tmp != NULL)
+ BIO_free(tmp);
+ return retval;
+}
+
+static void
+pkinit_fini_pkinit_oids(pkinit_plg_crypto_context ctx)
+{
+ if (ctx == NULL)
+ return;
+
+ /* Only call OBJ_cleanup once! */
+ if (--pkinit_oids_refs == 0)
+ OBJ_cleanup();
+}
+
+static krb5_error_code
+pkinit_init_dh_params(pkinit_plg_crypto_context plgctx)
+{
+ krb5_error_code retval = ENOMEM;
+
+ plgctx->dh_1024 = DH_new();
+ if (plgctx->dh_1024 == NULL)
+ goto cleanup;
+ plgctx->dh_1024->p = BN_bin2bn(pkinit_1024_dhprime,
+ sizeof(pkinit_1024_dhprime), NULL);
+ if ((plgctx->dh_1024->g = BN_new()) == NULL ||
+ (plgctx->dh_1024->q = BN_new()) == NULL)
+ goto cleanup;
+ BN_set_word(plgctx->dh_1024->g, DH_GENERATOR_2);
+ BN_rshift1(plgctx->dh_1024->q, plgctx->dh_1024->p);
+
+ plgctx->dh_2048 = DH_new();
+ if (plgctx->dh_2048 == NULL)
+ goto cleanup;
+ plgctx->dh_2048->p = BN_bin2bn(pkinit_2048_dhprime,
+ sizeof(pkinit_2048_dhprime), NULL);
+ if ((plgctx->dh_2048->g = BN_new()) == NULL ||
+ (plgctx->dh_2048->q = BN_new()) == NULL)
+ goto cleanup;
+ BN_set_word(plgctx->dh_2048->g, DH_GENERATOR_2);
+ BN_rshift1(plgctx->dh_2048->q, plgctx->dh_2048->p);
+
+ plgctx->dh_4096 = DH_new();
+ if (plgctx->dh_4096 == NULL)
+ goto cleanup;
+ plgctx->dh_4096->p = BN_bin2bn(pkinit_4096_dhprime,
+ sizeof(pkinit_4096_dhprime), NULL);
+ if ((plgctx->dh_4096->g = BN_new()) == NULL ||
+ (plgctx->dh_4096->q = BN_new()) == NULL)
+ goto cleanup;
+ BN_set_word(plgctx->dh_4096->g, DH_GENERATOR_2);
+ BN_rshift1(plgctx->dh_4096->q, plgctx->dh_4096->p);
+
+ retval = 0;
+
+cleanup:
+ if (retval)
+ pkinit_fini_dh_params(plgctx);
+
+ return retval;
+}
+
+static void
+pkinit_fini_dh_params(pkinit_plg_crypto_context plgctx)
+{
+ if (plgctx->dh_1024 != NULL)
+ DH_free(plgctx->dh_1024);
+ if (plgctx->dh_2048 != NULL)
+ DH_free(plgctx->dh_2048);
+ if (plgctx->dh_4096 != NULL)
+ DH_free(plgctx->dh_4096);
+
+ plgctx->dh_1024 = plgctx->dh_2048 = plgctx->dh_4096 = NULL;
+}
+
+static krb5_error_code
+pkinit_init_certs(pkinit_identity_crypto_context ctx)
+{
+ krb5_error_code retval = ENOMEM;
+ int i;
+
+ for (i = 0; i < MAX_CREDS_ALLOWED; i++)
+ ctx->creds[i] = NULL;
+ ctx->my_certs = NULL;
+ ctx->cert_index = 0;
+ ctx->my_key = NULL;
+ ctx->trustedCAs = NULL;
+ ctx->intermediateCAs = NULL;
+ ctx->revoked = NULL;
+
+ retval = 0;
+ return retval;
+}
+
+static void
+pkinit_fini_certs(pkinit_identity_crypto_context ctx)
+{
+ if (ctx == NULL)
+ return;
+
+ if (ctx->my_certs != NULL)
+ sk_X509_pop_free(ctx->my_certs, X509_free);
+
+ if (ctx->my_key != NULL)
+ EVP_PKEY_free(ctx->my_key);
+
+ if (ctx->trustedCAs != NULL)
+ sk_X509_pop_free(ctx->trustedCAs, X509_free);
+
+ if (ctx->intermediateCAs != NULL)
+ sk_X509_pop_free(ctx->intermediateCAs, X509_free);
+
+ if (ctx->revoked != NULL)
+ sk_X509_CRL_pop_free(ctx->revoked, X509_CRL_free);
+}
+
+static krb5_error_code
+pkinit_init_pkcs11(pkinit_identity_crypto_context ctx)
+{
+ krb5_error_code retval = ENOMEM;
+
+#ifndef WITHOUT_PKCS11
+ ctx->p11_module_name = strdup(PKCS11_MODNAME);
+ if (ctx->p11_module_name == NULL)
+ return retval;
+ ctx->p11_module = NULL;
+ ctx->slotid = PK_NOSLOT;
+ ctx->token_label = NULL;
+ ctx->cert_label = NULL;
+ ctx->session = CK_INVALID_HANDLE;
+ ctx->p11 = NULL;
+#endif
+ ctx->pkcs11_method = 0;
+
+ retval = 0;
+ return retval;
+}
+
+static void
+pkinit_fini_pkcs11(pkinit_identity_crypto_context ctx)
+{
+#ifndef WITHOUT_PKCS11
+ if (ctx == NULL)
+ return;
+
+ if (ctx->p11 != NULL) {
+ if (ctx->session) {
+ ctx->p11->C_CloseSession(ctx->session);
+ ctx->session = CK_INVALID_HANDLE;
+ }
+ ctx->p11->C_Finalize(NULL_PTR);
+ ctx->p11 = NULL;
+ }
+ if (ctx->p11_module != NULL) {
+ pkinit_C_UnloadModule(ctx->p11_module);
+ ctx->p11_module = NULL;
+ }
+ if (ctx->p11_module_name != NULL)
+ free(ctx->p11_module_name);
+ if (ctx->token_label != NULL)
+ free(ctx->token_label);
+ if (ctx->cert_id != NULL)
+ free(ctx->cert_id);
+ if (ctx->cert_label != NULL)
+ free(ctx->cert_label);
+#endif
+}
+
+krb5_error_code
+pkinit_identity_set_prompter(pkinit_identity_crypto_context id_cryptoctx,
+ krb5_prompter_fct prompter,
+ void *prompter_data)
+{
+ id_cryptoctx->prompter = prompter;
+ id_cryptoctx->prompter_data = prompter_data;
+
+ 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 *data,
+ unsigned int data_len,
+ unsigned char **signed_data,
+ unsigned int *signed_data_len)
+{
+ krb5_error_code retval = ENOMEM;
+ PKCS7 *p7 = NULL, *inner_p7 = NULL;
+ PKCS7_SIGNED *p7s = NULL;
+ PKCS7_SIGNER_INFO *p7si = NULL;
+ unsigned char *p;
+ ASN1_TYPE *pkinit_data = NULL;
+ STACK_OF(X509) * cert_stack = NULL;
+ ASN1_OCTET_STRING *digest_attr = NULL;
+ EVP_MD_CTX ctx, ctx2;
+ const EVP_MD *md_tmp = NULL;
+ unsigned char md_data[EVP_MAX_MD_SIZE], md_data2[EVP_MAX_MD_SIZE];
+ unsigned char *digestInfo_buf = NULL, *abuf = NULL;
+ unsigned int md_len, md_len2, alen, digestInfo_len;
+ STACK_OF(X509_ATTRIBUTE) * sk;
+ unsigned char *sig = NULL;
+ unsigned int sig_len = 0;
+ 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;
+ X509 *cert = NULL;
+ ASN1_OBJECT *oid = NULL;
+
+ /* start creating PKCS7 data */
+ if ((p7 = PKCS7_new()) == NULL)
+ goto cleanup;
+ p7->type = OBJ_nid2obj(NID_pkcs7_signed);
+
+ if ((p7s = PKCS7_SIGNED_new()) == NULL)
+ goto cleanup;
+ p7->d.sign = p7s;
+ if (!ASN1_INTEGER_set(p7s->version, 3))
+ goto cleanup;
+
+ /* create a cert chain that has at least the signer's certificate */
+ if ((cert_stack = sk_X509_new_null()) == NULL)
+ goto cleanup;
+
+ cert = sk_X509_value(id_cryptoctx->my_certs, id_cryptoctx->cert_index);
+ if (!include_certchain) {
+ pkiDebug("only including signer's certificate\n");
+ sk_X509_push(cert_stack, X509_dup(cert));
+ } else {
+ /* create a cert chain */
+ X509_STORE *certstore = NULL;
+ X509_STORE_CTX certctx;
+ STACK_OF(X509) *certstack = NULL;
+ char buf[DN_BUF_LEN];
+ int i = 0, size = 0;
+
+ if ((certstore = X509_STORE_new()) == NULL)
+ goto cleanup;
+ pkiDebug("building certificate chain\n");
+ X509_STORE_set_verify_cb_func(certstore, openssl_callback);
+ X509_STORE_CTX_init(&certctx, certstore, cert,
+ id_cryptoctx->intermediateCAs);
+ X509_STORE_CTX_trusted_stack(&certctx, id_cryptoctx->trustedCAs);
+ if (!X509_verify_cert(&certctx)) {
+ pkiDebug("failed to create a certificate chain: %s\n",
+ X509_verify_cert_error_string(X509_STORE_CTX_get_error(&certctx)));
+ if (!sk_X509_num(id_cryptoctx->trustedCAs))
+ pkiDebug("No trusted CAs found. Check your X509_anchors\n");
+ goto cleanup;
+ }
+ certstack = X509_STORE_CTX_get1_chain(&certctx);
+ size = sk_X509_num(certstack);
+ pkiDebug("size of certificate chain = %d\n", size);
+ for(i = 0; i < size - 1; i++) {
+ X509 *x = sk_X509_value(certstack, i);
+ X509_NAME_oneline(X509_get_subject_name(x), buf, sizeof(buf));
+ pkiDebug("cert #%d: %s\n", i, buf);
+ sk_X509_push(cert_stack, X509_dup(x));
+ }
+ X509_STORE_CTX_cleanup(&certctx);
+ X509_STORE_free(certstore);
+ sk_X509_pop_free(certstack, X509_free);
+ }
+ p7s->cert = cert_stack;
+
+ /* fill-in PKCS7_SIGNER_INFO */
+ if ((p7si = PKCS7_SIGNER_INFO_new()) == NULL)
+ goto cleanup;
+ if (!ASN1_INTEGER_set(p7si->version, 1))
+ goto cleanup;
+ if (!X509_NAME_set(&p7si->issuer_and_serial->issuer,
+ X509_get_issuer_name(cert)))
+ goto cleanup;
+ /* because ASN1_INTEGER_set is used to set a 'long' we will do
+ * things the ugly way. */
+ M_ASN1_INTEGER_free(p7si->issuer_and_serial->serial);
+ if (!(p7si->issuer_and_serial->serial =
+ M_ASN1_INTEGER_dup(X509_get_serialNumber(cert))))
+ goto cleanup;
+
+ /* will not fill-out EVP_PKEY because it's on the smartcard */
+
+ /* Set digest algs */
+ p7si->digest_alg->algorithm = OBJ_nid2obj(NID_sha1);
+
+ if (p7si->digest_alg->parameter != NULL)
+ ASN1_TYPE_free(p7si->digest_alg->parameter);
+ if ((p7si->digest_alg->parameter = ASN1_TYPE_new()) == NULL)
+ goto cleanup;
+ p7si->digest_alg->parameter->type = V_ASN1_NULL;
+
+ /* Set sig algs */
+ if (p7si->digest_enc_alg->parameter != NULL)
+ ASN1_TYPE_free(p7si->digest_enc_alg->parameter);
+ p7si->digest_enc_alg->algorithm = OBJ_nid2obj(NID_sha1WithRSAEncryption);
+ if (!(p7si->digest_enc_alg->parameter = ASN1_TYPE_new()))
+ goto cleanup;
+ p7si->digest_enc_alg->parameter->type = V_ASN1_NULL;
+
+ /* pick the correct oid for the eContentInfo */
+ oid = pkinit_pkcs7type2oid(plg_cryptoctx, cms_msg_type);
+ if (oid == NULL)
+ goto cleanup;
+
+ if (cms_msg_type == CMS_SIGN_DRAFT9) {
+ /* don't include signed attributes for pa-type 15 request */
+ abuf = data;
+ alen = data_len;
+ } else {
+ /* add signed attributes */
+ /* compute sha1 digest over the EncapsulatedContentInfo */
+ EVP_MD_CTX_init(&ctx);
+ EVP_DigestInit_ex(&ctx, EVP_sha1(), NULL);
+ EVP_DigestUpdate(&ctx, data, data_len);
+ md_tmp = EVP_MD_CTX_md(&ctx);
+ EVP_DigestFinal_ex(&ctx, md_data, &md_len);
+
+ /* create a message digest attr */
+ digest_attr = ASN1_OCTET_STRING_new();
+ ASN1_OCTET_STRING_set(digest_attr, md_data, (int)md_len);
+ PKCS7_add_signed_attribute(p7si, NID_pkcs9_messageDigest,
+ V_ASN1_OCTET_STRING, (char *) digest_attr);
+
+ /* create a content-type attr */
+ PKCS7_add_signed_attribute(p7si, NID_pkcs9_contentType,
+ V_ASN1_OBJECT, oid);
+
+ /* create the signature over signed attributes. get DER encoded value */
+ /* This is the place where smartcard signature needs to be calculated */
+ sk = p7si->auth_attr;
+ alen = ASN1_item_i2d((ASN1_VALUE *) sk, &abuf,
+ ASN1_ITEM_rptr(PKCS7_ATTR_SIGN));
+ if (abuf == NULL)
+ goto cleanup2;
+ }
+
+#ifndef WITHOUT_PKCS11
+ /* Some tokens can only do RSAEncryption without sha1 hash */
+ /* to compute sha1WithRSAEncryption, encode the algorithm ID for the hash
+ * function and the hash value into an ASN.1 value of type DigestInfo
+ * DigestInfo::=SEQUENCE {
+ * digestAlgorithm AlgorithmIdentifier,
+ * digest OCTET STRING }
+ */
+ if (id_cryptoctx->pkcs11_method == 1 &&
+ id_cryptoctx->mech == CKM_RSA_PKCS) {
+ pkiDebug("mech = CKM_RSA_PKCS\n");
+ EVP_MD_CTX_init(&ctx2);
+ /* if this is not draft9 request, include digest signed attribute */
+ if (cms_msg_type != CMS_SIGN_DRAFT9)
+ EVP_DigestInit_ex(&ctx2, md_tmp, NULL);
+ else
+ EVP_DigestInit_ex(&ctx2, EVP_sha1(), NULL);
+ EVP_DigestUpdate(&ctx2, abuf, alen);
+ EVP_DigestFinal_ex(&ctx2, md_data2, &md_len2);
+
+ alg = X509_ALGOR_new();
+ if (alg == NULL)
+ goto cleanup2;
+ alg->algorithm = OBJ_nid2obj(NID_sha1);
+ alg->parameter = NULL;
+ alg_len = i2d_X509_ALGOR(alg, NULL);
+ alg_buf = (unsigned char *)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 = (unsigned char *)malloc(digest_len);
+ if (digest_buf == NULL)
+ goto cleanup2;
+
+ digestInfo_len = ASN1_object_size(1, (int)(alg_len + digest_len),
+ V_ASN1_SEQUENCE);
+ y = digestInfo_buf = (unsigned char *)malloc(digestInfo_len);
+ if (digestInfo_buf == NULL)
+ goto cleanup2;
+ ASN1_put_object(&y, 1, (int)(alg_len + digest_len), V_ASN1_SEQUENCE,
+ V_ASN1_UNIVERSAL);
+ i2d_X509_ALGOR(alg, &y);
+ i2d_ASN1_OCTET_STRING(digest, &y);
+#ifdef DEBUG_SIG
+ pkiDebug("signing buffer\n");
+ print_buffer(digestInfo_buf, digestInfo_len);
+ print_buffer_bin(digestInfo_buf, digestInfo_len, "/tmp/pkcs7_tosign");
+#endif
+ retval = pkinit_sign_data(context, id_cryptoctx, digestInfo_buf,
+ digestInfo_len, &sig, &sig_len);
+ } else
+#endif
+ {
+ pkiDebug("mech = %s\n",
+ id_cryptoctx->pkcs11_method == 1 ? "CKM_SHA1_RSA_PKCS" : "FS");
+ retval = pkinit_sign_data(context, id_cryptoctx, abuf, alen,
+ &sig, &sig_len);
+ }
+#ifdef DEBUG_SIG
+ print_buffer(sig, sig_len);
+#endif
+ if (cms_msg_type != CMS_SIGN_DRAFT9)
+ free(abuf);
+ if (retval)
+ goto cleanup2;
+
+ /* Add signature */
+ if (!ASN1_STRING_set(p7si->enc_digest, (unsigned char *) sig,
+ (int)sig_len)) {
+ unsigned long err = ERR_peek_error();
+ retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ krb5_set_error_message(context, retval, "%s\n",
+ ERR_error_string(err, NULL));
+ pkiDebug("failed to add a signed digest attribute\n");
+ goto cleanup2;
+ }
+ /* adder signer_info to pkcs7 signed */
+ if (!PKCS7_add_signer(p7, p7si))
+ goto cleanup2;
+
+ /* start on adding data to the pkcs7 signed */
+ if ((inner_p7 = PKCS7_new()) == NULL)
+ goto cleanup2;
+ if ((pkinit_data = ASN1_TYPE_new()) == NULL)
+ goto cleanup2;
+ pkinit_data->type = V_ASN1_OCTET_STRING;
+ if ((pkinit_data->value.octet_string = ASN1_OCTET_STRING_new()) == NULL)
+ goto cleanup2;
+ if (!ASN1_OCTET_STRING_set(pkinit_data->value.octet_string, data,
+ (int)data_len)) {
+ unsigned long err = ERR_peek_error();
+ retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ krb5_set_error_message(context, retval, "%s\n",
+ ERR_error_string(err, NULL));
+ pkiDebug("failed to add pkcs7 data\n");
+ goto cleanup2;
+ }
+
+ if (!PKCS7_set0_type_other(inner_p7, OBJ_obj2nid(oid), pkinit_data))
+ goto cleanup2;
+
+ if (p7s->contents != NULL)
+ PKCS7_free(p7s->contents);
+ p7s->contents = inner_p7;
+
+ *signed_data_len = i2d_PKCS7(p7, NULL);
+ if (!(*signed_data_len)) {
+ unsigned long err = ERR_peek_error();
+ retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ krb5_set_error_message(context, retval, "%s\n",
+ ERR_error_string(err, NULL));
+ pkiDebug("failed to der encode pkcs7\n");
+ goto cleanup2;
+ }
+ if ((p = *signed_data =
+ (unsigned char *) malloc((size_t)*signed_data_len)) == NULL)
+ goto cleanup2;
+
+ /* DER encode PKCS7 data */
+ retval = i2d_PKCS7(p7, &p);
+ if (!retval) {
+ unsigned long err = ERR_peek_error();
+ retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ krb5_set_error_message(context, retval, "%s\n",
+ ERR_error_string(err, NULL));
+ pkiDebug("failed to der encode pkcs7\n");
+ goto cleanup2;
+ }
+ retval = 0;
+
+#ifdef DEBUG_ASN1
+ if (cms_msg_type == CMS_SIGN_CLIENT) {
+ print_buffer_bin(*signed_data, *signed_data_len,
+ "/tmp/client_pkcs7_signeddata");
+ } else {
+ if (cms_msg_type == CMS_SIGN_SERVER) {
+ print_buffer_bin(*signed_data, *signed_data_len,
+ "/tmp/kdc_pkcs7_signeddata");
+ } else {
+ print_buffer_bin(*signed_data, *signed_data_len,
+ "/tmp/draft9_pkcs7_signeddata");
+ }
+ }
+#endif
+
+ cleanup2:
+ if (cms_msg_type != CMS_SIGN_DRAFT9)
+ EVP_MD_CTX_cleanup(&ctx);
+#ifndef WITHOUT_PKCS11
+ if (id_cryptoctx->pkcs11_method == 1 &&
+ id_cryptoctx->mech == CKM_RSA_PKCS) {
+ EVP_MD_CTX_cleanup(&ctx2);
+ if (digest_buf != NULL)
+ free(digest_buf);
+ if (digestInfo_buf != NULL)
+ free(digestInfo_buf);
+ if (alg_buf != NULL)
+ free(alg_buf);
+ if (digest != NULL)
+ ASN1_OCTET_STRING_free(digest);
+ }
+#endif
+ if (alg != NULL)
+ X509_ALGOR_free(alg);
+ cleanup:
+ if (p7 != NULL)
+ PKCS7_free(p7);
+ if (sig != NULL)
+ free(sig);
+
+ return retval;
+}
+
+krb5_error_code
+cms_signeddata_verify(krb5_context context,
+ pkinit_plg_crypto_context plgctx,
+ pkinit_req_crypto_context reqctx,
+ pkinit_identity_crypto_context idctx,
+ int cms_msg_type,
+ int require_crl_checking,
+ unsigned char *signed_data,
+ unsigned int signed_data_len,
+ unsigned char **data,
+ unsigned int *data_len,
+ unsigned char **authz_data,
+ unsigned int *authz_data_len)
+{
+ krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ PKCS7 *p7 = NULL;
+ BIO *out = NULL;
+ int flags = PKCS7_NOVERIFY, i = 0;
+ unsigned int vflags = 0, size = 0;
+ const unsigned char *p = signed_data;
+ STACK_OF(PKCS7_SIGNER_INFO) *si_sk = NULL;
+ PKCS7_SIGNER_INFO *si = NULL;
+ X509 *x = NULL;
+ X509_STORE *store = NULL;
+ X509_STORE_CTX cert_ctx;
+ STACK_OF(X509) *intermediateCAs = NULL;
+ STACK_OF(X509_CRL) *revoked = NULL;
+ STACK_OF(X509) *verified_chain = NULL;
+ ASN1_OBJECT *oid = NULL;
+ krb5_external_principal_identifier **krb5_verified_chain = NULL;
+ krb5_data *authz = NULL;
+ char buf[DN_BUF_LEN];
+
+#ifdef DEBUG_ASN1
+ print_buffer_bin(signed_data, signed_data_len,
+ "/tmp/client_received_pkcs7_signeddata");
+#endif
+
+ /* Do this early enough to create the shadow OID for pkcs7-data if needed */
+ oid = pkinit_pkcs7type2oid(plgctx, cms_msg_type);
+ if (oid == NULL)
+ goto cleanup;
+
+ /* decode received PKCS7 message */
+ if ((p7 = d2i_PKCS7(NULL, &p, (int)signed_data_len)) == NULL) {
+ unsigned long err = ERR_peek_error();
+ krb5_set_error_message(context, retval, "%s\n",
+ ERR_error_string(err, NULL));
+ pkiDebug("%s: failed to decode message: %s\n",
+ __FUNCTION__, ERR_error_string(err, NULL));
+ goto cleanup;
+ }
+
+ /* verify that the received message is PKCS7 SignedData message */
+ if (OBJ_obj2nid(p7->type) != NID_pkcs7_signed) {
+ pkiDebug("Expected id-signedData PKCS7 msg (received type = %d)\n",
+ OBJ_obj2nid(p7->type));
+ krb5_set_error_message(context, retval, "wrong oid\n");
+ goto cleanup;
+ }
+
+ /* setup to verify X509 certificate used to sign PKCS7 message */
+ if (!(store = X509_STORE_new()))
+ goto cleanup;
+
+ /* check if we are inforcing CRL checking */
+ vflags = X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL;
+ if (require_crl_checking)
+ X509_STORE_set_verify_cb_func(store, openssl_callback);
+ else
+ X509_STORE_set_verify_cb_func(store, openssl_callback_ignore_crls);
+ X509_STORE_set_flags(store, vflags);
+
+ /* get the signer's information from the PKCS7 message */
+ if ((si_sk = PKCS7_get_signer_info(p7)) == NULL)
+ goto cleanup;
+ if ((si = sk_PKCS7_SIGNER_INFO_value(si_sk, 0)) == NULL)
+ goto cleanup;
+ if ((x = PKCS7_cert_from_signer_info(p7, si)) == NULL)
+ goto cleanup;
+
+ /* create available CRL information (get local CRLs and include CRLs
+ * received in the PKCS7 message
+ */
+ if (idctx->revoked == NULL)
+ revoked = p7->d.sign->crl;
+ else if (p7->d.sign->crl == NULL)
+ revoked = idctx->revoked;
+ else {
+ size = sk_X509_CRL_num(idctx->revoked);
+ revoked = sk_X509_CRL_new_null();
+ for (i = 0; i < size; i++)
+ sk_X509_CRL_push(revoked, sk_X509_CRL_value(idctx->revoked, i));
+ size = sk_X509_num(p7->d.sign->crl);
+ for (i = 0; i < size; i++)
+ sk_X509_CRL_push(revoked, sk_X509_CRL_value(p7->d.sign->crl, i));
+ }
+
+ /* create available intermediate CAs chains (get local intermediateCAs and
+ * include the CA chain received in the PKCS7 message
+ */
+ if (idctx->intermediateCAs == NULL)
+ intermediateCAs = p7->d.sign->cert;
+ else if (p7->d.sign->cert == NULL)
+ intermediateCAs = idctx->intermediateCAs;
+ else {
+ size = sk_X509_num(idctx->intermediateCAs);
+ intermediateCAs = sk_X509_new_null();
+ for (i = 0; i < size; i++) {
+ sk_X509_push(intermediateCAs,
+ sk_X509_value(idctx->intermediateCAs, i));
+ }
+ size = sk_X509_num(p7->d.sign->cert);
+ for (i = 0; i < size; i++) {
+ sk_X509_push(intermediateCAs, sk_X509_value(p7->d.sign->cert, i));
+ }
+ }
+
+ /* initialize x509 context with the received certificate and
+ * trusted and intermediate CA chains and CRLs
+ */
+ if (!X509_STORE_CTX_init(&cert_ctx, store, x, intermediateCAs))
+ goto cleanup;
+
+ X509_STORE_CTX_set0_crls(&cert_ctx, revoked);
+
+ /* add trusted CAs certificates for cert verification */
+ if (idctx->trustedCAs != NULL)
+ X509_STORE_CTX_trusted_stack(&cert_ctx, idctx->trustedCAs);
+ else {
+ pkiDebug("unable to find any trusted CAs\n");
+ goto cleanup;
+ }
+#ifdef DEBUG_CERTCHAIN
+ if (intermediateCAs != NULL) {
+ size = sk_X509_num(intermediateCAs);
+ pkiDebug("untrusted cert chain of size %d\n", size);
+ for (i = 0; i < size; i++) {
+ X509_NAME_oneline(X509_get_subject_name(
+ sk_X509_value(intermediateCAs, i)), buf, sizeof(buf));
+ pkiDebug("cert #%d: %s\n", i, buf);
+ }
+ }
+ if (idctx->trustedCAs != NULL) {
+ size = sk_X509_num(idctx->trustedCAs);
+ pkiDebug("trusted cert chain of size %d\n", size);
+ for (i = 0; i < size; i++) {
+ X509_NAME_oneline(X509_get_subject_name(
+ sk_X509_value(idctx->trustedCAs, i)), buf, sizeof(buf));
+ pkiDebug("cert #%d: %s\n", i, buf);
+ }
+ }
+ if (revoked != NULL) {
+ size = sk_X509_CRL_num(revoked);
+ pkiDebug("CRL chain of size %d\n", size);
+ for (i = 0; i < size; i++) {
+ X509_CRL *crl = sk_X509_CRL_value(revoked, i);
+ X509_NAME_oneline(X509_CRL_get_issuer(crl), buf, sizeof(buf));
+ pkiDebug("crls by CA #%d: %s\n", i , buf);
+ }
+ }
+#endif
+
+ i = X509_verify_cert(&cert_ctx);
+ if (i <= 0) {
+ int j = X509_STORE_CTX_get_error(&cert_ctx);
+
+ reqctx->received_cert = X509_dup(cert_ctx.current_cert);
+ switch(j) {
+ case X509_V_ERR_CERT_REVOKED:
+ retval = KRB5KDC_ERR_REVOKED_CERTIFICATE;
+ break;
+ case X509_V_ERR_UNABLE_TO_GET_CRL:
+ retval = KRB5KDC_ERR_REVOCATION_STATUS_UNKNOWN;
+ break;
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
+ retval = KRB5KDC_ERR_CANT_VERIFY_CERTIFICATE;
+ break;
+ default:
+ retval = KRB5KDC_ERR_INVALID_CERTIFICATE;
+ }
+ X509_NAME_oneline(X509_get_subject_name(
+ reqctx->received_cert), buf, sizeof(buf));
+ pkiDebug("problem with cert DN = %s (error=%d) %s\n", buf, j,
+ X509_verify_cert_error_string(j));
+ krb5_set_error_message(context, retval, "%s\n",
+ X509_verify_cert_error_string(j));
+#ifdef DEBUG_CERTCHAIN
+ size = sk_X509_num(p7->d.sign->cert);
+ pkiDebug("received cert chain of size %d\n", size);
+ for (j = 0; j < size; j++) {
+ X509 *tmp_cert = sk_X509_value(p7->d.sign->cert, j);
+ X509_NAME_oneline(X509_get_subject_name(tmp_cert), buf, sizeof(buf));
+ pkiDebug("cert #%d: %s\n", j, buf);
+ }
+#endif
+ } else {
+ /* retrieve verified certificate chain */
+ if (cms_msg_type == CMS_SIGN_CLIENT || cms_msg_type == CMS_SIGN_DRAFT9)
+ verified_chain = X509_STORE_CTX_get1_chain(&cert_ctx);
+ }
+ X509_STORE_CTX_cleanup(&cert_ctx);
+ if (i <= 0)
+ goto cleanup;
+
+ out = BIO_new(BIO_s_mem());
+ if (cms_msg_type == CMS_SIGN_DRAFT9)
+ flags |= PKCS7_NOATTR;
+ if (PKCS7_verify(p7, NULL, store, NULL, out, flags)) {
+ int valid_oid = 0;
+
+ if (!OBJ_cmp(p7->d.sign->contents->type, oid))
+ valid_oid = 1;
+ else if (cms_msg_type == CMS_SIGN_DRAFT9) {
+ /*
+ * Various implementations of the pa-type 15 request use
+ * different OIDS. We check that the returned object
+ * has any of the acceptable OIDs
+ */
+ ASN1_OBJECT *client_oid = NULL, *server_oid = NULL, *rsa_oid = NULL;
+ client_oid = pkinit_pkcs7type2oid(plgctx, CMS_SIGN_CLIENT);
+ server_oid = pkinit_pkcs7type2oid(plgctx, CMS_SIGN_SERVER);
+ rsa_oid = pkinit_pkcs7type2oid(plgctx, CMS_ENVEL_SERVER);
+ if (!OBJ_cmp(p7->d.sign->contents->type, client_oid) ||
+ !OBJ_cmp(p7->d.sign->contents->type, server_oid) ||
+ !OBJ_cmp(p7->d.sign->contents->type, rsa_oid))
+ valid_oid = 1;
+ }
+
+ if (valid_oid)
+ pkiDebug("PKCS7 Verification successful\n");
+ else {
+ pkiDebug("wrong oid in eContentType\n");
+ print_buffer(p7->d.sign->contents->type->data,
+ (unsigned int)p7->d.sign->contents->type->length);
+ retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ krb5_set_error_message(context, retval, "wrong oid\n");
+ goto cleanup;
+ }
+ }
+ else {
+ unsigned long err = ERR_peek_error();
+ switch(ERR_GET_REASON(err)) {
+ case PKCS7_R_DIGEST_FAILURE:
+ retval = KRB5KDC_ERR_DIGEST_IN_SIGNED_DATA_NOT_ACCEPTED;
+ break;
+ case PKCS7_R_SIGNATURE_FAILURE:
+ default:
+ retval = KRB5KDC_ERR_INVALID_SIG;
+ }
+ pkiDebug("PKCS7 Verification failure\n");
+ krb5_set_error_message(context, retval, "%s\n",
+ ERR_error_string(err, NULL));
+ goto cleanup;
+ }
+
+ /* transfer the data from PKCS7 message into return buffer */
+ for (size = 0;;) {
+ if ((*data = realloc(*data, size + 1024 * 10)) == NULL)
+ goto cleanup;
+ i = BIO_read(out, &((*data)[size]), 1024 * 10);
+ if (i <= 0)
+ break;
+ else
+ size += i;
+ }
+ *data_len = size;
+
+ reqctx->received_cert = X509_dup(x);
+
+ /* generate authorization data */
+ if (cms_msg_type == CMS_SIGN_CLIENT || cms_msg_type == CMS_SIGN_DRAFT9) {
+
+ if (authz_data == NULL || authz_data_len == NULL)
+ goto out;
+
+ *authz_data = NULL;
+ retval = create_identifiers_from_stack(verified_chain,
+ &krb5_verified_chain);
+ if (retval) {
+ pkiDebug("create_identifiers_from_stack failed\n");
+ goto cleanup;
+ }
+
+ retval = k5int_encode_krb5_td_trusted_certifiers((const krb5_external_principal_identifier **)krb5_verified_chain, &authz);
+ if (retval) {
+ pkiDebug("encode_krb5_td_trusted_certifiers failed\n");
+ goto cleanup;
+ }
+#ifdef DEBUG_ASN1
+ print_buffer_bin((unsigned char *)authz->data, authz->length,
+ "/tmp/kdc_ad_initial_verified_cas");
+#endif
+ *authz_data = (unsigned char *)malloc(authz->length);
+ if (*authz_data == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ memcpy(*authz_data, authz->data, authz->length);
+ *authz_data_len = authz->length;
+ }
+ out:
+ retval = 0;
+
+ cleanup:
+ if (out != NULL)
+ BIO_free(out);
+ if (store != NULL)
+ X509_STORE_free(store);
+ if (p7 != NULL) {
+ if (idctx->intermediateCAs != NULL && p7->d.sign->cert)
+ sk_X509_free(intermediateCAs);
+ if (idctx->revoked != NULL && p7->d.sign->crl)
+ sk_X509_CRL_free(revoked);
+ PKCS7_free(p7);
+ }
+ if (verified_chain != NULL)
+ sk_X509_pop_free(verified_chain, X509_free);
+ if (krb5_verified_chain != NULL)
+ free_krb5_external_principal_identifier(&krb5_verified_chain);
+ if (authz != NULL)
+ krb5_free_data(context, authz);
+
+ return retval;
+}
+
+krb5_error_code
+cms_envelopeddata_create(krb5_context context,
+ pkinit_plg_crypto_context plgctx,
+ pkinit_req_crypto_context reqctx,
+ pkinit_identity_crypto_context idctx,
+ krb5_preauthtype pa_type,
+ int include_certchain,
+ unsigned char *key_pack,
+ unsigned int key_pack_len,
+ unsigned char **out,
+ unsigned int *out_len)
+{
+
+ krb5_error_code retval = ENOMEM;
+ PKCS7 *p7 = NULL;
+ BIO *in = NULL;
+ unsigned char *p = NULL, *signed_data = NULL, *enc_data = NULL;
+ int signed_data_len = 0, enc_data_len = 0, flags = PKCS7_BINARY;
+ STACK_OF(X509) *encerts = NULL;
+ const EVP_CIPHER *cipher = NULL;
+ int cms_msg_type;
+
+ /* create the PKCS7 SignedData portion of the PKCS7 EnvelopedData */
+ switch ((int)pa_type) {
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ case KRB5_PADATA_PK_AS_REP_OLD:
+ cms_msg_type = CMS_SIGN_DRAFT9;
+ break;
+ case KRB5_PADATA_PK_AS_REQ:
+ cms_msg_type = CMS_ENVEL_SERVER;
+ break;
+ default:
+ goto cleanup;
+ }
+
+ retval = cms_signeddata_create(context, plgctx, reqctx, idctx,
+ cms_msg_type, include_certchain, key_pack, key_pack_len,
+ &signed_data, (unsigned int *)&signed_data_len);
+ if (retval) {
+ pkiDebug("failed to create pkcs7 signed data\n");
+ goto cleanup;
+ }
+
+ /* check we have client's certificate */
+ if (reqctx->received_cert == NULL) {
+ retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ goto cleanup;
+ }
+ encerts = sk_X509_new_null();
+ sk_X509_push(encerts, reqctx->received_cert);
+
+ cipher = EVP_des_ede3_cbc();
+ in = BIO_new(BIO_s_mem());
+ switch (pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ prepare_enc_data(signed_data, signed_data_len, &enc_data,
+ &enc_data_len);
+ retval = BIO_write(in, enc_data, enc_data_len);
+ if (retval != enc_data_len) {
+ pkiDebug("BIO_write only wrote %d\n", retval);
+ goto cleanup;
+ }
+ break;
+ case KRB5_PADATA_PK_AS_REP_OLD:
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ retval = BIO_write(in, signed_data, signed_data_len);
+ if (retval != signed_data_len) {
+ pkiDebug("BIO_write only wrote %d\n", retval);
+ goto cleanup;
+ }
+ break;
+ default:
+ retval = -1;
+ goto cleanup;
+ }
+
+ p7 = PKCS7_encrypt(encerts, in, cipher, flags);
+ if (p7 == NULL) {
+ pkiDebug("failed to encrypt PKCS7 object\n");
+ retval = -1;
+ goto cleanup;
+ }
+ switch (pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ p7->d.enveloped->enc_data->content_type =
+ OBJ_nid2obj(NID_pkcs7_signed);
+ break;
+ case KRB5_PADATA_PK_AS_REP_OLD:
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ p7->d.enveloped->enc_data->content_type =
+ OBJ_nid2obj(NID_pkcs7_data);
+ break;
+ break;
+ break;
+break;
+ }
+
+ *out_len = i2d_PKCS7(p7, NULL);
+ if (!*out_len || (p = *out = (unsigned char *)malloc(*out_len)) == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ retval = i2d_PKCS7(p7, &p);
+ if (!retval) {
+ pkiDebug("unable to write pkcs7 object\n");
+ goto cleanup;
+ }
+ retval = 0;
+
+#ifdef DEBUG_ASN1
+ print_buffer_bin(*out, *out_len, "/tmp/kdc_enveloped_data");
+#endif
+
+cleanup:
+ if (p7 != NULL)
+ PKCS7_free(p7);
+ if (in != NULL)
+ BIO_free(in);
+ if (signed_data != NULL)
+ free(signed_data);
+ if (enc_data != NULL)
+ free(enc_data);
+ if (encerts != NULL)
+ sk_X509_free(encerts);
+
+ return retval;
+}
+
+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 *enveloped_data,
+ unsigned int enveloped_data_len,
+ unsigned char **data,
+ unsigned int *data_len)
+{
+ krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ PKCS7 *p7 = NULL;
+ BIO *out = NULL;
+ int i = 0;
+ unsigned int size = 0;
+ const unsigned char *p = enveloped_data;
+ unsigned int tmp_buf_len = 0, tmp_buf2_len = 0, vfy_buf_len = 0;
+ unsigned char *tmp_buf = NULL, *tmp_buf2 = NULL, *vfy_buf = NULL;
+ int msg_type = 0;
+
+#ifdef DEBUG_ASN1
+ print_buffer_bin(enveloped_data, enveloped_data_len,
+ "/tmp/client_envelopeddata");
+#endif
+ /* decode received PKCS7 message */
+ if ((p7 = d2i_PKCS7(NULL, &p, (int)enveloped_data_len)) == NULL) {
+ unsigned long err = ERR_peek_error();
+ pkiDebug("failed to decode pkcs7\n");
+ krb5_set_error_message(context, retval, "%s\n",
+ ERR_error_string(err, NULL));
+ goto cleanup;
+ }
+
+ /* verify that the received message is PKCS7 EnvelopedData message */
+ if (OBJ_obj2nid(p7->type) != NID_pkcs7_enveloped) {
+ pkiDebug("Expected id-enveloped PKCS7 msg (received type = %d)\n",
+ OBJ_obj2nid(p7->type));
+ krb5_set_error_message(context, retval, "wrong oid\n");
+ goto cleanup;
+ }
+
+ /* decrypt received PKCS7 message */
+ out = BIO_new(BIO_s_mem());
+ if (pkcs7_decrypt(context, id_cryptoctx, p7, out)) {
+ pkiDebug("PKCS7 decryption successful\n");
+ } else {
+ unsigned long err = ERR_peek_error();
+ if (err != 0)
+ krb5_set_error_message(context, retval, "%s\n",
+ ERR_error_string(err, NULL));
+ pkiDebug("PKCS7 decryption failed\n");
+ goto cleanup;
+ }
+
+ /* transfer the decoded PKCS7 SignedData message into a separate buffer */
+ for (;;) {
+ if ((tmp_buf = realloc(tmp_buf, size + 1024 * 10)) == NULL)
+ goto cleanup;
+ i = BIO_read(out, &(tmp_buf[size]), 1024 * 10);
+ if (i <= 0)
+ break;
+ else
+ size += i;
+ }
+ tmp_buf_len = size;
+
+#ifdef DEBUG_ASN1
+ print_buffer_bin(tmp_buf, tmp_buf_len, "/tmp/client_enc_keypack");
+#endif
+ /* verify PKCS7 SignedData message */
+ switch (pa_type) {
+ case KRB5_PADATA_PK_AS_REP:
+ msg_type = CMS_ENVEL_SERVER;
+
+ break;
+ case KRB5_PADATA_PK_AS_REP_OLD:
+ msg_type = CMS_SIGN_DRAFT9;
+ break;
+ default:
+ pkiDebug("%s: unrecognized pa_type = %d\n", __FUNCTION__, pa_type);
+ retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ goto cleanup;
+ }
+ /*
+ * If this is the RFC style, wrap the signed data to make
+ * decoding easier in the verify routine.
+ * For draft9-compatible, we don't do anything because it
+ * is already wrapped.
+ */
+#ifdef LONGHORN_BETA_COMPAT
+ /*
+ * The Longhorn server returns the expected RFC-style data, but
+ * it is missing the sequence tag and length, so it requires
+ * special processing when wrapping.
+ * This will hopefully be fixed before the final release and
+ * this can all be removed.
+ */
+ if (msg_type == CMS_ENVEL_SERVER || longhorn == 1) {
+ retval = wrap_signeddata(tmp_buf, tmp_buf_len,
+ &tmp_buf2, &tmp_buf2_len, longhorn);
+ if (retval) {
+ pkiDebug("failed to encode signeddata\n");
+ goto cleanup;
+ }
+ vfy_buf = tmp_buf2;
+ vfy_buf_len = tmp_buf2_len;
+
+ } else {
+ vfy_buf = tmp_buf;
+ vfy_buf_len = tmp_buf_len;
+ }
+#else
+ if (msg_type == CMS_ENVEL_SERVER) {
+ retval = wrap_signeddata(tmp_buf, tmp_buf_len,
+ &tmp_buf2, &tmp_buf2_len);
+ if (retval) {
+ pkiDebug("failed to encode signeddata\n");
+ goto cleanup;
+ }
+ vfy_buf = tmp_buf2;
+ vfy_buf_len = tmp_buf2_len;
+
+ } else {
+ vfy_buf = tmp_buf;
+ vfy_buf_len = tmp_buf_len;
+ }
+#endif
+
+#ifdef DEBUG_ASN1
+ print_buffer_bin(vfy_buf, vfy_buf_len, "/tmp/client_enc_keypack2");
+#endif
+
+ retval = cms_signeddata_verify(context, plg_cryptoctx, req_cryptoctx,
+ id_cryptoctx, msg_type,
+ require_crl_checking,
+ vfy_buf, vfy_buf_len,
+ data, data_len, NULL, NULL);
+
+ if (!retval)
+ pkiDebug("PKCS7 Verification Success\n");
+ else {
+ pkiDebug("PKCS7 Verification Failure\n");
+ goto cleanup;
+ }
+
+ retval = 0;
+
+ cleanup:
+
+ if (p7 != NULL)
+ PKCS7_free(p7);
+ if (out != NULL)
+ BIO_free(out);
+ if (tmp_buf != NULL)
+ free(tmp_buf);
+ if (tmp_buf2 != NULL)
+ free(tmp_buf2);
+
+ return retval;
+}
+
+static krb5_error_code
+crypto_retrieve_X509_sans(krb5_context context,
+ pkinit_plg_crypto_context plgctx,
+ pkinit_req_crypto_context reqctx,
+ X509 *cert,
+ krb5_principal **princs_ret,
+ krb5_principal **upn_ret,
+ unsigned char ***dns_ret)
+{
+ krb5_error_code retval = EINVAL;
+ char buf[DN_BUF_LEN];
+ int p = 0, u = 0, d = 0;
+ krb5_principal *princs = NULL;
+ krb5_principal *upns = NULL;
+ unsigned char **dnss = NULL;
+ int i, num_found = 0;
+
+ if (princs_ret == NULL && upn_ret == NULL && dns_ret == NULL) {
+ pkiDebug("%s: nowhere to return any values!\n", __FUNCTION__);
+ return retval;
+ }
+
+ if (cert == NULL) {
+ pkiDebug("%s: no certificate!\n", __FUNCTION__);
+ return retval;
+ }
+
+ X509_NAME_oneline(X509_get_subject_name(cert),
+ buf, sizeof(buf));
+ pkiDebug("%s: looking for SANs in cert = %s\n", __FUNCTION__, buf);
+
+ if ((i = 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;
+
+ if (!(ext = X509_get_ext(cert, i)) || !(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);
+
+ /* 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 (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 };
+
+ 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");
+#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) {
+ 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++;
+ }
+ } else {
+ pkiDebug("%s: unrecognized othername oid in SAN\n",
+ __FUNCTION__);
+ continue;
+ }
+
+ break;
+ case GEN_DNS:
+ if (dnss != NULL) {
+ 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);
+ }
+ }
+ sk_GENERAL_NAME_pop_free(ialt, GENERAL_NAME_free);
+ }
+
+ retval = 0;
+ if (princs)
+ *princs_ret = princs;
+ if (upns)
+ *upn_ret = upns;
+ if (dnss)
+ *dns_ret = dnss;
+
+ 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);
+ }
+ }
+ return retval;
+}
+
+krb5_error_code
+crypto_retrieve_cert_sans(krb5_context context,
+ pkinit_plg_crypto_context plgctx,
+ pkinit_req_crypto_context reqctx,
+ pkinit_identity_crypto_context idctx,
+ krb5_principal **princs_ret,
+ krb5_principal **upn_ret,
+ unsigned char ***dns_ret)
+{
+ krb5_error_code retval = EINVAL;
+
+ if (reqctx->received_cert == NULL) {
+ pkiDebug("%s: No certificate!\n", __FUNCTION__);
+ return retval;
+ }
+
+ return crypto_retrieve_X509_sans(context, plgctx, reqctx,
+ reqctx->received_cert, princs_ret,
+ upn_ret, dns_ret);
+}
+
+krb5_error_code
+crypto_check_cert_eku(krb5_context context,
+ pkinit_plg_crypto_context plgctx,
+ pkinit_req_crypto_context reqctx,
+ pkinit_identity_crypto_context idctx,
+ int checking_kdc_cert,
+ int allow_secondary_usage,
+ int *valid_eku)
+{
+ char buf[DN_BUF_LEN];
+ int found_eku = 0;
+ krb5_error_code retval = EINVAL;
+ int i;
+
+ *valid_eku = 0;
+ if (reqctx->received_cert == NULL)
+ goto cleanup;
+
+ 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) {
+ EXTENDED_KEY_USAGE *extusage;
+
+ extusage = X509_get_ext_d2i(reqctx->received_cert, NID_ext_key_usage,
+ NULL, NULL);
+ if (extusage) {
+ pkiDebug("%s: found eku info in the cert\n", __FUNCTION__);
+ for (i = 0; found_eku == 0 && i < sk_ASN1_OBJECT_num(extusage); i++) {
+ ASN1_OBJECT *tmp_oid;
+
+ tmp_oid = sk_ASN1_OBJECT_value(extusage, i);
+ pkiDebug("%s: checking eku %d of %d, allow_secondary = %d\n",
+ __FUNCTION__, i+1, sk_ASN1_OBJECT_num(extusage),
+ allow_secondary_usage);
+ if (checking_kdc_cert) {
+ if ((OBJ_cmp(tmp_oid, plgctx->id_pkinit_KPKdc) == 0)
+ || (allow_secondary_usage
+ && OBJ_cmp(tmp_oid, plgctx->id_kp_serverAuth) == 0))
+ found_eku = 1;
+ } else {
+ if ((OBJ_cmp(tmp_oid, plgctx->id_pkinit_KPClientAuth) == 0)
+ || (allow_secondary_usage
+ && OBJ_cmp(tmp_oid, plgctx->id_ms_kp_sc_logon) == 0))
+ found_eku = 1;
+ }
+ }
+ }
+ EXTENDED_KEY_USAGE_free(extusage);
+
+ if (found_eku) {
+ ASN1_BIT_STRING *usage = NULL;
+ pkiDebug("%s: found acceptable EKU, checking for digitalSignature\n", __FUNCTION__);
+
+ /* check that digitalSignature KeyUsage is present */
+ if ((usage = X509_get_ext_d2i(reqctx->received_cert,
+ NID_key_usage, NULL, NULL))) {
+
+ if (!ku_reject(reqctx->received_cert,
+ X509v3_KU_DIGITAL_SIGNATURE)) {
+ pkiDebug("%s: found digitalSignature KU\n",
+ __FUNCTION__);
+ *valid_eku = 1;
+ } else
+ pkiDebug("%s: didn't find digitalSignature KU\n",
+ __FUNCTION__);
+ }
+ ASN1_BIT_STRING_free(usage);
+ }
+ }
+ retval = 0;
+cleanup:
+ pkiDebug("%s: returning retval %d, valid_eku %d\n",
+ __FUNCTION__, retval, *valid_eku);
+ return retval;
+}
+
+krb5_error_code
+pkinit_octetstring2key(krb5_context context,
+ krb5_enctype etype,
+ unsigned char *key,
+ unsigned int dh_key_len,
+ krb5_keyblock * key_block)
+{
+ krb5_error_code retval;
+ unsigned char *buf = NULL;
+ unsigned char md[SHA_DIGEST_LENGTH];
+ unsigned char counter;
+ size_t keybytes, keylength, offset;
+ krb5_data random_data;
+
+
+ if ((buf = (unsigned char *) malloc(dh_key_len)) == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ memset(buf, 0, dh_key_len);
+
+ counter = 0;
+ offset = 0;
+ do {
+ SHA_CTX c;
+
+ SHA1_Init(&c);
+ SHA1_Update(&c, &counter, 1);
+ SHA1_Update(&c, key, dh_key_len);
+ SHA1_Final(md, &c);
+
+ if (dh_key_len - offset < sizeof(md))
+ memcpy(buf + offset, md, dh_key_len - offset);
+ else
+ memcpy(buf + offset, md, sizeof(md));
+
+ offset += sizeof(md);
+ counter++;
+ } while (offset < dh_key_len);
+
+ key_block->magic = 0;
+ key_block->enctype = etype;
+
+ retval = krb5_c_keylengths(context, etype, &keybytes, &keylength);
+ if (retval)
+ goto cleanup;
+
+ key_block->length = keylength;
+ key_block->contents = calloc(keylength, sizeof(unsigned char *));
+ if (key_block->contents == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+
+ random_data.length = keybytes;
+ random_data.data = (char *)buf;
+
+ retval = krb5_c_random_to_key(context, etype, &random_data, key_block);
+
+ cleanup:
+ if (buf != NULL)
+ free(buf);
+ if (retval && key_block->contents != NULL && key_block->length != 0) {
+ memset(key_block->contents, 0, key_block->length);
+ key_block->length = 0;
+ }
+
+ return retval;
+}
+
+krb5_error_code
+client_create_dh(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ int dh_size,
+ unsigned char **dh_params,
+ unsigned int *dh_params_len,
+ unsigned char **dh_pubkey,
+ unsigned int *dh_pubkey_len)
+{
+ krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ unsigned char *buf = NULL;
+ int dh_err = 0;
+ ASN1_INTEGER *pub_key = NULL;
+
+ if (cryptoctx->dh == NULL) {
+ if ((cryptoctx->dh = DH_new()) == NULL)
+ goto cleanup;
+ if ((cryptoctx->dh->g = BN_new()) == NULL ||
+ (cryptoctx->dh->q = BN_new()) == NULL)
+ goto cleanup;
+
+ switch(dh_size) {
+ case 1024:
+ pkiDebug("client uses 1024 DH keys\n");
+ cryptoctx->dh->p = get_rfc2409_prime_1024(NULL);
+ break;
+ case 2048:
+ pkiDebug("client uses 2048 DH keys\n");
+ cryptoctx->dh->p = BN_bin2bn(pkinit_2048_dhprime,
+ sizeof(pkinit_2048_dhprime), NULL);
+ break;
+ case 4096:
+ pkiDebug("client uses 4096 DH keys\n");
+ cryptoctx->dh->p = BN_bin2bn(pkinit_4096_dhprime,
+ sizeof(pkinit_4096_dhprime), NULL);
+ break;
+ default:
+ goto cleanup;
+ }
+
+ BN_set_word((cryptoctx->dh->g), DH_GENERATOR_2);
+ BN_rshift1(cryptoctx->dh->q, cryptoctx->dh->p);
+ }
+
+ DH_generate_key(cryptoctx->dh);
+ DH_check(cryptoctx->dh, &dh_err);
+ if (dh_err != 0) {
+ pkiDebug("Warning: dh_check failed with %d\n", dh_err);
+ if (dh_err & DH_CHECK_P_NOT_PRIME)
+ pkiDebug("p value is not prime\n");
+ if (dh_err & DH_CHECK_P_NOT_SAFE_PRIME)
+ pkiDebug("p value is not a safe prime\n");
+ if (dh_err & DH_UNABLE_TO_CHECK_GENERATOR)
+ pkiDebug("unable to check the generator value\n");
+ if (dh_err & DH_NOT_SUITABLE_GENERATOR)
+ pkiDebug("the g value is not a generator\n");
+ }
+#ifdef DEBUG_DH
+ print_dh(cryptoctx->dh, "client's DH params\n");
+ print_pubkey(cryptoctx->dh->pub_key, "client's pub_key=");
+#endif
+
+ DH_check_pub_key(cryptoctx->dh, cryptoctx->dh->pub_key, &dh_err);
+ if (dh_err != 0) {
+ pkiDebug("dh_check_pub_key failed with %d\n", dh_err);
+ goto cleanup;
+ }
+
+ /* pack DHparams */
+ /* aglo: usually we could just call i2d_DHparams to encode DH params
+ * however, PKINIT requires RFC3279 encoding and openssl does pkcs#3.
+ */
+ retval = pkinit_encode_dh_params(cryptoctx->dh->p, cryptoctx->dh->g,
+ cryptoctx->dh->q, dh_params, dh_params_len);
+ if (retval)
+ goto cleanup;
+
+ /* pack DH public key */
+ /* Diffie-Hellman public key must be ASN1 encoded as an INTEGER; this
+ * encoding shall be used as the contents (the value) of the
+ * subjectPublicKey component (a BIT STRING) of the SubjectPublicKeyInfo
+ * data element
+ */
+ if ((pub_key = BN_to_ASN1_INTEGER(cryptoctx->dh->pub_key, NULL)) == NULL)
+ goto cleanup;
+ *dh_pubkey_len = i2d_ASN1_INTEGER(pub_key, NULL);
+ if ((buf = *dh_pubkey = (unsigned char *)
+ malloc((size_t) *dh_pubkey_len)) == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ i2d_ASN1_INTEGER(pub_key, &buf);
+
+ if (pub_key != NULL)
+ ASN1_INTEGER_free(pub_key);
+
+ retval = 0;
+ return retval;
+
+ cleanup:
+ if (cryptoctx->dh != NULL)
+ DH_free(cryptoctx->dh);
+ cryptoctx->dh = NULL;
+ if (*dh_params != NULL)
+ free(*dh_params);
+ *dh_params = NULL;
+ if (*dh_pubkey != NULL)
+ free(*dh_pubkey);
+ *dh_pubkey = NULL;
+ if (pub_key != NULL)
+ ASN1_INTEGER_free(pub_key);
+
+ return retval;
+}
+
+krb5_error_code
+client_process_dh(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ unsigned char *subjectPublicKey_data,
+ unsigned int subjectPublicKey_length,
+ unsigned char **client_key,
+ unsigned int *client_key_len)
+{
+ krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ BIGNUM *server_pub_key = NULL;
+ ASN1_INTEGER *pub_key = NULL;
+ const unsigned char *p = NULL;
+ unsigned char *data = NULL;
+ long data_len;
+
+ /* decode subjectPublicKey (retrieve INTEGER from OCTET_STRING) */
+
+ if (der_decode_data(subjectPublicKey_data, (long)subjectPublicKey_length,
+ &data, &data_len) != 0) {
+ pkiDebug("failed to decode subjectPublicKey\n");
+ retval = -1;
+ goto cleanup;
+ }
+
+ *client_key_len = DH_size(cryptoctx->dh);
+ if ((*client_key = (unsigned char *)
+ malloc((size_t) *client_key_len)) == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ p = data;
+ if ((pub_key = d2i_ASN1_INTEGER(NULL, &p, data_len)) == NULL)
+ goto cleanup;
+ if ((server_pub_key = ASN1_INTEGER_to_BN(pub_key, NULL)) == NULL)
+ goto cleanup;
+
+ DH_compute_key(*client_key, server_pub_key, cryptoctx->dh);
+#ifdef DEBUG_DH
+ print_pubkey(server_pub_key, "server's pub_key=");
+ pkiDebug("client secret key (%d)= ", *client_key_len);
+ print_buffer(*client_key, *client_key_len);
+#endif
+
+ retval = 0;
+ if (server_pub_key != NULL)
+ BN_free(server_pub_key);
+ if (pub_key != NULL)
+ ASN1_INTEGER_free(pub_key);
+ if (data != NULL)
+ free (data);
+
+ return retval;
+
+ cleanup:
+ if (*client_key != NULL)
+ free(*client_key);
+ *client_key = NULL;
+ if (pub_key != NULL)
+ ASN1_INTEGER_free(pub_key);
+ if (data != NULL)
+ free (data);
+
+ return retval;
+}
+
+krb5_error_code
+server_check_dh(krb5_context context,
+ pkinit_plg_crypto_context cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ krb5_octet_data *dh_params,
+ int minbits)
+{
+ DH *dh = NULL;
+ unsigned char *tmp = NULL;
+ int dh_prime_bits;
+ krb5_error_code retval = KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
+
+ tmp = dh_params->data;
+ dh = DH_new();
+ dh = pkinit_decode_dh_params(&dh, &tmp, dh_params->length);
+ if (dh == NULL) {
+ pkiDebug("failed to decode dhparams\n");
+ goto cleanup;
+ }
+
+ /* KDC SHOULD check to see if the key parameters satisfy its policy */
+ dh_prime_bits = BN_num_bits(dh->p);
+ if (minbits && dh_prime_bits < minbits) {
+ pkiDebug("client sent dh params with %d bits, we require %d\n",
+ dh_prime_bits, minbits);
+ goto cleanup;
+ }
+
+ /* check dhparams is group 2 */
+ if (pkinit_check_dh_params(cryptoctx->dh_1024->p,
+ dh->p, dh->g, dh->q) == 0) {
+ retval = 0;
+ goto cleanup;
+ }
+
+ /* check dhparams is group 14 */
+ if (pkinit_check_dh_params(cryptoctx->dh_2048->p,
+ dh->p, dh->g, dh->q) == 0) {
+ retval = 0;
+ goto cleanup;
+ }
+
+ /* check dhparams is group 16 */
+ if (pkinit_check_dh_params(cryptoctx->dh_4096->p,
+ dh->p, dh->g, dh->q) == 0) {
+ retval = 0;
+ goto cleanup;
+ }
+
+ cleanup:
+ if (retval == 0)
+ req_cryptoctx->dh = dh;
+ else
+ DH_free(dh);
+
+ return retval;
+}
+
+/* kdc's dh function */
+krb5_error_code
+server_process_dh(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ unsigned char *data,
+ unsigned int data_len,
+ unsigned char **dh_pubkey,
+ unsigned int *dh_pubkey_len,
+ unsigned char **server_key,
+ unsigned int *server_key_len)
+{
+ krb5_error_code retval = ENOMEM;
+ DH *dh = NULL, *dh_server = NULL;
+ unsigned char *p = NULL;
+ ASN1_INTEGER *pub_key = NULL;
+
+ /* get client's received DH parameters that we saved in server_check_dh */
+ dh = cryptoctx->dh;
+
+ dh_server = DH_new();
+ if (dh_server == NULL)
+ goto cleanup;
+ dh_server->p = BN_dup(dh->p);
+ dh_server->g = BN_dup(dh->g);
+ dh_server->q = BN_dup(dh->q);
+
+ /* decode client's public key */
+ p = data;
+ pub_key = d2i_ASN1_INTEGER(NULL, (const unsigned char **)&p, (int)data_len);
+ if (pub_key == NULL)
+ goto cleanup;
+ dh->pub_key = ASN1_INTEGER_to_BN(pub_key, NULL);
+ if (dh->pub_key == NULL)
+ goto cleanup;
+ ASN1_INTEGER_free(pub_key);
+
+ if (!DH_generate_key(dh_server))
+ goto cleanup;
+
+ /* generate DH session key */
+ *server_key_len = DH_size(dh_server);
+ if ((*server_key = (unsigned char *) malloc((size_t)*server_key_len)) == NULL)
+ goto cleanup;
+ DH_compute_key(*server_key, dh->pub_key, dh_server);
+
+#ifdef DEBUG_DH
+ print_dh(dh_server, "client&server's DH params\n");
+ print_pubkey(dh->pub_key, "client's pub_key=");
+ print_pubkey(dh_server->pub_key, "server's pub_key=");
+ pkiDebug("server secret key=");
+ print_buffer(*server_key, *server_key_len);
+#endif
+
+ /* KDC reply */
+ /* pack DH public key */
+ /* Diffie-Hellman public key must be ASN1 encoded as an INTEGER; this
+ * encoding shall be used as the contents (the value) of the
+ * subjectPublicKey component (a BIT STRING) of the SubjectPublicKeyInfo
+ * data element
+ */
+ if ((pub_key = BN_to_ASN1_INTEGER(dh_server->pub_key, NULL)) == NULL)
+ goto cleanup;
+ *dh_pubkey_len = i2d_ASN1_INTEGER(pub_key, NULL);
+ if ((p = *dh_pubkey = (unsigned char *) malloc((size_t)*dh_pubkey_len)) == NULL)
+ goto cleanup;
+ i2d_ASN1_INTEGER(pub_key, &p);
+ if (pub_key != NULL)
+ ASN1_INTEGER_free(pub_key);
+
+ retval = 0;
+
+ if (dh_server != NULL)
+ DH_free(dh_server);
+ return retval;
+
+ cleanup:
+ if (dh_server != NULL)
+ DH_free(dh_server);
+ if (*dh_pubkey != NULL)
+ free(*dh_pubkey);
+ if (*server_key != NULL)
+ free(*server_key);
+
+ return retval;
+}
+
+static void
+openssl_init()
+{
+ static int did_init = 0;
+
+ if (!did_init) {
+ /* initialize openssl routines */
+ CRYPTO_malloc_init();
+ ERR_load_crypto_strings();
+ OpenSSL_add_all_algorithms();
+ did_init++;
+ }
+}
+
+static krb5_error_code
+pkinit_encode_dh_params(BIGNUM *p, BIGNUM *g, BIGNUM *q,
+ unsigned char **buf, unsigned int *buf_len)
+{
+ krb5_error_code retval = ENOMEM;
+ int bufsize = 0, r = 0;
+ unsigned char *tmp = NULL;
+ ASN1_INTEGER *ap = NULL, *ag = NULL, *aq = NULL;
+
+ if ((ap = BN_to_ASN1_INTEGER(p, NULL)) == NULL)
+ goto cleanup;
+ if ((ag = BN_to_ASN1_INTEGER(g, NULL)) == NULL)
+ goto cleanup;
+ if ((aq = BN_to_ASN1_INTEGER(q, NULL)) == NULL)
+ goto cleanup;
+ bufsize = i2d_ASN1_INTEGER(ap, NULL);
+ bufsize += i2d_ASN1_INTEGER(ag, NULL);
+ bufsize += i2d_ASN1_INTEGER(aq, NULL);
+
+ r = ASN1_object_size(1, bufsize, V_ASN1_SEQUENCE);
+
+ tmp = *buf = (unsigned char *)malloc((size_t) r);
+ if (tmp == NULL)
+ goto cleanup;
+
+ ASN1_put_object(&tmp, 1, bufsize, V_ASN1_SEQUENCE, V_ASN1_UNIVERSAL);
+
+ i2d_ASN1_INTEGER(ap, &tmp);
+ i2d_ASN1_INTEGER(ag, &tmp);
+ i2d_ASN1_INTEGER(aq, &tmp);
+
+ *buf_len = r;
+
+ retval = 0;
+
+cleanup:
+ if (ap != NULL)
+ ASN1_INTEGER_free(ap);
+ if (ag != NULL)
+ ASN1_INTEGER_free(ag);
+ if (aq != NULL)
+ ASN1_INTEGER_free(aq);
+
+ return retval;
+}
+
+static DH *
+pkinit_decode_dh_params(DH ** a, unsigned char **pp, unsigned int len)
+{
+ ASN1_INTEGER ai, *aip = NULL;
+ long length = (long) len;
+
+ M_ASN1_D2I_vars(a, DH *, DH_new);
+
+ M_ASN1_D2I_Init();
+ M_ASN1_D2I_start_sequence();
+ aip = &ai;
+ ai.data = NULL;
+ ai.length = 0;
+ M_ASN1_D2I_get_x(ASN1_INTEGER, aip, d2i_ASN1_INTEGER);
+ if (aip == NULL)
+ return NULL;
+ else {
+ (*a)->p = ASN1_INTEGER_to_BN(aip, NULL);
+ if ((*a)->p == NULL)
+ return NULL;
+ if (ai.data != NULL) {
+ OPENSSL_free(ai.data);
+ ai.data = NULL;
+ ai.length = 0;
+ }
+ }
+ M_ASN1_D2I_get_x(ASN1_INTEGER, aip, d2i_ASN1_INTEGER);
+ if (aip == NULL)
+ return NULL;
+ else {
+ (*a)->g = ASN1_INTEGER_to_BN(aip, NULL);
+ if ((*a)->g == NULL)
+ return NULL;
+ if (ai.data != NULL) {
+ OPENSSL_free(ai.data);
+ ai.data = NULL;
+ ai.length = 0;
+ }
+
+ }
+ M_ASN1_D2I_get_x(ASN1_INTEGER, aip, d2i_ASN1_INTEGER);
+ if (aip == NULL)
+ return NULL;
+ else {
+ (*a)->q = ASN1_INTEGER_to_BN(aip, NULL);
+ if ((*a)->q == NULL)
+ return NULL;
+ if (ai.data != NULL) {
+ OPENSSL_free(ai.data);
+ ai.data = NULL;
+ ai.length = 0;
+ }
+
+ }
+ M_ASN1_D2I_end_sequence();
+ M_ASN1_D2I_Finish(a, DH_free, 0);
+
+}
+
+static krb5_error_code
+pkinit_create_sequence_of_principal_identifiers(
+ krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ int type,
+ krb5_data **out_data)
+{
+ krb5_error_code retval = KRB5KRB_ERR_GENERIC;
+ krb5_external_principal_identifier **krb5_trusted_certifiers = NULL;
+ krb5_data *td_certifiers = NULL, *data = NULL;
+ krb5_typed_data **typed_data = NULL;
+
+ switch(type) {
+ case TD_TRUSTED_CERTIFIERS:
+ retval = create_krb5_trustedCertifiers(context, plg_cryptoctx,
+ req_cryptoctx, id_cryptoctx, &krb5_trusted_certifiers);
+ if (retval) {
+ pkiDebug("create_krb5_trustedCertifiers failed\n");
+ goto cleanup;
+ }
+ break;
+ case TD_INVALID_CERTIFICATES:
+ retval = create_krb5_invalidCertificates(context, plg_cryptoctx,
+ req_cryptoctx, id_cryptoctx, &krb5_trusted_certifiers);
+ if (retval) {
+ pkiDebug("create_krb5_invalidCertificates failed\n");
+ goto cleanup;
+ }
+ break;
+ default:
+ retval = -1;
+ goto cleanup;
+ }
+
+ retval = k5int_encode_krb5_td_trusted_certifiers((const krb5_external_principal_identifier **)krb5_trusted_certifiers, &td_certifiers);
+ if (retval) {
+ pkiDebug("encode_krb5_td_trusted_certifiers failed\n");
+ goto cleanup;
+ }
+#ifdef DEBUG_ASN1
+ print_buffer_bin((unsigned char *)td_certifiers->data,
+ td_certifiers->length, "/tmp/kdc_td_certifiers");
+#endif
+ typed_data = malloc (2 * sizeof(krb5_typed_data *));
+ if (typed_data == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ typed_data[1] = NULL;
+ init_krb5_typed_data(&typed_data[0]);
+ if (typed_data[0] == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ typed_data[0]->type = type;
+ typed_data[0]->length = td_certifiers->length;
+ typed_data[0]->data = (unsigned char *)td_certifiers->data;
+ retval = k5int_encode_krb5_typed_data((const krb5_typed_data **)typed_data,
+ &data);
+ if (retval) {
+ pkiDebug("encode_krb5_typed_data failed\n");
+ goto cleanup;
+ }
+#ifdef DEBUG_ASN1
+ print_buffer_bin((unsigned char *)data->data, data->length,
+ "/tmp/kdc_edata");
+#endif
+ *out_data = (krb5_data *)malloc(sizeof(krb5_data));
+ (*out_data)->length = data->length;
+ (*out_data)->data = (char *)malloc(data->length);
+ memcpy((*out_data)->data, data->data, data->length);
+
+ retval = 0;
+
+cleanup:
+ if (krb5_trusted_certifiers != NULL)
+ free_krb5_external_principal_identifier(&krb5_trusted_certifiers);
+
+ if (data != NULL) {
+ if (data->data != NULL)
+ free(data->data);
+ free(data);
+ }
+
+ if (td_certifiers != NULL)
+ free(td_certifiers);
+
+ if (typed_data != NULL)
+ free_krb5_typed_data(&typed_data);
+
+ return retval;
+}
+
+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_data **out_data)
+{
+ krb5_error_code retval = KRB5KRB_ERR_GENERIC;
+
+ retval = pkinit_create_sequence_of_principal_identifiers(context,
+ plg_cryptoctx, req_cryptoctx, id_cryptoctx,
+ TD_TRUSTED_CERTIFIERS, out_data);
+
+ return retval;
+}
+
+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_data **out_data)
+{
+ krb5_error_code retval = KRB5KRB_ERR_GENERIC;
+
+ retval = pkinit_create_sequence_of_principal_identifiers(context,
+ plg_cryptoctx, req_cryptoctx, id_cryptoctx,
+ TD_INVALID_CERTIFICATES, out_data);
+
+ return retval;
+}
+
+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_data **out_data)
+{
+ krb5_error_code retval = ENOMEM;
+ unsigned int buf1_len = 0, buf2_len = 0, buf3_len = 0, i = 0;
+ unsigned char *buf1 = NULL, *buf2 = NULL, *buf3 = NULL;
+ krb5_typed_data **typed_data = NULL;
+ krb5_data *data = NULL, *encoded_algId = NULL;
+ krb5_algorithm_identifier **algId = NULL;
+
+ if (opts->dh_min_bits > 4096)
+ goto cleanup;
+
+ if (opts->dh_min_bits <= 1024) {
+ retval = pkinit_encode_dh_params(plg_cryptoctx->dh_1024->p,
+ plg_cryptoctx->dh_1024->g, plg_cryptoctx->dh_1024->q,
+ &buf1, &buf1_len);
+ if (retval)
+ goto cleanup;
+ }
+ if (opts->dh_min_bits <= 2048) {
+ retval = pkinit_encode_dh_params(plg_cryptoctx->dh_2048->p,
+ plg_cryptoctx->dh_2048->g, plg_cryptoctx->dh_2048->q,
+ &buf2, &buf2_len);
+ if (retval)
+ goto cleanup;
+ }
+ retval = pkinit_encode_dh_params(plg_cryptoctx->dh_4096->p,
+ plg_cryptoctx->dh_4096->g, plg_cryptoctx->dh_4096->q,
+ &buf3, &buf3_len);
+ if (retval)
+ goto cleanup;
+
+ if (opts->dh_min_bits <= 1024) {
+ algId = malloc(4 * sizeof(krb5_algorithm_identifier *));
+ if (algId == NULL)
+ goto cleanup;
+ algId[3] = NULL;
+ algId[0] = (krb5_algorithm_identifier *)malloc(sizeof(krb5_algorithm_identifier));
+ if (algId[0] == NULL)
+ goto cleanup;
+ algId[0]->parameters.data = (unsigned char *)malloc(buf2_len);
+ if (algId[0]->parameters.data == NULL)
+ goto cleanup;
+ memcpy(algId[0]->parameters.data, buf2, buf2_len);
+ algId[0]->parameters.length = buf2_len;
+ algId[0]->algorithm = dh_oid;
+
+ algId[1] = (krb5_algorithm_identifier *)malloc(sizeof(krb5_algorithm_identifier));
+ if (algId[1] == NULL)
+ goto cleanup;
+ algId[1]->parameters.data = (unsigned char *)malloc(buf3_len);
+ if (algId[1]->parameters.data == NULL)
+ goto cleanup;
+ memcpy(algId[1]->parameters.data, buf3, buf3_len);
+ algId[1]->parameters.length = buf3_len;
+ algId[1]->algorithm = dh_oid;
+
+ algId[2] = (krb5_algorithm_identifier *)malloc(sizeof(krb5_algorithm_identifier));
+ if (algId[2] == NULL)
+ goto cleanup;
+ algId[2]->parameters.data = (unsigned char *)malloc(buf1_len);
+ if (algId[2]->parameters.data == NULL)
+ goto cleanup;
+ memcpy(algId[2]->parameters.data, buf1, buf1_len);
+ algId[2]->parameters.length = buf1_len;
+ algId[2]->algorithm = dh_oid;
+
+ } else if (opts->dh_min_bits <= 2048) {
+ algId = malloc(3 * sizeof(krb5_algorithm_identifier *));
+ if (algId == NULL)
+ goto cleanup;
+ algId[2] = NULL;
+ algId[0] = (krb5_algorithm_identifier *)malloc(sizeof(krb5_algorithm_identifier));
+ if (algId[0] == NULL)
+ goto cleanup;
+ algId[0]->parameters.data = (unsigned char *)malloc(buf2_len);
+ if (algId[0]->parameters.data == NULL)
+ goto cleanup;
+ memcpy(algId[0]->parameters.data, buf2, buf2_len);
+ algId[0]->parameters.length = buf2_len;
+ algId[0]->algorithm = dh_oid;
+
+ algId[1] = (krb5_algorithm_identifier *)malloc(sizeof(krb5_algorithm_identifier));
+ if (algId[1] == NULL)
+ goto cleanup;
+ algId[1]->parameters.data = (unsigned char *)malloc(buf3_len);
+ if (algId[1]->parameters.data == NULL)
+ goto cleanup;
+ memcpy(algId[1]->parameters.data, buf3, buf3_len);
+ algId[1]->parameters.length = buf3_len;
+ algId[1]->algorithm = dh_oid;
+
+ } else if (opts->dh_min_bits <= 4096) {
+ algId = malloc(2 * sizeof(krb5_algorithm_identifier *));
+ if (algId == NULL)
+ goto cleanup;
+ algId[1] = NULL;
+ algId[0] = (krb5_algorithm_identifier *)malloc(sizeof(krb5_algorithm_identifier));
+ if (algId[0] == NULL)
+ goto cleanup;
+ algId[0]->parameters.data = (unsigned char *)malloc(buf3_len);
+ if (algId[0]->parameters.data == NULL)
+ goto cleanup;
+ memcpy(algId[0]->parameters.data, buf3, buf3_len);
+ algId[0]->parameters.length = buf3_len;
+ algId[0]->algorithm = dh_oid;
+
+ }
+ retval = k5int_encode_krb5_td_dh_parameters((const krb5_algorithm_identifier **)algId, &encoded_algId);
+ if (retval)
+ goto cleanup;
+#ifdef DEBUG_ASN1
+ print_buffer_bin((unsigned char *)encoded_algId->data,
+ encoded_algId->length, "/tmp/kdc_td_dh_params");
+#endif
+ typed_data = malloc (2 * sizeof(krb5_typed_data *));
+ if (typed_data == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ typed_data[1] = NULL;
+ init_krb5_typed_data(&typed_data[0]);
+ if (typed_data == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ typed_data[0]->type = TD_DH_PARAMETERS;
+ typed_data[0]->length = encoded_algId->length;
+ typed_data[0]->data = (unsigned char *)encoded_algId->data;
+ retval = k5int_encode_krb5_typed_data((const krb5_typed_data**)typed_data,
+ &data);
+ if (retval) {
+ pkiDebug("encode_krb5_typed_data failed\n");
+ goto cleanup;
+ }
+#ifdef DEBUG_ASN1
+ print_buffer_bin((unsigned char *)data->data, data->length,
+ "/tmp/kdc_edata");
+#endif
+ *out_data = (krb5_data *)malloc(sizeof(krb5_data));
+ if (*out_data == NULL)
+ goto cleanup;
+ (*out_data)->length = data->length;
+ (*out_data)->data = (char *)malloc(data->length);
+ if ((*out_data)->data == NULL) {
+ free(*out_data);
+ *out_data = NULL;
+ goto cleanup;
+ }
+ memcpy((*out_data)->data, data->data, data->length);
+
+ retval = 0;
+cleanup:
+
+ if (buf1 != NULL)
+ free(buf1);
+ if (buf2 != NULL)
+ free(buf2);
+ if (buf3 != NULL)
+ free(buf3);
+ if (data != NULL) {
+ if (data->data != NULL)
+ free(data->data);
+ free(data);
+ }
+ if (typed_data != NULL)
+ free_krb5_typed_data(&typed_data);
+ if (encoded_algId != NULL)
+ free(encoded_algId);
+
+ if (algId != NULL) {
+ while(algId[i] != NULL) {
+ if (algId[i]->parameters.data != NULL)
+ free(algId[i]->parameters.data);
+ free(algId[i]);
+ i++;
+ }
+ free(algId);
+ }
+
+ return retval;
+}
+
+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 *pdid_buf,
+ unsigned int pkid_len,
+ int *valid_kdcPkId)
+{
+ krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ PKCS7_ISSUER_AND_SERIAL *is = NULL;
+ const unsigned char *p = pdid_buf;
+ int status = 1;
+ X509 *kdc_cert = sk_X509_value(id_cryptoctx->my_certs, id_cryptoctx->cert_index);
+
+ *valid_kdcPkId = 0;
+ pkiDebug("found kdcPkId in AS REQ\n");
+ is = d2i_PKCS7_ISSUER_AND_SERIAL(NULL, &p, (int)pkid_len);
+ if (is == NULL)
+ goto cleanup;
+
+ status = X509_NAME_cmp(X509_get_issuer_name(kdc_cert), is->issuer);
+ if (!status) {
+ status = ASN1_INTEGER_cmp(X509_get_serialNumber(kdc_cert), is->serial);
+ if (!status)
+ *valid_kdcPkId = 1;
+ }
+
+ retval = 0;
+cleanup:
+ X509_NAME_free(is->issuer);
+ ASN1_INTEGER_free(is->serial);
+ free(is);
+
+ return retval;
+}
+
+static int
+pkinit_check_dh_params(BIGNUM * p1, BIGNUM * p2, BIGNUM * g1, BIGNUM * q1)
+{
+ BIGNUM *g2 = NULL, *q2 = NULL;
+ int retval = -1;
+
+ if (!BN_cmp(p1, p2)) {
+ g2 = BN_new();
+ BN_set_word(g2, DH_GENERATOR_2);
+ if (!BN_cmp(g1, g2)) {
+ q2 = BN_new();
+ BN_rshift1(q2, p1);
+ if (!BN_cmp(q1, q2)) {
+ pkiDebug("good %d dhparams\n", BN_num_bits(p1));
+ retval = 0;
+ } else
+ pkiDebug("bad group 2 q dhparameter\n");
+ BN_free(q2);
+ } else
+ pkiDebug("bad g dhparameter\n");
+ BN_free(g2);
+ } else
+ pkiDebug("p is not well-known group 2 dhparameter\n");
+
+ return retval;
+}
+
+krb5_error_code
+pkinit_process_td_dh_params(krb5_context context,
+ pkinit_plg_crypto_context cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ krb5_algorithm_identifier **algId,
+ int *new_dh_size)
+{
+ krb5_error_code retval = KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
+ int i = 0, use_sent_dh = 0, ok = 0;
+
+ pkiDebug("dh parameters\n");
+
+ while (algId[i] != NULL) {
+ DH *dh = NULL;
+ unsigned char *tmp = NULL;
+ int dh_prime_bits = 0;
+
+ if (algId[i]->algorithm.length != dh_oid.length ||
+ memcmp(algId[i]->algorithm.data, dh_oid.data, dh_oid.length))
+ goto cleanup;
+
+ tmp = algId[i]->parameters.data;
+ dh = DH_new();
+ dh = pkinit_decode_dh_params(&dh, &tmp, algId[i]->parameters.length);
+ dh_prime_bits = BN_num_bits(dh->p);
+ pkiDebug("client sent %d DH bits server prefers %d DH bits\n",
+ *new_dh_size, dh_prime_bits);
+ switch(dh_prime_bits) {
+ case 1024:
+ if (pkinit_check_dh_params(cryptoctx->dh_1024->p, dh->p,
+ dh->g, dh->q) == 0) {
+ *new_dh_size = 1024;
+ ok = 1;
+ }
+ break;
+ case 2048:
+ if (pkinit_check_dh_params(cryptoctx->dh_2048->p, dh->p,
+ dh->g, dh->q) == 0) {
+ *new_dh_size = 2048;
+ ok = 1;
+ }
+ break;
+ case 4096:
+ if (pkinit_check_dh_params(cryptoctx->dh_4096->p, dh->p,
+ dh->g, dh->q) == 0) {
+ *new_dh_size = 4096;
+ ok = 1;
+ }
+ break;
+ default:
+ break;
+ }
+ if (!ok) {
+ DH_check(dh, &retval);
+ if (retval != 0) {
+ pkiDebug("DH parameters provided by server are unacceptable\n");
+ retval = KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
+ }
+ else {
+ use_sent_dh = 1;
+ ok = 1;
+ }
+ }
+ if (!use_sent_dh)
+ DH_free(dh);
+ if (ok) {
+ if (req_cryptoctx->dh != NULL) {
+ DH_free(req_cryptoctx->dh);
+ req_cryptoctx->dh = NULL;
+ }
+ if (use_sent_dh)
+ req_cryptoctx->dh = dh;
+ break;
+ }
+ i++;
+ }
+
+ if (ok)
+ retval = 0;
+
+cleanup:
+ return retval;
+}
+
+static int
+openssl_callback(int ok, X509_STORE_CTX * ctx)
+{
+#ifdef DEBUG
+ if (!ok) {
+ char buf[DN_BUF_LEN];
+
+ X509_NAME_oneline(X509_get_subject_name(ctx->current_cert), buf, sizeof(buf));
+ pkiDebug("cert = %s\n", buf);
+ pkiDebug("callback function: %d (%s)\n", ctx->error,
+ X509_verify_cert_error_string(ctx->error));
+ }
+#endif
+ return ok;
+}
+
+static int
+openssl_callback_ignore_crls(int ok, X509_STORE_CTX * ctx)
+{
+ if (!ok) {
+ switch (ctx->error) {
+ case X509_V_ERR_UNABLE_TO_GET_CRL:
+ return 1;
+ default:
+ return 0;
+ }
+ }
+ return ok;
+}
+
+static ASN1_OBJECT *
+pkinit_pkcs7type2oid(pkinit_plg_crypto_context cryptoctx, int pkcs7_type)
+{
+ int nid;
+
+ switch (pkcs7_type) {
+ case CMS_SIGN_CLIENT:
+ return cryptoctx->id_pkinit_authData;
+ case CMS_SIGN_DRAFT9:
+ /*
+ * Delay creating this OID until we know we need it.
+ * It shadows an existing OpenSSL oid. If it
+ * is created too early, it breaks things like
+ * the use of pkcs12 (which uses pkcs7 structures).
+ * We need this shadow version because our code
+ * depends on the "other" type to be unknown to the
+ * OpenSSL code.
+ */
+ if (cryptoctx->id_pkinit_authData9 == NULL) {
+ pkiDebug("%s: Creating shadow instance of pkcs7-data oid\n",
+ __FUNCTION__);
+ nid = OBJ_create("1.2.840.113549.1.7.1", "id-pkcs7-data",
+ "PKCS7 data");
+ if (nid == NID_undef)
+ return NULL;
+ cryptoctx->id_pkinit_authData9 = OBJ_nid2obj(nid);
+ }
+ return cryptoctx->id_pkinit_authData9;
+ case CMS_SIGN_SERVER:
+ return cryptoctx->id_pkinit_DHKeyData;
+ case CMS_ENVEL_SERVER:
+ return cryptoctx->id_pkinit_rkeyData;
+ default:
+ return NULL;
+ }
+
+}
+
+#ifdef LONGHORN_BETA_COMPAT
+#if 0
+/*
+ * This is a version that worked with Longhorn Beta 3.
+ */
+static int
+wrap_signeddata(unsigned char *data, unsigned int data_len,
+ unsigned char **out, unsigned int *out_len,
+ int is_longhorn_server)
+{
+
+ unsigned int orig_len = 0, oid_len = 0, tot_len = 0;
+ ASN1_OBJECT *oid = NULL;
+ unsigned char *p = NULL;
+
+ pkiDebug("%s: This is the Longhorn version and is_longhorn_server = %d\n",
+ __FUNCTION__, is_longhorn_server);
+
+ /* Get length to wrap the original data with SEQUENCE tag */
+ tot_len = orig_len = ASN1_object_size(1, (int)data_len, V_ASN1_SEQUENCE);
+
+ if (is_longhorn_server == 0) {
+ /* Add the signedData OID and adjust lengths */
+ oid = OBJ_nid2obj(NID_pkcs7_signed);
+ oid_len = i2d_ASN1_OBJECT(oid, NULL);
+
+ tot_len = ASN1_object_size(1, (int)(orig_len+oid_len), V_ASN1_SEQUENCE);
+ }
+
+ p = *out = (unsigned char *)malloc(tot_len);
+ if (p == NULL) return -1;
+
+ if (is_longhorn_server == 0) {
+ ASN1_put_object(&p, 1, (int)(orig_len+oid_len),
+ V_ASN1_SEQUENCE, V_ASN1_UNIVERSAL);
+
+ i2d_ASN1_OBJECT(oid, &p);
+
+ ASN1_put_object(&p, 1, (int)data_len, 0, V_ASN1_CONTEXT_SPECIFIC);
+ } else {
+ ASN1_put_object(&p, 1, (int)data_len, V_ASN1_SEQUENCE, V_ASN1_UNIVERSAL);
+ }
+ memcpy(p, data, data_len);
+
+ *out_len = tot_len;
+
+ return 0;
+}
+#else
+/*
+ * This is a version that works with a patched Longhorn KDC.
+ * (Which should match SP1 ??).
+ */
+static int
+wrap_signeddata(unsigned char *data, unsigned int data_len,
+ unsigned char **out, unsigned int *out_len,
+ int is_longhorn_server)
+{
+
+ unsigned int oid_len = 0, tot_len = 0, wrap_len = 0, tag_len = 0;
+ ASN1_OBJECT *oid = NULL;
+ unsigned char *p = NULL;
+
+ pkiDebug("%s: This is the Longhorn version and is_longhorn_server = %d\n",
+ __FUNCTION__, is_longhorn_server);
+
+ /* New longhorn is missing another sequence */
+ if (is_longhorn_server == 1)
+ wrap_len = ASN1_object_size(1, (int)(data_len), V_ASN1_SEQUENCE);
+ else
+ wrap_len = data_len;
+
+ /* Get length to wrap the original data with SEQUENCE tag */
+ tag_len = ASN1_object_size(1, (int)wrap_len, V_ASN1_SEQUENCE);
+
+ /* Always add oid */
+ oid = OBJ_nid2obj(NID_pkcs7_signed);
+ oid_len = i2d_ASN1_OBJECT(oid, NULL);
+ oid_len += tag_len;
+
+ tot_len = ASN1_object_size(1, (int)(oid_len), V_ASN1_SEQUENCE);
+
+ p = *out = (unsigned char *)malloc(tot_len);
+ if (p == NULL)
+ return -1;
+
+ ASN1_put_object(&p, 1, (int)(oid_len),
+ V_ASN1_SEQUENCE, V_ASN1_UNIVERSAL);
+
+ i2d_ASN1_OBJECT(oid, &p);
+
+ ASN1_put_object(&p, 1, (int)wrap_len, 0, V_ASN1_CONTEXT_SPECIFIC);
+
+ /* Wrap in extra seq tag */
+ if (is_longhorn_server == 1) {
+ ASN1_put_object(&p, 1, (int)data_len, V_ASN1_SEQUENCE, V_ASN1_UNIVERSAL);
+ }
+ memcpy(p, data, data_len);
+
+ *out_len = tot_len;
+
+ return 0;
+}
+
+#endif
+#else
+static int
+wrap_signeddata(unsigned char *data, unsigned int data_len,
+ unsigned char **out, unsigned int *out_len)
+{
+
+ unsigned int orig_len = 0, oid_len = 0, tot_len = 0;
+ ASN1_OBJECT *oid = NULL;
+ unsigned char *p = NULL;
+
+ /* Get length to wrap the original data with SEQUENCE tag */
+ tot_len = orig_len = ASN1_object_size(1, (int)data_len, V_ASN1_SEQUENCE);
+
+ /* Add the signedData OID and adjust lengths */
+ oid = OBJ_nid2obj(NID_pkcs7_signed);
+ oid_len = i2d_ASN1_OBJECT(oid, NULL);
+
+ tot_len = ASN1_object_size(1, (int)(orig_len+oid_len), V_ASN1_SEQUENCE);
+
+ p = *out = (unsigned char *)malloc(tot_len);
+ if (p == NULL) return -1;
+
+ ASN1_put_object(&p, 1, (int)(orig_len+oid_len),
+ V_ASN1_SEQUENCE, V_ASN1_UNIVERSAL);
+
+ i2d_ASN1_OBJECT(oid, &p);
+
+ ASN1_put_object(&p, 1, (int)data_len, 0, V_ASN1_CONTEXT_SPECIFIC);
+ memcpy(p, data, data_len);
+
+ *out_len = tot_len;
+
+ return 0;
+}
+#endif
+
+static int
+prepare_enc_data(unsigned char *indata,
+ int indata_len,
+ unsigned char **outdata,
+ int *outdata_len)
+{
+ int retval = -1;
+ ASN1_const_CTX c;
+ long length = indata_len;
+ int Ttag, Tclass;
+ long Tlen;
+
+ c.pp = (const unsigned char **)&indata;
+ c.q = *(const unsigned char **)&indata;
+ c.error = ERR_R_NESTED_ASN1_ERROR;
+ c.p= *(const unsigned char **)&indata;
+ c.max = (length == 0)?0:(c.p+length);
+
+ asn1_GetSequence(&c,&length);
+
+ ASN1_get_object(&c.p,&Tlen,&Ttag,&Tclass,c.slen);
+ c.p += Tlen;
+ ASN1_get_object(&c.p,&Tlen,&Ttag,&Tclass,c.slen);
+
+ asn1_const_Finish(&c);
+
+ *outdata = (unsigned char *)malloc((size_t)Tlen);
+ if (outdata == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ memcpy(*outdata, c.p, (size_t)Tlen);
+ *outdata_len = Tlen;
+
+ retval = 0;
+cleanup:
+
+ return retval;
+}
+
+#ifndef WITHOUT_PKCS11
+static void *
+pkinit_C_LoadModule(const char *modname, CK_FUNCTION_LIST_PTR_PTR p11p)
+{
+ void *handle;
+ CK_RV (*getflist)(CK_FUNCTION_LIST_PTR_PTR);
+
+ pkiDebug("loading module \"%s\"... ", modname);
+ handle = dlopen(modname, RTLD_NOW);
+ if (handle == NULL) {
+ pkiDebug("not found\n");
+ return NULL;
+ }
+ getflist = (CK_RV (*)(CK_FUNCTION_LIST_PTR_PTR)) dlsym(handle, "C_GetFunctionList");
+ if (getflist == NULL || (*getflist)(p11p) != CKR_OK) {
+ dlclose(handle);
+ pkiDebug("failed\n");
+ return NULL;
+ }
+ pkiDebug("ok\n");
+ return handle;
+}
+
+static CK_RV
+pkinit_C_UnloadModule(void *handle)
+{
+ dlclose(handle);
+ return CKR_OK;
+}
+
+static krb5_error_code
+pkinit_login(krb5_context context,
+ pkinit_identity_crypto_context id_cryptoctx,
+ CK_TOKEN_INFO *tip)
+{
+ krb5_data rdat;
+ char *prompt;
+ krb5_prompt kprompt;
+ krb5_prompt_type prompt_type;
+ int r = 0;
+
+ if (tip->flags & CKF_PROTECTED_AUTHENTICATION_PATH) {
+ rdat.data = NULL;
+ rdat.length = 0;
+ } else {
+ if ((prompt = (char *) malloc(sizeof (tip->label) + 32)) == NULL)
+ return ENOMEM;
+ sprintf(prompt, "%.*s PIN", sizeof (tip->label), tip->label);
+ if (tip->flags & CKF_USER_PIN_LOCKED)
+ strcat(prompt, " (Warning: PIN locked)");
+ else if (tip->flags & CKF_USER_PIN_FINAL_TRY)
+ strcat(prompt, " (Warning: PIN final try)");
+ else if (tip->flags & CKF_USER_PIN_COUNT_LOW)
+ strcat(prompt, " (Warning: PIN count low)");
+ rdat.data = (char *)malloc(tip->ulMaxPinLen + 2);
+ rdat.length = tip->ulMaxPinLen + 1;
+
+ kprompt.prompt = prompt;
+ kprompt.hidden = 1;
+ kprompt.reply = &rdat;
+ prompt_type = KRB5_PROMPT_TYPE_PREAUTH;
+
+ /* PROMPTER_INVOCATION */
+ k5int_set_prompt_types(context, &prompt_type);
+ r = (*id_cryptoctx->prompter)(context, id_cryptoctx->prompter_data,
+ NULL, NULL, 1, &kprompt);
+ k5int_set_prompt_types(context, 0);
+ free(prompt);
+ }
+
+ if (r == 0) {
+ r = id_cryptoctx->p11->C_Login(id_cryptoctx->session, CKU_USER,
+ (u_char *) rdat.data, rdat.length);
+
+ if (r != CKR_OK) {
+ pkiDebug("C_Login: %s\n", pkinit_pkcs11_code_to_text(r));
+ r = KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+ }
+ if (rdat.data)
+ free(rdat.data);
+
+ return r;
+}
+
+static krb5_error_code
+pkinit_open_session(krb5_context context,
+ pkinit_identity_crypto_context cctx)
+{
+ int i, r;
+ unsigned char *cp;
+ CK_ULONG count = 0;
+ CK_SLOT_ID_PTR slotlist;
+ CK_TOKEN_INFO tinfo;
+
+ if (cctx->p11_module != NULL)
+ return 0; /* session already open */
+
+ /* Load module */
+ cctx->p11_module =
+ pkinit_C_LoadModule(cctx->p11_module_name, &cctx->p11);
+ if (cctx->p11_module == NULL)
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+
+ /* Init */
+ if ((r = cctx->p11->C_Initialize(NULL)) != CKR_OK) {
+ pkiDebug("C_Initialize: %s\n", pkinit_pkcs11_code_to_text(r));
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+
+ /* Get the list of available slots */
+ if (cctx->slotid != PK_NOSLOT) {
+ /* A slot was specified, so that's the only one in the list */
+ count = 1;
+ slotlist = (CK_SLOT_ID_PTR) malloc(sizeof (CK_SLOT_ID));
+ slotlist[0] = cctx->slotid;
+ } else {
+ if (cctx->p11->C_GetSlotList(TRUE, NULL, &count) != CKR_OK)
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ if (count == 0)
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ slotlist = (CK_SLOT_ID_PTR) malloc(count * sizeof (CK_SLOT_ID));
+ if (cctx->p11->C_GetSlotList(TRUE, slotlist, &count) != CKR_OK)
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+
+ /* Look for the given token label, or if none given take the first one */
+ for (i = 0; i < count; i++) {
+ /* Open session */
+ if ((r = cctx->p11->C_OpenSession(slotlist[i], CKF_SERIAL_SESSION,
+ NULL, NULL, &cctx->session)) != CKR_OK) {
+ pkiDebug("C_OpenSession: %s\n", pkinit_pkcs11_code_to_text(r));
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+
+ /* Get token info */
+ if ((r = cctx->p11->C_GetTokenInfo(slotlist[i], &tinfo)) != CKR_OK) {
+ pkiDebug("C_GetTokenInfo: %s\n", pkinit_pkcs11_code_to_text(r));
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+ for (cp = tinfo.label + sizeof (tinfo.label) - 1;
+ *cp == '\0' || *cp == ' '; cp--)
+ *cp = '\0';
+ pkiDebug("open_session: slotid %d token \"%s\"\n",
+ (int) slotlist[i], tinfo.label);
+ if (cctx->token_label == NULL ||
+ !strcmp((char *) cctx->token_label, (char *) tinfo.label))
+ break;
+ cctx->p11->C_CloseSession(cctx->session);
+ }
+ if (i >= count) {
+ free(slotlist);
+ pkiDebug("open_session: no matching token found\n");
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+ cctx->slotid = slotlist[i];
+ free(slotlist);
+ pkiDebug("open_session: slotid %d (%d of %d)\n", (int) cctx->slotid,
+ i + 1, (int) count);
+
+ /* Login if needed */
+ if (tinfo.flags & CKF_LOGIN_REQUIRED)
+ r = pkinit_login(context, cctx, &tinfo);
+
+ return r;
+}
+
+/*
+ * Look for a key that's:
+ * 1. private
+ * 2. capable of the specified operation (usually signing or decrypting)
+ * 3. RSA (this may be wrong but it's all we can do for now)
+ * 4. matches the id of the cert we chose
+ *
+ * You must call pkinit_get_certs before calling pkinit_find_private_key
+ * (that's because we need the ID of the private key)
+ *
+ * pkcs11 says the id of the key doesn't have to match that of the cert, but
+ * I can't figure out any other way to decide which key to use.
+ *
+ * We should only find one key that fits all the requirements.
+ * If there are more than one, we just take the first one.
+ */
+
+krb5_error_code
+pkinit_find_private_key(pkinit_identity_crypto_context id_cryptoctx,
+ CK_ATTRIBUTE_TYPE usage,
+ CK_OBJECT_HANDLE *objp)
+{
+ CK_OBJECT_CLASS cls;
+ CK_ATTRIBUTE attrs[4];
+ CK_ULONG count;
+ CK_KEY_TYPE keytype;
+ unsigned int nattrs = 0;
+ int r;
+#ifdef PKINIT_USE_KEY_USAGE
+ CK_BBOOL true_false;
+#endif
+
+ cls = CKO_PRIVATE_KEY;
+ attrs[nattrs].type = CKA_CLASS;
+ attrs[nattrs].pValue = &cls;
+ attrs[nattrs].ulValueLen = sizeof cls;
+ nattrs++;
+
+#ifdef PKINIT_USE_KEY_USAGE
+ /*
+ * Some cards get confused if you try to specify a key usage,
+ * so don't, and hope for the best. This will fail if you have
+ * several keys with the same id and different usages but I have
+ * not seen this on real cards.
+ */
+ true_false = TRUE;
+ attrs[nattrs].type = usage;
+ attrs[nattrs].pValue = &true_false;
+ attrs[nattrs].ulValueLen = sizeof true_false;
+ nattrs++;
+#endif
+
+ keytype = CKK_RSA;
+ attrs[nattrs].type = CKA_KEY_TYPE;
+ attrs[nattrs].pValue = &keytype;
+ attrs[nattrs].ulValueLen = sizeof keytype;
+ nattrs++;
+
+ attrs[nattrs].type = CKA_ID;
+ attrs[nattrs].pValue = id_cryptoctx->cert_id;
+ attrs[nattrs].ulValueLen = id_cryptoctx->cert_id_len;
+ nattrs++;
+
+ r = id_cryptoctx->p11->C_FindObjectsInit(id_cryptoctx->session, attrs, nattrs);
+ if (r != CKR_OK) {
+ pkiDebug("krb5_pkinit_sign_data: C_FindObjectsInit: %s\n",
+ pkinit_pkcs11_code_to_text(r));
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+
+ r = id_cryptoctx->p11->C_FindObjects(id_cryptoctx->session, objp, 1, &count);
+ id_cryptoctx->p11->C_FindObjectsFinal(id_cryptoctx->session);
+ pkiDebug("found %d private keys (%s)\n", (int) count, pkinit_pkcs11_code_to_text(r));
+ if (r != CKR_OK || count < 1)
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ return 0;
+}
+#endif
+
+static krb5_error_code
+pkinit_decode_data_fs(krb5_context context,
+ pkinit_identity_crypto_context id_cryptoctx,
+ unsigned char *data,
+ unsigned int data_len,
+ unsigned char **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");
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+ return 0;
+}
+
+#ifndef WITHOUT_PKCS11
+#ifdef SILLYDECRYPT
+CK_RV
+pkinit_C_Decrypt(pkinit_identity_crypto_context id_cryptoctx,
+ CK_BYTE_PTR pEncryptedData,
+ CK_ULONG ulEncryptedDataLen,
+ CK_BYTE_PTR pData,
+ CK_ULONG_PTR pulDataLen)
+{
+ CK_RV rv = CKR_OK;
+
+ rv = id_cryptoctx->p11->C_Decrypt(id_cryptoctx->session, pEncryptedData,
+ ulEncryptedDataLen, pData, pulDataLen);
+ if (rv == CKR_OK) {
+ pkiDebug("pData %x *pulDataLen %d\n", (int) pData, (int) *pulDataLen);
+ }
+ return rv;
+}
+#endif
+
+static krb5_error_code
+pkinit_decode_data_pkcs11(krb5_context context,
+ pkinit_identity_crypto_context id_cryptoctx,
+ unsigned char *data,
+ unsigned int data_len,
+ unsigned char **decoded_data,
+ unsigned int *decoded_data_len)
+{
+ CK_OBJECT_HANDLE obj;
+ CK_ULONG len;
+ CK_MECHANISM mech;
+ unsigned char *cp;
+ int r;
+
+ if (pkinit_open_session(context, id_cryptoctx)) {
+ pkiDebug("can't open pkcs11 session\n");
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+
+ pkinit_find_private_key(id_cryptoctx, CKA_DECRYPT, &obj);
+
+ mech.mechanism = CKM_RSA_PKCS;
+ mech.pParameter = NULL;
+ mech.ulParameterLen = 0;
+
+ if ((r = id_cryptoctx->p11->C_DecryptInit(id_cryptoctx->session, &mech,
+ obj)) != CKR_OK) {
+ pkiDebug("C_DecryptInit: 0x%x\n", (int) r);
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+ pkiDebug("data_len = %d\n", data_len);
+ cp = (unsigned char *)malloc((size_t) data_len);
+ if (cp == NULL)
+ return ENOMEM;
+ len = data_len;
+#ifdef SILLYDECRYPT
+ pkiDebug("session %x edata %x edata_len %d data %x datalen @%x %d\n",
+ (int) id_cryptoctx->session, (int) data, (int) data_len, (int) cp,
+ (int) &len, (int) len);
+ if ((r = pkinit_C_Decrypt(id_cryptoctx, data, (CK_ULONG) data_len,
+ cp, &len)) != CKR_OK) {
+#else
+ if ((r = id_cryptoctx->p11->C_Decrypt(id_cryptoctx->session, data,
+ (CK_ULONG) data_len, cp, &len)) != CKR_OK) {
+#endif
+ pkiDebug("C_Decrypt: %s\n", pkinit_pkcs11_code_to_text(r));
+ if (r == CKR_BUFFER_TOO_SMALL)
+ pkiDebug("decrypt %d needs %d\n", (int) data_len, (int) len);
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+ pkiDebug("decrypt %d -> %d\n", (int) data_len, (int) len);
+ *decoded_data_len = len;
+ *decoded_data = cp;
+
+ return 0;
+}
+#endif
+
+krb5_error_code
+pkinit_decode_data(krb5_context context,
+ pkinit_identity_crypto_context id_cryptoctx,
+ unsigned char *data,
+ unsigned int data_len,
+ unsigned char **decoded_data,
+ unsigned int *decoded_data_len)
+{
+ krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+
+ if (id_cryptoctx->pkcs11_method != 1)
+ retval = pkinit_decode_data_fs(context, id_cryptoctx, data, data_len,
+ decoded_data, decoded_data_len);
+#ifndef WITHOUT_PKCS11
+ else
+ retval = pkinit_decode_data_pkcs11(context, id_cryptoctx, data,
+ data_len, decoded_data, decoded_data_len);
+#endif
+
+ return retval;
+}
+
+static krb5_error_code
+pkinit_sign_data_fs(krb5_context context,
+ pkinit_identity_crypto_context id_cryptoctx,
+ unsigned char *data,
+ unsigned int data_len,
+ unsigned char **sig,
+ unsigned int *sig_len)
+{
+ if (create_signature(sig, sig_len, data, data_len,
+ id_cryptoctx->my_key) != 0) {
+ pkiDebug("failed to create the signature\n");
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+ return 0;
+}
+
+#ifndef WITHOUT_PKCS11
+static krb5_error_code
+pkinit_sign_data_pkcs11(krb5_context context,
+ pkinit_identity_crypto_context id_cryptoctx,
+ unsigned char *data,
+ unsigned int data_len,
+ unsigned char **sig,
+ unsigned int *sig_len)
+{
+ CK_OBJECT_HANDLE obj;
+ CK_ULONG len;
+ CK_MECHANISM mech;
+ unsigned char *cp;
+ int r;
+
+ if (pkinit_open_session(context, id_cryptoctx)) {
+ pkiDebug("can't open pkcs11 session\n");
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+
+ pkinit_find_private_key(id_cryptoctx, CKA_SIGN, &obj);
+
+ mech.mechanism = id_cryptoctx->mech;
+ mech.pParameter = NULL;
+ mech.ulParameterLen = 0;
+
+ if ((r = id_cryptoctx->p11->C_SignInit(id_cryptoctx->session, &mech,
+ obj)) != CKR_OK) {
+ pkiDebug("C_SignInit: %s\n", pkinit_pkcs11_code_to_text(r));
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+
+ /*
+ * Key len would give an upper bound on sig size, but there's no way to
+ * get that. So guess, and if it's too small, re-malloc.
+ */
+ len = PK_SIGLEN_GUESS;
+ cp = (unsigned char *)malloc((size_t) len);
+ if (cp == NULL)
+ return ENOMEM;
+
+ r = id_cryptoctx->p11->C_Sign(id_cryptoctx->session, data,
+ (CK_ULONG) data_len, cp, &len);
+ if (r == CKR_BUFFER_TOO_SMALL || (r == CKR_OK && len >= PK_SIGLEN_GUESS)) {
+ free(cp);
+ pkiDebug("C_Sign realloc %d\n", (int) len);
+ cp = (unsigned char *)malloc((size_t) len);
+ r = id_cryptoctx->p11->C_Sign(id_cryptoctx->session, data,
+ (CK_ULONG) data_len, cp, &len);
+ }
+ if (r != CKR_OK) {
+ pkiDebug("C_Sign: %s\n", pkinit_pkcs11_code_to_text(r));
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+ pkiDebug("sign %d -> %d\n", (int) data_len, (int) len);
+ *sig_len = len;
+ *sig = cp;
+
+ return 0;
+}
+#endif
+
+krb5_error_code
+pkinit_sign_data(krb5_context context,
+ pkinit_identity_crypto_context id_cryptoctx,
+ unsigned char *data,
+ unsigned int data_len,
+ unsigned char **sig,
+ unsigned int *sig_len)
+{
+ krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+
+ if (id_cryptoctx == NULL || id_cryptoctx->pkcs11_method != 1)
+ retval = pkinit_sign_data_fs(context, id_cryptoctx, data, data_len,
+ sig, sig_len);
+#ifndef WITHOUT_PKCS11
+ else
+ retval = pkinit_sign_data_pkcs11(context, id_cryptoctx, data, data_len,
+ sig, sig_len);
+#endif
+
+ return retval;
+}
+
+
+static krb5_error_code
+decode_data(unsigned char **out_data, unsigned int *out_data_len,
+ unsigned char *data, unsigned int data_len,
+ EVP_PKEY *pkey, X509 *cert)
+{
+ krb5_error_code retval = ENOMEM;
+ unsigned char *buf = NULL;
+ int buf_len = 0;
+
+ if (cert && !X509_check_private_key(cert, pkey)) {
+ pkiDebug("private key does not match certificate\n");
+ goto cleanup;
+ }
+
+ buf_len = EVP_PKEY_size(pkey);
+ buf = (unsigned char *)malloc((size_t) buf_len + 10);
+ if (buf == NULL)
+ goto cleanup;
+
+ retval = EVP_PKEY_decrypt(buf, data, (int)data_len, pkey);
+ if (retval <= 0) {
+ pkiDebug("unable to decrypt received data (len=%d)\n", data_len);
+ goto cleanup;
+ }
+ *out_data = buf;
+ *out_data_len = retval;
+
+ cleanup:
+ if (retval == ENOMEM)
+ free(buf);
+
+ return retval;
+}
+
+static krb5_error_code
+create_signature(unsigned char **sig, unsigned int *sig_len,
+ unsigned char *data, unsigned int data_len, EVP_PKEY *pkey)
+{
+ krb5_error_code retval = ENOMEM;
+ EVP_MD_CTX md_ctx;
+
+ if (pkey == NULL)
+ return retval;
+
+ EVP_VerifyInit(&md_ctx, EVP_sha1());
+ EVP_SignUpdate(&md_ctx, data, data_len);
+ *sig_len = EVP_PKEY_size(pkey);
+ if ((*sig = (unsigned char *) malloc((size_t) *sig_len)) == NULL)
+ goto cleanup;
+ EVP_SignFinal(&md_ctx, *sig, sig_len, pkey);
+
+ retval = 0;
+
+ cleanup:
+ EVP_MD_CTX_cleanup(&md_ctx);
+
+ return retval;
+}
+
+/*
+ * Note:
+ * This is not the routine the KDC uses to get its certificate.
+ * This routine is intended to be called by the client
+ * to obtain the KDC's certificate from some local storage
+ * to be sent as a hint in its request to the KDC.
+ */
+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)
+{
+ krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+
+ req_cryptoctx->received_cert = NULL;
+ retval = 0;
+ return retval;
+}
+
+static krb5_error_code
+pkinit_get_certs_pkcs12(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_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ X509 *x = NULL;
+ PKCS12 *p12 = NULL;
+ int ret;
+ FILE *fp;
+ EVP_PKEY *y = NULL;
+
+ if (idopts->cert_filename == NULL) {
+ pkiDebug("%s: failed to get user's cert location\n", __FUNCTION__);
+ goto cleanup;
+ }
+
+ if (idopts->key_filename == NULL) {
+ pkiDebug("%s: failed to get user's private key location\n", __FUNCTION__);
+ goto cleanup;
+ }
+
+ fp = fopen(idopts->cert_filename, "rb");
+ if (fp == NULL) {
+ pkiDebug("Failed to open PKCS12 file '%s', error %d\n",
+ idopts->cert_filename, errno);
+ goto cleanup;
+ }
+
+ p12 = d2i_PKCS12_fp(fp, NULL);
+ fclose(fp);
+ if (p12 == NULL) {
+ pkiDebug("Failed to decode PKCS12 file '%s' contents\n",
+ idopts->cert_filename);
+ goto cleanup;
+ }
+ /*
+ * Try parsing with no pass phrase first. If that fails,
+ * prompt for the pass phrase and try again.
+ */
+ ret = PKCS12_parse(p12, NULL, &y, &x, NULL);
+ if (ret == 0) {
+ krb5_data rdat;
+ krb5_prompt kprompt;
+ krb5_prompt_type prompt_type;
+ int r = 0;
+ char prompt_string[128];
+ char prompt_reply[128];
+ char prompt_prefix[] = "Pass phrase for";
+
+ pkiDebug("Initial PKCS12_parse with no password failed\n");
+
+ memset(prompt_reply, '\0', sizeof(prompt_reply));
+ rdat.data = prompt_reply;
+ rdat.length = sizeof(prompt_reply);
+
+ r = snprintf(prompt_string, sizeof(prompt_string), "%s %s",
+ prompt_prefix, idopts->cert_filename);
+ if (r >= sizeof(prompt_string)) {
+ pkiDebug("Prompt string, '%s %s', is too long!\n",
+ prompt_prefix, idopts->cert_filename);
+ goto cleanup;
+ }
+ kprompt.prompt = prompt_string;
+ kprompt.hidden = 1;
+ kprompt.reply = &rdat;
+ prompt_type = KRB5_PROMPT_TYPE_PREAUTH;
+
+ /* PROMPTER_INVOCATION */
+ k5int_set_prompt_types(context, &prompt_type);
+ r = (*id_cryptoctx->prompter)(context, id_cryptoctx->prompter_data,
+ NULL, NULL, 1, &kprompt);
+ k5int_set_prompt_types(context, 0);
+
+ ret = PKCS12_parse(p12, rdat.data, &y, &x, NULL);
+ if (ret == 0) {
+ pkiDebug("Seconde PKCS12_parse with password failed\n");
+ goto cleanup;
+ }
+ }
+ id_cryptoctx->creds[0] = malloc(sizeof(struct _pkinit_cred_info));
+ if (id_cryptoctx->creds[0] == NULL)
+ goto cleanup;
+ id_cryptoctx->creds[0]->cert = x;
+#ifndef WITHOUT_PKCS11
+ id_cryptoctx->creds[0]->cert_id = NULL;
+ id_cryptoctx->creds[0]->cert_id_len = 0;
+#endif
+ id_cryptoctx->creds[0]->key = y;
+ id_cryptoctx->creds[1] = NULL;
+
+ retval = 0;
+
+cleanup:
+ if (p12)
+ PKCS12_free(p12);
+ if (retval) {
+ if (x != NULL)
+ X509_free(x);
+ if (y != NULL)
+ EVP_PKEY_free(y);
+ }
+ return retval;
+}
+
+static krb5_error_code
+pkinit_load_fs_cert_and_key(krb5_context context,
+ pkinit_identity_crypto_context id_cryptoctx,
+ char *certname,
+ char *keyname,
+ int cindex)
+{
+ krb5_error_code retval;
+ X509 *x = NULL;
+ EVP_PKEY *y = NULL;
+
+ /* load the certificate */
+ retval = get_cert(certname, &x);
+ if (retval != 0 || x == NULL) {
+ pkiDebug("failed to load user's certificate from '%s'\n", certname);
+ goto cleanup;
+ }
+ retval = get_key(keyname, &y);
+ if (retval != 0 || y == NULL) {
+ pkiDebug("failed to load user's private key from '%s'\n", keyname);
+ goto cleanup;
+ }
+
+ id_cryptoctx->creds[cindex] = malloc(sizeof(struct _pkinit_cred_info));
+ if (id_cryptoctx->creds[cindex] == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ id_cryptoctx->creds[cindex]->cert = x;
+#ifndef WITHOUT_PKCS11
+ id_cryptoctx->creds[cindex]->cert_id = NULL;
+ id_cryptoctx->creds[cindex]->cert_id_len = 0;
+#endif
+ id_cryptoctx->creds[cindex]->key = y;
+ id_cryptoctx->creds[cindex+1] = NULL;
+
+ retval = 0;
+
+cleanup:
+ if (retval) {
+ if (x != NULL)
+ X509_free(x);
+ if (y != NULL)
+ EVP_PKEY_free(y);
+ }
+ return retval;
+}
+
+static krb5_error_code
+pkinit_get_certs_fs(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_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+
+ if (idopts->cert_filename == NULL) {
+ pkiDebug("%s: failed to get user's cert location\n", __FUNCTION__);
+ goto cleanup;
+ }
+
+ if (idopts->key_filename == NULL) {
+ pkiDebug("%s: failed to get user's private key location\n",
+ __FUNCTION__);
+ goto cleanup;
+ }
+
+ retval = pkinit_load_fs_cert_and_key(context, id_cryptoctx,
+ idopts->cert_filename,
+ idopts->key_filename, 0);
+cleanup:
+ return retval;
+}
+
+static krb5_error_code
+pkinit_get_certs_dir(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_error_code retval = ENOMEM;
+ DIR *d = NULL;
+ struct dirent *dentry = NULL;
+ char certname[1024];
+ char keyname[1024];
+ int i = 0, len;
+ char *dirname, *suf;
+
+ if (idopts->cert_filename == NULL) {
+ pkiDebug("%s: failed to get user's certificate directory location\n",
+ __FUNCTION__);
+ return ENOENT;
+ }
+
+ dirname = idopts->cert_filename;
+ d = opendir(dirname);
+ if (d == NULL)
+ return errno;
+
+ /*
+ * We'll assume that certs are named XXX.crt and the corresponding
+ * key is named XXX.key
+ */
+ while ((i < MAX_CREDS_ALLOWED) && (dentry = readdir(d)) != NULL) {
+ /* Ignore subdirectories and anything starting with a dot */
+#ifdef DT_DIR
+ if (dentry->d_type == DT_DIR)
+ continue;
+#endif
+ if (dentry->d_name[0] == '.')
+ continue;
+ len = strlen(dentry->d_name);
+ if (len < 5)
+ continue;
+ suf = dentry->d_name + (len - 4);
+ if (strncmp(suf, ".crt", 4) != 0)
+ continue;
+
+ /* Checked length */
+ if (strlen(dirname) + strlen(dentry->d_name) + 2 > sizeof(certname)) {
+ pkiDebug("%s: Path too long -- directory '%s' and file '%s'\n",
+ __FUNCTION__, dirname, dentry->d_name);
+ continue;
+ }
+ snprintf(certname, sizeof(certname), "%s/%s", dirname, dentry->d_name);
+ snprintf(keyname, sizeof(keyname), "%s/%s", dirname, dentry->d_name);
+ len = strlen(keyname);
+ keyname[len - 3] = 'k';
+ keyname[len - 2] = 'e';
+ keyname[len - 1] = 'y';
+
+ 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);
+ i++;
+ }
+ else
+ continue;
+ }
+
+ if (i == 0) {
+ pkiDebug("%s: No cert/key pairs found in directory '%s'\n",
+ __FUNCTION__, idopts->cert_filename);
+ retval = ENOENT;
+ goto cleanup;
+ }
+
+ retval = 0;
+
+ cleanup:
+ if (d)
+ closedir(d);
+
+ return retval;
+}
+
+#ifndef WITHOUT_PKCS11
+static krb5_error_code
+pkinit_get_certs_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,
+ krb5_principal princ)
+{
+#ifdef PKINIT_USE_MECH_LIST
+ CK_MECHANISM_TYPE_PTR mechp;
+ CK_MECHANISM_INFO info;
+#endif
+ CK_OBJECT_CLASS cls;
+ CK_OBJECT_HANDLE obj;
+ CK_ATTRIBUTE attrs[4];
+ CK_ULONG count;
+ CK_CERTIFICATE_TYPE certtype;
+ CK_BYTE_PTR cert = NULL, cert_id;
+ const unsigned char *cp;
+ int i, r;
+ unsigned int nattrs;
+ X509 *x = NULL;
+
+ /* Copy stuff from idopts -> id_cryptoctx */
+ if (idopts->p11_module_name != NULL) {
+ id_cryptoctx->p11_module_name = strdup(idopts->p11_module_name);
+ if (id_cryptoctx->p11_module_name == NULL)
+ return ENOMEM;
+ }
+ if (idopts->token_label != NULL) {
+ id_cryptoctx->token_label = strdup(idopts->token_label);
+ if (id_cryptoctx->token_label == NULL)
+ return ENOMEM;
+ }
+ if (idopts->cert_label != NULL) {
+ id_cryptoctx->cert_label = strdup(idopts->cert_label);
+ if (id_cryptoctx->cert_label == NULL)
+ return ENOMEM;
+ }
+ /* Convert the ascii cert_id string into a binary blob */
+ if (idopts->cert_id_string != NULL) {
+ BIGNUM *bn = NULL;
+ BN_hex2bn(&bn, idopts->cert_id_string);
+ if (bn == NULL)
+ return ENOMEM;
+ id_cryptoctx->cert_id_len = BN_num_bytes(bn);
+ id_cryptoctx->cert_id = malloc((size_t) id_cryptoctx->cert_id_len);
+ if (id_cryptoctx->cert_id == NULL) {
+ BN_free(bn);
+ return ENOMEM;
+ }
+ BN_bn2bin(bn, id_cryptoctx->cert_id);
+ BN_free(bn);
+ }
+ id_cryptoctx->slotid = idopts->slotid;
+ id_cryptoctx->pkcs11_method = 1;
+
+
+
+ if (pkinit_open_session(context, id_cryptoctx)) {
+ pkiDebug("can't open pkcs11 session\n");
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+
+#ifndef PKINIT_USE_MECH_LIST
+ /*
+ * We'd like to use CKM_SHA1_RSA_PKCS for signing if it's available, but
+ * many cards seems to be confused about whether they are capable of
+ * this or not. The safe thing seems to be to ignore the mechanism list,
+ * always use CKM_RSA_PKCS and calculate the sha1 digest ourselves.
+ */
+
+ id_cryptoctx->mech = CKM_RSA_PKCS;
+#else
+ if ((r = id_cryptoctx->p11->C_GetMechanismList(id_cryptoctx->slotid, NULL,
+ &count)) != CKR_OK || count <= 0) {
+ pkiDebug("C_GetMechanismList: %s\n", pkinit_pkcs11_code_to_text(r));
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+ mechp = (CK_MECHANISM_TYPE_PTR) malloc(count * sizeof (CK_MECHANISM_TYPE));
+ if (mechp == NULL)
+ return ENOMEM;
+ if ((r = id_cryptoctx->p11->C_GetMechanismList(id_cryptoctx->slotid,
+ mechp, &count)) != CKR_OK)
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ for (i = 0; i < count; i++) {
+ if ((r = id_cryptoctx->p11->C_GetMechanismInfo(id_cryptoctx->slotid,
+ mechp[i], &info)) != CKR_OK)
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+#ifdef DEBUG_MECHINFO
+ pkiDebug("mech %x flags %x\n", (int) mechp[i], (int) info.flags);
+ if ((info.flags & (CKF_SIGN|CKF_DECRYPT)) == (CKF_SIGN|CKF_DECRYPT))
+ pkiDebug(" this mech is good for sign & decrypt\n");
+#endif
+ if (mechp[i] == CKM_RSA_PKCS) {
+ /* This seems backwards... */
+ id_cryptoctx->mech =
+ (info.flags & CKF_SIGN) ? CKM_SHA1_RSA_PKCS : CKM_RSA_PKCS;
+ }
+ }
+ free(mechp);
+
+ pkiDebug("got %d mechs from card\n", (int) count);
+#endif
+
+ cls = CKO_CERTIFICATE;
+ attrs[0].type = CKA_CLASS;
+ attrs[0].pValue = &cls;
+ attrs[0].ulValueLen = sizeof cls;
+
+ certtype = CKC_X_509;
+ attrs[1].type = CKA_CERTIFICATE_TYPE;
+ attrs[1].pValue = &certtype;
+ attrs[1].ulValueLen = sizeof certtype;
+
+ nattrs = 2;
+
+ /* If a cert id and/or label were given, use them too */
+ if (id_cryptoctx->cert_id_len > 0) {
+ attrs[nattrs].type = CKA_ID;
+ attrs[nattrs].pValue = id_cryptoctx->cert_id;
+ attrs[nattrs].ulValueLen = id_cryptoctx->cert_id_len;
+ nattrs++;
+ }
+ if (id_cryptoctx->cert_label != NULL) {
+ attrs[nattrs].type = CKA_LABEL;
+ attrs[nattrs].pValue = id_cryptoctx->cert_label;
+ attrs[nattrs].ulValueLen = strlen(id_cryptoctx->cert_label);
+ nattrs++;
+ }
+
+ r = id_cryptoctx->p11->C_FindObjectsInit(id_cryptoctx->session, attrs, nattrs);
+ if (r != CKR_OK) {
+ pkiDebug("C_FindObjectsInit: %s\n", pkinit_pkcs11_code_to_text(r));
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+
+ for (i = 0; ; i++) {
+ if (i >= MAX_CREDS_ALLOWED)
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+
+ /* Look for x.509 cert */
+ if ((r = id_cryptoctx->p11->C_FindObjects(id_cryptoctx->session,
+ &obj, 1, &count)) != CKR_OK || count <= 0) {
+ id_cryptoctx->creds[i] = NULL;
+ break;
+ }
+
+ /* Get cert and id len */
+ attrs[0].type = CKA_VALUE;
+ attrs[0].pValue = NULL;
+ attrs[0].ulValueLen = 0;
+
+ attrs[1].type = CKA_ID;
+ attrs[1].pValue = NULL;
+ attrs[1].ulValueLen = 0;
+
+ if ((r = id_cryptoctx->p11->C_GetAttributeValue(id_cryptoctx->session,
+ obj, attrs, 2)) != CKR_OK && r != CKR_BUFFER_TOO_SMALL) {
+ pkiDebug("C_GetAttributeValue: %s\n", pkinit_pkcs11_code_to_text(r));
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+ cert = (CK_BYTE_PTR) malloc((size_t) attrs[0].ulValueLen + 1);
+ cert_id = (CK_BYTE_PTR) malloc((size_t) attrs[1].ulValueLen + 1);
+ if (cert == NULL || cert_id == NULL)
+ return ENOMEM;
+
+ /* Read the cert and id off the card */
+
+ attrs[0].type = CKA_VALUE;
+ attrs[0].pValue = cert;
+
+ attrs[1].type = CKA_ID;
+ attrs[1].pValue = cert_id;
+
+ if ((r = id_cryptoctx->p11->C_GetAttributeValue(id_cryptoctx->session,
+ obj, attrs, 2)) != CKR_OK) {
+ pkiDebug("C_GetAttributeValue: %s\n", pkinit_pkcs11_code_to_text(r));
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+
+ pkiDebug("cert %d size %d id %d idlen %d\n", i,
+ (int) attrs[0].ulValueLen, (int) cert_id[0],
+ (int) attrs[1].ulValueLen);
+
+ cp = (unsigned char *) cert;
+ x = d2i_X509(NULL, &cp, (int) attrs[0].ulValueLen);
+ if (x == NULL)
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ id_cryptoctx->creds[i] = malloc(sizeof(struct _pkinit_cred_info));
+ if (id_cryptoctx->creds[i] == NULL)
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ id_cryptoctx->creds[i]->cert = x;
+ id_cryptoctx->creds[i]->key = NULL;
+ id_cryptoctx->creds[i]->cert_id = cert_id;
+ id_cryptoctx->creds[i]->cert_id_len = attrs[1].ulValueLen;
+ free(cert);
+ }
+ id_cryptoctx->p11->C_FindObjectsFinal(id_cryptoctx->session);
+ if (cert == NULL)
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ return 0;
+}
+#endif
+
+
+static void
+free_cred_info(krb5_context context,
+ pkinit_identity_crypto_context id_cryptoctx,
+ struct _pkinit_cred_info *cred)
+{
+ if (cred != NULL) {
+ if (cred->cert != NULL)
+ X509_free(cred->cert);
+ if (cred->key != NULL)
+ EVP_PKEY_free(cred->key);
+#ifndef WITHOUT_PKCS11
+ if (cred->cert_id != NULL)
+ free(cred->cert_id);
+#endif
+ free(cred);
+ }
+}
+
+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)
+{
+ int i;
+
+ if (id_cryptoctx == NULL)
+ return EINVAL;
+
+ for (i = 0; i < MAX_CREDS_ALLOWED; i++) {
+ if (id_cryptoctx->creds[i] != NULL) {
+ free_cred_info(context, id_cryptoctx, id_cryptoctx->creds[i]);
+ id_cryptoctx->creds[i] = NULL;
+ }
+ }
+ return 0;
+}
+
+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_error_code retval;
+
+ switch(idopts->idtype) {
+ case IDTYPE_FILE:
+ retval = pkinit_get_certs_fs(context, plg_cryptoctx,
+ req_cryptoctx, idopts,
+ id_cryptoctx, princ);
+ break;
+ case IDTYPE_DIR:
+ retval = pkinit_get_certs_dir(context, plg_cryptoctx,
+ req_cryptoctx, idopts,
+ id_cryptoctx, princ);
+ break;
+#ifndef WITHOUT_PKCS11
+ case IDTYPE_PKCS11:
+ retval = pkinit_get_certs_pkcs11(context, plg_cryptoctx,
+ req_cryptoctx, idopts,
+ id_cryptoctx, princ);
+ break;
+#endif
+ case IDTYPE_PKCS12:
+ retval = pkinit_get_certs_pkcs12(context, plg_cryptoctx,
+ req_cryptoctx, idopts,
+ id_cryptoctx, princ);
+ break;
+ default:
+ retval = EINVAL;
+ }
+ if (retval)
+ goto cleanup;
+
+cleanup:
+ return retval;
+}
+
+/*
+ * 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)
+{
+ krb5_error_code retval = 0;
+ int i;
+ unsigned int eku_bits = 0, ku_bits = 0;
+ ASN1_BIT_STRING *usage = NULL;
+
+ if (ret_ku_bits == NULL && ret_eku_bits == NULL)
+ return EINVAL;
+
+ if (ret_eku_bits)
+ *ret_eku_bits = 0;
+ else {
+ pkiDebug("%s: EKUs not requested, not checking\n", __FUNCTION__);
+ goto check_kus;
+ }
+
+ /* Start with Extended Key usage */
+ i = X509_get_ext_by_NID(x, NID_ext_key_usage, -1);
+ if (i >= 0) {
+ EXTENDED_KEY_USAGE *eku;
+
+ eku = X509_get_ext_d2i(x, NID_ext_key_usage, NULL, NULL);
+ if (eku) {
+ for (i = 0; i < sk_ASN1_OBJECT_num(eku); i++) {
+ ASN1_OBJECT *certoid;
+ certoid = sk_ASN1_OBJECT_value(eku, i);
+ if ((OBJ_cmp(certoid, plgcctx->id_pkinit_KPClientAuth)) == 0)
+ eku_bits |= PKINIT_EKU_PKINIT;
+ else if ((OBJ_cmp(certoid, OBJ_nid2obj(NID_ms_smartcard_login))) == 0)
+ eku_bits |= PKINIT_EKU_MSSCLOGIN;
+ else if ((OBJ_cmp(certoid, OBJ_nid2obj(NID_client_auth))) == 0)
+ eku_bits |= PKINIT_EKU_CLIENTAUTH;
+ else if ((OBJ_cmp(certoid, OBJ_nid2obj(NID_email_protect))) == 0)
+ eku_bits |= PKINIT_EKU_EMAILPROTECTION;
+ }
+ EXTENDED_KEY_USAGE_free(eku);
+ }
+ }
+ pkiDebug("%s: returning eku 0x%08x\n", __FUNCTION__, eku_bits);
+ *ret_eku_bits = eku_bits;
+
+check_kus:
+ /* Now the Key Usage bits */
+ if (ret_ku_bits)
+ *ret_ku_bits = 0;
+ else {
+ pkiDebug("%s: KUs not requested, not checking\n", __FUNCTION__);
+ goto out;
+ }
+
+ /* Make sure usage exists before checking bits */
+ usage = X509_get_ext_d2i(x, NID_key_usage, NULL, NULL);
+ if (usage) {
+ if (!ku_reject(x, X509v3_KU_DIGITAL_SIGNATURE))
+ ku_bits |= PKINIT_KU_DIGITALSIGNATURE;
+ if (!ku_reject(x, X509v3_KU_KEY_ENCIPHERMENT))
+ ku_bits |= PKINIT_KU_KEYENCIPHERMENT;
+ ASN1_BIT_STRING_free(usage);
+ }
+
+ pkiDebug("%s: returning ku 0x%08x\n", __FUNCTION__, ku_bits);
+ *ret_ku_bits = ku_bits;
+ retval = 0;
+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)
+{
+ BIO *out = NULL;
+
+ out = BIO_new(BIO_s_mem ());
+ if (X509_NAME_print_ex(out, a, 0, flag) > 0) {
+ if (buf != NULL && *size > (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);
+}
+
+/*
+ * Get certificate information
+ */
+krb5_error_code
+crypto_cert_get_matching_data(krb5_context context,
+ pkinit_cert_handle ch,
+ pkinit_cert_matching_data **ret_md)
+{
+ 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;
+ int i, j;
+ char buf[DN_BUF_LEN];
+ unsigned int bufsize = sizeof(buf);
+
+ if (cd == NULL || cd->magic != CERT_MAGIC)
+ return EINVAL;
+ if (ret_md == NULL)
+ return EINVAL;
+
+ md = calloc(1, sizeof(*md));
+ if (md == NULL)
+ return ENOMEM;
+
+ md->ch = ch;
+
+ /* 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;
+ 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;
+ 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)
+ goto cleanup;
+
+ j = 0;
+ if (pkinit_sans != NULL) {
+ for (i = 0; pkinit_sans[i] != NULL; i++)
+ j++;
+ }
+ if (upn_sans != NULL) {
+ for (i = 0; upn_sans[i] != NULL; i++)
+ j++;
+ }
+ if (j != 0) {
+ md->sans = calloc((size_t)j+1, sizeof(*md->sans));
+ if (md->sans == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ j = 0;
+ if (pkinit_sans != NULL) {
+ for (i = 0; pkinit_sans[i] != NULL; i++)
+ md->sans[j++] = pkinit_sans[i];
+ free(pkinit_sans);
+ }
+ if (upn_sans != NULL) {
+ for (i = 0; upn_sans[i] != NULL; i++)
+ md->sans[j++] = upn_sans[i];
+ free(upn_sans);
+ }
+ md->sans[j] = NULL;
+ } 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)
+ goto cleanup;
+
+ *ret_md = md;
+ retval = 0;
+cleanup:
+ if (retval) {
+ if (md)
+ crypto_cert_free_matching_data(context, md);
+ }
+ return retval;
+}
+
+/*
+ * Free certificate information
+ */
+krb5_error_code
+crypto_cert_free_matching_data(krb5_context context,
+ pkinit_cert_matching_data *md)
+{
+ krb5_principal p;
+ int 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);
+ }
+ free(md);
+ return 0;
+}
+
+/*
+ * Make this matching certificate "the chosen one"
+ */
+krb5_error_code
+crypto_cert_select(krb5_context context,
+ pkinit_cert_matching_data *md)
+{
+ struct _pkinit_cert_data *cd;
+ if (md == NULL)
+ return EINVAL;
+
+ cd = (struct _pkinit_cert_data *)md->ch;
+ if (cd == NULL || cd->magic != CERT_MAGIC)
+ return EINVAL;
+
+ /* 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);
+ cd->idctx->creds[cd->index]->cert = NULL; /* Don't free it twice */
+ cd->idctx->cert_index = 0;
+
+ 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 */
+ }
+#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;
+ }
+#endif
+ return 0;
+}
+
+/*
+ * Choose the default certificate as "the chosen 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)
+{
+ krb5_error_code retval;
+ int cert_count = 0;
+
+ 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 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);
+ retval = EINVAL;
+ goto errout;
+ }
+ /* copy the selected cert into our id_cryptoctx */
+ if (id_cryptoctx->my_certs != NULL) {
+ sk_X509_pop_free(id_cryptoctx->my_certs, X509_free);
+ }
+ id_cryptoctx->my_certs = sk_X509_new_null();
+ sk_X509_push(id_cryptoctx->my_certs, id_cryptoctx->creds[0]->cert);
+ id_cryptoctx->creds[0]->cert = NULL; /* Don't free it twice */
+ id_cryptoctx->cert_index = 0;
+
+ if (id_cryptoctx->pkcs11_method != 1) {
+ id_cryptoctx->my_key = id_cryptoctx->creds[0]->key;
+ id_cryptoctx->creds[0]->key = NULL; /* Don't free it twice */
+ }
+#ifndef WITHOUT_PKCS11
+ else {
+ id_cryptoctx->cert_id = id_cryptoctx->creds[0]->cert_id;
+ id_cryptoctx->creds[0]->cert_id = NULL; /* Don't free it twice */
+ id_cryptoctx->cert_id_len = id_cryptoctx->creds[0]->cert_id_len;
+ }
+#endif
+ retval = 0;
+errout:
+ return retval;
+}
+
+
+
+static krb5_error_code
+load_cas_and_crls(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ int catype,
+ char *filename)
+{
+ STACK_OF(X509_INFO) *sk = NULL;
+ STACK_OF(X509) *ca_certs = NULL;
+ STACK_OF(X509_CRL) *ca_crls = NULL;
+ BIO *in = NULL;
+ krb5_error_code retval = ENOMEM;
+ int i = 0;
+
+ /* If there isn't already a stack in the context,
+ * create a temporary one now */
+ switch(catype) {
+ case CATYPE_ANCHORS:
+ if (id_cryptoctx->trustedCAs != NULL)
+ ca_certs = id_cryptoctx->trustedCAs;
+ else {
+ ca_certs = sk_X509_new_null();
+ if (ca_certs == NULL)
+ return ENOMEM;
+ }
+ break;
+ case CATYPE_INTERMEDIATES:
+ if (id_cryptoctx->intermediateCAs != NULL)
+ ca_certs = id_cryptoctx->intermediateCAs;
+ else {
+ ca_certs = sk_X509_new_null();
+ if (ca_certs == NULL)
+ return ENOMEM;
+ }
+ break;
+ case CATYPE_CRLS:
+ if (id_cryptoctx->revoked != NULL)
+ ca_crls = id_cryptoctx->revoked;
+ else {
+ ca_crls = sk_X509_CRL_new_null();
+ if (ca_crls == NULL)
+ return ENOMEM;
+ }
+ break;
+ default:
+ return ENOTSUP;
+ }
+
+ if (!(in = BIO_new_file(filename, "r"))) {
+ retval = errno;
+ pkiDebug("%s: error opening file '%s': %s\n", __FUNCTION__,
+ filename, error_message(errno));
+ goto cleanup;
+ }
+
+ /* This loads from a file, a stack of x509/crl/pkey sets */
+ if ((sk = PEM_X509_INFO_read_bio(in, NULL, NULL, NULL)) == NULL) {
+ pkiDebug("%s: error reading file '%s'\n", __FUNCTION__, filename);
+ retval = EIO;
+ goto cleanup;
+ }
+
+ /* scan over the stack created from loading the file contents,
+ * weed out duplicates, and push new ones onto the return stack
+ */
+ for (i = 0; i < sk_X509_INFO_num(sk); i++) {
+ X509_INFO *xi = sk_X509_INFO_value(sk, i);
+ if (xi != NULL && xi->x509 != NULL && catype != CATYPE_CRLS) {
+ int j = 0, size = sk_X509_num(ca_certs), flag = 0;
+
+ if (!size) {
+ sk_X509_push(ca_certs, xi->x509);
+ xi->x509 = NULL;
+ continue;
+ }
+ for (j = 0; j < size; j++) {
+ X509 *x = sk_X509_value(ca_certs, j);
+ flag = X509_cmp(x, xi->x509);
+ if (flag == 0)
+ break;
+ else
+ continue;
+ }
+ if (flag != 0) {
+ sk_X509_push(ca_certs, X509_dup(xi->x509));
+ }
+ } else if (xi != NULL && xi->crl != NULL && catype == CATYPE_CRLS) {
+ int j = 0, size = sk_X509_CRL_num(ca_crls), flag = 0;
+ if (!size) {
+ sk_X509_CRL_push(ca_crls, xi->crl);
+ xi->crl = NULL;
+ continue;
+ }
+ for (j = 0; j < size; j++) {
+ X509_CRL *x = sk_X509_CRL_value(ca_crls, j);
+ flag = X509_CRL_cmp(x, xi->crl);
+ if (flag == 0)
+ break;
+ else
+ continue;
+ }
+ if (flag != 0) {
+ sk_X509_push(ca_crls, X509_CRL_dup(xi->crl));
+ }
+ }
+ }
+
+ /* If we added something and there wasn't a stack in the
+ * context before, add the temporary stack to the context.
+ */
+ switch(catype) {
+ case CATYPE_ANCHORS:
+ if (sk_X509_num(ca_certs) == 0) {
+ pkiDebug("no anchors in file, %s\n", filename);
+ if (id_cryptoctx->trustedCAs == NULL)
+ sk_X509_free(ca_certs);
+ } else {
+ if (id_cryptoctx->trustedCAs == NULL)
+ id_cryptoctx->trustedCAs = ca_certs;
+ }
+ break;
+ case CATYPE_INTERMEDIATES:
+ if (sk_X509_num(ca_certs) == 0) {
+ pkiDebug("no intermediates in file, %s\n", filename);
+ if (id_cryptoctx->intermediateCAs == NULL)
+ sk_X509_free(ca_certs);
+ } else {
+ if (id_cryptoctx->intermediateCAs == NULL)
+ id_cryptoctx->intermediateCAs = ca_certs;
+ }
+ break;
+ case CATYPE_CRLS:
+ if (sk_X509_num(ca_crls) == 0) {
+ pkiDebug("no crls in file, %s\n", filename);
+ if (id_cryptoctx->revoked == NULL)
+ sk_X509_CRL_free(ca_crls);
+ } else {
+ if (id_cryptoctx->revoked == NULL)
+ id_cryptoctx->revoked = ca_crls;
+ }
+ break;
+ default:
+ /* Should have been caught above! */
+ retval = EINVAL;
+ goto cleanup;
+ break;
+ }
+
+ retval = 0;
+
+ cleanup:
+ if (in != NULL)
+ BIO_free(in);
+ if (sk != NULL)
+ sk_X509_INFO_pop_free(sk, X509_INFO_free);
+
+ return retval;
+}
+
+static krb5_error_code
+load_cas_and_crls_dir(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ int catype,
+ char *dirname)
+{
+ krb5_error_code retval = EINVAL;
+ DIR *d = NULL;
+ struct dirent *dentry = NULL;
+ char filename[1024];
+
+ if (dirname == NULL)
+ return EINVAL;
+
+ d = opendir(dirname);
+ if (d == NULL)
+ return ENOENT;
+
+ while ((dentry = readdir(d))) {
+ if (strlen(dirname) + strlen(dentry->d_name) + 2 > sizeof(filename)) {
+ pkiDebug("%s: Path too long -- directory '%s' and file '%s'\n",
+ __FUNCTION__, dirname, dentry->d_name);
+ goto cleanup;
+ }
+ /* Ignore subdirectories and anything starting with a dot */
+#ifdef DT_DIR
+ if (dentry->d_type == DT_DIR)
+ continue;
+#endif
+ if (dentry->d_name[0] == '.')
+ continue;
+ snprintf(filename, sizeof(filename), "%s/%s", dirname, dentry->d_name);
+
+ retval = load_cas_and_crls(context, plg_cryptoctx, req_cryptoctx,
+ id_cryptoctx, catype, filename);
+ if (retval)
+ goto cleanup;
+ }
+
+ retval = 0;
+
+ cleanup:
+ if (d != NULL)
+ closedir(d);
+
+ return retval;
+}
+
+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)
+{
+ pkiDebug("%s: called with idtype %s and catype %s\n",
+ __FUNCTION__, idtype2string(idtype), catype2string(catype));
+ switch (idtype) {
+ case IDTYPE_FILE:
+ return load_cas_and_crls(context, plg_cryptoctx, req_cryptoctx,
+ id_cryptoctx, catype, id);
+ break;
+ case IDTYPE_DIR:
+ return load_cas_and_crls_dir(context, plg_cryptoctx, req_cryptoctx,
+ id_cryptoctx, catype, id);
+ break;
+ default:
+ return ENOTSUP;
+ break;
+ }
+}
+
+static krb5_error_code
+create_identifiers_from_stack(STACK_OF(X509) *sk,
+ krb5_external_principal_identifier *** ids)
+{
+ krb5_error_code retval = ENOMEM;
+ int i = 0, sk_size = sk_X509_num(sk);
+ krb5_external_principal_identifier **krb5_cas = NULL;
+ X509 *x = NULL;
+ X509_NAME *xn = NULL;
+ unsigned char *p = NULL;
+ int len = 0;
+ PKCS7_ISSUER_AND_SERIAL *is = NULL;
+ char buf[DN_BUF_LEN];
+
+ *ids = NULL;
+
+ krb5_cas =
+ malloc((sk_size + 1) * sizeof(krb5_external_principal_identifier *));
+ if (krb5_cas == NULL)
+ return ENOMEM;
+ krb5_cas[sk_size] = NULL;
+
+ for (i = 0; i < sk_size; i++) {
+ krb5_cas[i] = (krb5_external_principal_identifier *)malloc(sizeof(krb5_external_principal_identifier));
+
+ x = sk_X509_value(sk, i);
+
+ X509_NAME_oneline(X509_get_subject_name(x), buf, sizeof(buf));
+ pkiDebug("#%d cert= %s\n", i, buf);
+
+ /* fill-in subjectName */
+ krb5_cas[i]->subjectName.magic = 0;
+ krb5_cas[i]->subjectName.length = 0;
+ krb5_cas[i]->subjectName.data = NULL;
+
+ xn = X509_get_subject_name(x);
+ len = i2d_X509_NAME(xn, NULL);
+ if ((p = krb5_cas[i]->subjectName.data = (unsigned char *)malloc((size_t) len)) == NULL)
+ goto cleanup;
+ i2d_X509_NAME(xn, &p);
+ krb5_cas[i]->subjectName.length = len;
+
+ /* fill-in issuerAndSerialNumber */
+ krb5_cas[i]->issuerAndSerialNumber.length = 0;
+ krb5_cas[i]->issuerAndSerialNumber.magic = 0;
+ krb5_cas[i]->issuerAndSerialNumber.data = NULL;
+
+#ifdef LONGHORN_BETA_COMPAT
+if (longhorn == 0) { /* XXX Longhorn doesn't like this */
+#endif
+ is = PKCS7_ISSUER_AND_SERIAL_new();
+ X509_NAME_set(&is->issuer, X509_get_issuer_name(x));
+ M_ASN1_INTEGER_free(is->serial);
+ is->serial = M_ASN1_INTEGER_dup(X509_get_serialNumber(x));
+ len = i2d_PKCS7_ISSUER_AND_SERIAL(is, NULL);
+ if ((p = krb5_cas[i]->issuerAndSerialNumber.data =
+ (unsigned char *)malloc((size_t) len)) == NULL)
+ goto cleanup;
+ i2d_PKCS7_ISSUER_AND_SERIAL(is, &p);
+ krb5_cas[i]->issuerAndSerialNumber.length = len;
+#ifdef LONGHORN_BETA_COMPAT
+}
+#endif
+
+ /* fill-in subjectKeyIdentifier */
+ krb5_cas[i]->subjectKeyIdentifier.length = 0;
+ krb5_cas[i]->subjectKeyIdentifier.magic = 0;
+ krb5_cas[i]->subjectKeyIdentifier.data = NULL;
+
+
+#ifdef LONGHORN_BETA_COMPAT
+if (longhorn == 0) { /* XXX Longhorn doesn't like this */
+#endif
+ if (X509_get_ext_by_NID(x, NID_subject_key_identifier, -1) >= 0) {
+ ASN1_OCTET_STRING *ikeyid = NULL;
+
+ if ((ikeyid = X509_get_ext_d2i(x, NID_subject_key_identifier, NULL,
+ NULL))) {
+ len = i2d_ASN1_OCTET_STRING(ikeyid, NULL);
+ if ((p = krb5_cas[i]->subjectKeyIdentifier.data =
+ (unsigned char *)malloc((size_t) len)) == NULL)
+ goto cleanup;
+ i2d_ASN1_OCTET_STRING(ikeyid, &p);
+ krb5_cas[i]->subjectKeyIdentifier.length = len;
+ }
+ if (ikeyid != NULL)
+ ASN1_OCTET_STRING_free(ikeyid);
+ }
+#ifdef LONGHORN_BETA_COMPAT
+}
+#endif
+ if (is != NULL) {
+ if (is->issuer != NULL)
+ X509_NAME_free(is->issuer);
+ if (is->serial != NULL)
+ ASN1_INTEGER_free(is->serial);
+ free(is);
+ }
+ }
+
+ *ids = krb5_cas;
+
+ retval = 0;
+ cleanup:
+ if (retval)
+ free_krb5_external_principal_identifier(&krb5_cas);
+
+ return retval;
+}
+
+static krb5_error_code
+create_krb5_invalidCertificates(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 *** ids)
+{
+
+ krb5_error_code retval = ENOMEM;
+ STACK_OF(X509) *sk = NULL;
+
+ *ids = NULL;
+ if (req_cryptoctx->received_cert == NULL)
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+
+ sk = sk_X509_new_null();
+ if (sk == NULL)
+ goto cleanup;
+ sk_X509_push(sk, req_cryptoctx->received_cert);
+
+ retval = create_identifiers_from_stack(sk, ids);
+
+ sk_X509_free(sk);
+cleanup:
+
+ return retval;
+}
+
+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 ***oids)
+{
+
+ krb5_error_code retval = ENOMEM;
+ krb5_algorithm_identifier **loids = NULL;
+ krb5_octet_data des3oid = {0, 8, (unsigned char *)"\x2A\x86\x48\x86\xF7\x0D\x03\x07" };
+
+ *oids = NULL;
+ loids = malloc(2 * sizeof(krb5_algorithm_identifier *));
+ if (loids == NULL)
+ goto cleanup;
+ loids[1] = NULL;
+ loids[0] = (krb5_algorithm_identifier *)malloc(sizeof(krb5_algorithm_identifier));
+ if (loids[0] == NULL) {
+ free(loids);
+ goto cleanup;
+ }
+ retval = pkinit_copy_krb5_octet_data(&loids[0]->algorithm, &des3oid);
+ if (retval) {
+ free(loids[0]);
+ free(loids);
+ goto cleanup;
+ }
+ loids[0]->parameters.length = 0;
+ loids[0]->parameters.data = NULL;
+
+ *oids = loids;
+ retval = 0;
+cleanup:
+
+ return retval;
+}
+
+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 *** ids)
+{
+
+ krb5_error_code retval = ENOMEM;
+ STACK_OF(X509) *sk = id_cryptoctx->trustedCAs;
+
+ *ids = NULL;
+ if (id_cryptoctx->trustedCAs == NULL)
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+
+ retval = create_identifiers_from_stack(sk, ids);
+
+ return retval;
+}
+
+krb5_error_code
+create_krb5_trustedCas(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ int flag,
+ krb5_trusted_ca *** ids)
+{
+ krb5_error_code retval = ENOMEM;
+ STACK_OF(X509) *sk = id_cryptoctx->trustedCAs;
+ int i = 0, len = 0, sk_size = sk_X509_num(sk);
+ krb5_trusted_ca **krb5_cas = NULL;
+ X509 *x = NULL;
+ char buf[DN_BUF_LEN];
+ X509_NAME *xn = NULL;
+ unsigned char *p = NULL;
+ PKCS7_ISSUER_AND_SERIAL *is = NULL;
+
+ *ids = NULL;
+ if (id_cryptoctx->trustedCAs == NULL)
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+
+ krb5_cas = malloc((sk_size + 1) * sizeof(krb5_trusted_ca *));
+ if (krb5_cas == NULL)
+ return ENOMEM;
+ krb5_cas[sk_size] = NULL;
+
+ for (i = 0; i < sk_size; i++) {
+ krb5_cas[i] = (krb5_trusted_ca *)malloc(sizeof(krb5_trusted_ca));
+ if (krb5_cas[i] == NULL)
+ goto cleanup;
+ x = sk_X509_value(sk, i);
+
+ X509_NAME_oneline(X509_get_subject_name(x), buf, sizeof(buf));
+ pkiDebug("#%d cert= %s\n", i, buf);
+
+ switch (flag) {
+ case choice_trusted_cas_principalName:
+ krb5_cas[i]->choice = choice_trusted_cas_principalName;
+ break;
+ case choice_trusted_cas_caName:
+ krb5_cas[i]->choice = choice_trusted_cas_caName;
+ krb5_cas[i]->u.caName.data = NULL;
+ krb5_cas[i]->u.caName.length = 0;
+ xn = X509_get_subject_name(x);
+ len = i2d_X509_NAME(xn, NULL);
+ if ((p = krb5_cas[i]->u.caName.data =
+ (unsigned char *)malloc((size_t) len)) == NULL)
+ goto cleanup;
+ i2d_X509_NAME(xn, &p);
+ krb5_cas[i]->u.caName.length = len;
+ break;
+ case choice_trusted_cas_issuerAndSerial:
+ krb5_cas[i]->choice = choice_trusted_cas_issuerAndSerial;
+ krb5_cas[i]->u.issuerAndSerial.data = NULL;
+ krb5_cas[i]->u.issuerAndSerial.length = 0;
+ is = PKCS7_ISSUER_AND_SERIAL_new();
+ X509_NAME_set(&is->issuer, X509_get_issuer_name(x));
+ M_ASN1_INTEGER_free(is->serial);
+ is->serial = M_ASN1_INTEGER_dup(X509_get_serialNumber(x));
+ len = i2d_PKCS7_ISSUER_AND_SERIAL(is, NULL);
+ if ((p = krb5_cas[i]->u.issuerAndSerial.data =
+ (unsigned char *)malloc((size_t) len)) == NULL)
+ goto cleanup;
+ i2d_PKCS7_ISSUER_AND_SERIAL(is, &p);
+ krb5_cas[i]->u.issuerAndSerial.length = len;
+ if (is != NULL) {
+ if (is->issuer != NULL)
+ X509_NAME_free(is->issuer);
+ if (is->serial != NULL)
+ ASN1_INTEGER_free(is->serial);
+ free(is);
+ }
+ break;
+ default: break;
+ }
+ }
+ retval = 0;
+ *ids = krb5_cas;
+cleanup:
+ if (retval)
+ free_krb5_trusted_ca(&krb5_cas);
+
+ return retval;
+}
+
+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 **out,
+ unsigned int *out_len)
+{
+ unsigned char *p = NULL;
+ PKCS7_ISSUER_AND_SERIAL *is = NULL;
+ int len = 0;
+ krb5_error_code retval = ENOMEM;
+ X509 *cert = req_cryptoctx->received_cert;
+
+ *out = NULL;
+ *out_len = 0;
+ if (req_cryptoctx->received_cert == NULL)
+ return 0;
+
+ is = PKCS7_ISSUER_AND_SERIAL_new();
+ X509_NAME_set(&is->issuer, X509_get_issuer_name(cert));
+ M_ASN1_INTEGER_free(is->serial);
+ is->serial = M_ASN1_INTEGER_dup(X509_get_serialNumber(cert));
+ len = i2d_PKCS7_ISSUER_AND_SERIAL(is, NULL);
+ if ((p = *out = (unsigned char *)malloc((size_t) len)) == NULL)
+ goto cleanup;
+ i2d_PKCS7_ISSUER_AND_SERIAL(is, &p);
+ *out_len = len;
+ retval = 0;
+
+cleanup:
+ X509_NAME_free(is->issuer);
+ ASN1_INTEGER_free(is->serial);
+ free(is);
+
+ return retval;
+}
+
+static int
+pkcs7_decrypt(krb5_context context,
+ pkinit_identity_crypto_context id_cryptoctx,
+ PKCS7 *p7,
+ BIO *data)
+{
+ BIO *tmpmem = NULL;
+ int retval = 0, i = 0;
+ char buf[4096];
+
+ if(p7 == NULL)
+ return 0;
+
+ if(!PKCS7_type_is_enveloped(p7)) {
+ pkiDebug("wrong pkcs7 content type\n");
+ return 0;
+ }
+
+ if(!(tmpmem = pkcs7_dataDecode(context, id_cryptoctx, p7))) {
+ pkiDebug("unable to decrypt pkcs7 object\n");
+ return 0;
+ }
+
+ for(;;) {
+ i = BIO_read(tmpmem, buf, sizeof(buf));
+ if (i <= 0) break;
+ BIO_write(data, buf, i);
+ BIO_free_all(tmpmem);
+ return 1;
+ }
+ return retval;
+}
+
+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 **krb5_trusted_certifiers,
+ int td_type)
+{
+ krb5_error_code retval = ENOMEM;
+ STACK_OF(X509_NAME) *sk_xn = NULL;
+ X509_NAME *xn = NULL;
+ PKCS7_ISSUER_AND_SERIAL *is = NULL;
+ ASN1_OCTET_STRING *id = NULL;
+ const unsigned char *p = NULL;
+ char buf[DN_BUF_LEN];
+ int i = 0;
+
+ if (td_type == TD_TRUSTED_CERTIFIERS)
+ pkiDebug("received trusted certifiers\n");
+ else
+ pkiDebug("received invalid certificate\n");
+
+ sk_xn = sk_X509_NAME_new_null();
+ while(krb5_trusted_certifiers[i] != NULL) {
+ if (krb5_trusted_certifiers[i]->subjectName.data != NULL) {
+ p = krb5_trusted_certifiers[i]->subjectName.data;
+ xn = d2i_X509_NAME(NULL, &p,
+ (int)krb5_trusted_certifiers[i]->subjectName.length);
+ if (xn == NULL)
+ goto cleanup;
+ X509_NAME_oneline(xn, buf, sizeof(buf));
+ if (td_type == TD_TRUSTED_CERTIFIERS)
+ pkiDebug("#%d cert = %s is trusted by kdc\n", i, buf);
+ else
+ pkiDebug("#%d cert = %s is invalid\n", i, buf);
+ sk_X509_NAME_push(sk_xn, xn);
+ }
+
+ if (krb5_trusted_certifiers[i]->issuerAndSerialNumber.data != NULL) {
+ p = krb5_trusted_certifiers[i]->issuerAndSerialNumber.data;
+ is = d2i_PKCS7_ISSUER_AND_SERIAL(NULL, &p,
+ (int)krb5_trusted_certifiers[i]->issuerAndSerialNumber.length);
+ if (is == NULL)
+ goto cleanup;
+ X509_NAME_oneline(is->issuer, buf, sizeof(buf));
+ if (td_type == TD_TRUSTED_CERTIFIERS)
+ pkiDebug("#%d issuer = %s serial = %ld is trusted bu kdc\n", i,
+ buf, ASN1_INTEGER_get(is->serial));
+ else
+ pkiDebug("#%d issuer = %s serial = %ld is invalid\n", i, buf,
+ ASN1_INTEGER_get(is->serial));
+ PKCS7_ISSUER_AND_SERIAL_free(is);
+ }
+
+ if (krb5_trusted_certifiers[i]->subjectKeyIdentifier.data != NULL) {
+ p = krb5_trusted_certifiers[i]->subjectKeyIdentifier.data;
+ id = d2i_ASN1_OCTET_STRING(NULL, &p,
+ (int)krb5_trusted_certifiers[i]->subjectKeyIdentifier.length);
+ if (id == NULL)
+ goto cleanup;
+ /* XXX */
+ ASN1_OCTET_STRING_free(id);
+ }
+ i++;
+ }
+ /* XXX Since we not doing anything with received trusted certifiers
+ * return an error. this is the place where we can pick a different
+ * client certificate based on the information in td_trusted_certifiers
+ */
+ retval = KRB5KDC_ERR_PREAUTH_FAILED;
+cleanup:
+ if (sk_xn != NULL)
+ sk_X509_NAME_pop_free(sk_xn, X509_NAME_free);
+
+ return retval;
+}
+
+static BIO *
+pkcs7_dataDecode(krb5_context context,
+ pkinit_identity_crypto_context id_cryptoctx,
+ PKCS7 *p7)
+{
+ int i = 0;
+ unsigned int jj = 0, tmp_len = 0;
+ BIO *out=NULL,*etmp=NULL,*bio=NULL;
+ unsigned char *tmp=NULL;
+ ASN1_OCTET_STRING *data_body=NULL;
+ const EVP_CIPHER *evp_cipher=NULL;
+ EVP_CIPHER_CTX *evp_ctx=NULL;
+ X509_ALGOR *enc_alg=NULL;
+ STACK_OF(PKCS7_RECIP_INFO) *rsk=NULL;
+ X509_ALGOR *xalg=NULL;
+ PKCS7_RECIP_INFO *ri=NULL;
+ X509 *cert = sk_X509_value(id_cryptoctx->my_certs,
+ id_cryptoctx->cert_index);
+
+ p7->state=PKCS7_S_HEADER;
+
+ rsk=p7->d.enveloped->recipientinfo;
+ enc_alg=p7->d.enveloped->enc_data->algorithm;
+ data_body=p7->d.enveloped->enc_data->enc_data;
+ evp_cipher=EVP_get_cipherbyobj(enc_alg->algorithm);
+ if (evp_cipher == NULL) {
+ PKCS7err(PKCS7_F_PKCS7_DATADECODE,PKCS7_R_UNSUPPORTED_CIPHER_TYPE);
+ goto cleanup;
+ }
+ xalg=p7->d.enveloped->enc_data->algorithm;
+
+ if ((etmp=BIO_new(BIO_f_cipher())) == NULL) {
+ PKCS7err(PKCS7_F_PKCS7_DATADECODE,ERR_R_BIO_LIB);
+ goto cleanup;
+ }
+
+ /* It was encrypted, we need to decrypt the secret key
+ * with the private key */
+
+ /* Find the recipientInfo which matches the passed certificate
+ * (if any)
+ */
+
+ if (cert) {
+ for (i=0; i<sk_PKCS7_RECIP_INFO_num(rsk); i++) {
+ int tmp_ret = 0;
+ ri=sk_PKCS7_RECIP_INFO_value(rsk,i);
+ tmp_ret = X509_NAME_cmp(ri->issuer_and_serial->issuer,
+ cert->cert_info->issuer);
+ if (!tmp_ret) {
+ tmp_ret = M_ASN1_INTEGER_cmp(cert->cert_info->serialNumber,
+ ri->issuer_and_serial->serial);
+ if (!tmp_ret)
+ break;
+ }
+ ri=NULL;
+ }
+ if (ri == NULL) {
+ PKCS7err(PKCS7_F_PKCS7_DATADECODE,
+ PKCS7_R_NO_RECIPIENT_MATCHES_CERTIFICATE);
+ goto cleanup;
+ }
+
+ }
+
+ /* If we haven't got a certificate try each ri in turn */
+
+ if (cert == NULL) {
+ for (i=0; i<sk_PKCS7_RECIP_INFO_num(rsk); i++) {
+ ri=sk_PKCS7_RECIP_INFO_value(rsk,i);
+ jj = pkinit_decode_data(context, id_cryptoctx,
+ M_ASN1_STRING_data(ri->enc_key),
+ (unsigned int) M_ASN1_STRING_length(ri->enc_key),
+ &tmp, &tmp_len);
+ if (jj) {
+ PKCS7err(PKCS7_F_PKCS7_DATADECODE, ERR_R_EVP_LIB);
+ goto cleanup;
+ }
+
+ if (!jj && tmp_len > 0) {
+ jj = tmp_len;
+ break;
+ }
+
+ ERR_clear_error();
+ ri = NULL;
+ }
+
+ if (ri == NULL) {
+ PKCS7err(PKCS7_F_PKCS7_DATADECODE, PKCS7_R_NO_RECIPIENT_MATCHES_KEY);
+ goto cleanup;
+ }
+ }
+ else {
+ jj = pkinit_decode_data(context, id_cryptoctx,
+ M_ASN1_STRING_data(ri->enc_key),
+ (unsigned int) M_ASN1_STRING_length(ri->enc_key),
+ &tmp, &tmp_len);
+ if (jj || tmp_len <= 0) {
+ PKCS7err(PKCS7_F_PKCS7_DATADECODE, ERR_R_EVP_LIB);
+ goto cleanup;
+ }
+ jj = tmp_len;
+ }
+
+ evp_ctx=NULL;
+ BIO_get_cipher_ctx(etmp,&evp_ctx);
+ if (EVP_CipherInit_ex(evp_ctx,evp_cipher,NULL,NULL,NULL,0) <= 0)
+ goto cleanup;
+ if (EVP_CIPHER_asn1_to_param(evp_ctx,enc_alg->parameter) < 0)
+ goto cleanup;
+
+ if (jj != EVP_CIPHER_CTX_key_length(evp_ctx)) {
+ /* Some S/MIME clients don't use the same key
+ * and effective key length. The key length is
+ * determined by the size of the decrypted RSA key.
+ */
+ if(!EVP_CIPHER_CTX_set_key_length(evp_ctx, (int)jj)) {
+ PKCS7err(PKCS7_F_PKCS7_DATADECODE,
+ PKCS7_R_DECRYPTED_KEY_IS_WRONG_LENGTH);
+ goto cleanup;
+ }
+ }
+ if (EVP_CipherInit_ex(evp_ctx,NULL,NULL,tmp,NULL,0) <= 0)
+ goto cleanup;
+
+ OPENSSL_cleanse(tmp,jj);
+
+ if (out == NULL)
+ out=etmp;
+ else
+ BIO_push(out,etmp);
+ etmp=NULL;
+
+ if (data_body->length > 0)
+ bio = BIO_new_mem_buf(data_body->data, data_body->length);
+ else {
+ bio=BIO_new(BIO_s_mem());
+ BIO_set_mem_eof_return(bio,0);
+ }
+ BIO_push(out,bio);
+ bio=NULL;
+
+ if (0) {
+cleanup:
+ if (out != NULL) BIO_free_all(out);
+ if (etmp != NULL) BIO_free_all(etmp);
+ if (bio != NULL) BIO_free_all(bio);
+ out=NULL;
+ }
+
+ if (tmp != NULL)
+ free(tmp);
+
+ return(out);
+}
+
+static krb5_error_code
+der_decode_data(unsigned char *data, long data_len,
+ unsigned char **out, long *out_len)
+{
+ krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ ASN1_OCTET_STRING *s = NULL;
+ const unsigned char *p = data;
+
+ if ((s = d2i_ASN1_BIT_STRING(NULL, &p, data_len)) == NULL)
+ goto cleanup;
+ *out_len = s->length;
+ if ((*out = (unsigned char *) malloc((size_t) *out_len + 1)) == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ memcpy(*out, s->data, (size_t) s->length);
+ (*out)[s->length] = '\0';
+
+ retval = 0;
+ cleanup:
+ if (s != NULL)
+ ASN1_OCTET_STRING_free(s);
+
+ return retval;
+}
+
+
+#ifdef DEBUG_DH
+static void
+print_dh(DH * dh, char *msg)
+{
+ BIO *bio_err = NULL;
+
+ bio_err = BIO_new(BIO_s_file());
+ BIO_set_fp(bio_err, stderr, BIO_NOCLOSE | BIO_FP_TEXT);
+
+ if (msg)
+ BIO_puts(bio_err, (const char *)msg);
+ if (dh)
+ DHparams_print(bio_err, dh);
+
+ BN_print(bio_err, dh->q);
+ BIO_puts(bio_err, (const char *)"\n");
+ BIO_free(bio_err);
+
+}
+
+static void
+print_pubkey(BIGNUM * key, char *msg)
+{
+ BIO *bio_err = NULL;
+
+ bio_err = BIO_new(BIO_s_file());
+ BIO_set_fp(bio_err, stderr, BIO_NOCLOSE | BIO_FP_TEXT);
+
+ if (msg)
+ BIO_puts(bio_err, (const char *)msg);
+ if (key)
+ BN_print(bio_err, key);
+ BIO_puts(bio_err, "\n");
+
+ BIO_free(bio_err);
+
+}
+#endif
+
+static char *
+pkinit_pkcs11_code_to_text(int err)
+{
+ int i;
+ static char uc[32];
+
+ for (i = 0; pkcs11_errstrings[i].text != NULL; i++)
+ if (pkcs11_errstrings[i].code == err)
+ break;
+ if (pkcs11_errstrings[i].text != NULL)
+ return (pkcs11_errstrings[i].text);
+ sprintf(uc, "unknown code 0x%x", err);
+ return (uc);
+}
diff --git a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.h b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.h
new file mode 100644
index 0000000..9c824c8
--- /dev/null
+++ b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.h
@@ -0,0 +1,265 @@
+/*
+ * COPYRIGHT (C) 2006,2007
+ * THE REGENTS OF THE UNIVERSITY OF MICHIGAN
+ * ALL RIGHTS RESERVED
+ *
+ * Permission is granted to use, copy, create derivative works
+ * and redistribute this software and such derivative works
+ * for any purpose, so long as the name of The University of
+ * Michigan is not used in any advertising or publicity
+ * pertaining to the use of distribution of this software
+ * without specific, written prior authorization. If the
+ * above copyright notice or any other identification of the
+ * University of Michigan is included in any copy of any
+ * portion of this software, then the disclaimer below must
+ * also be included.
+ *
+ * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
+ * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
+ * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
+ * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
+ * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
+ * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGES.
+ */
+
+#ifndef _PKINIT_CRYPTO_OPENSSL_H
+#define _PKINIT_CRYPTO_OPENSSL_H
+
+#include <openssl/bn.h>
+#include <openssl/dh.h>
+#include <openssl/x509.h>
+#include <openssl/pkcs7.h>
+#include <openssl/pkcs12.h>
+#include <openssl/obj_mac.h>
+#include <openssl/x509v3.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/asn1_mac.h>
+#include <openssl/sha.h>
+#include <openssl/asn1.h>
+#include <openssl/pem.h>
+
+#include "pkinit.h"
+
+#define DN_BUF_LEN 256
+#define MAX_CREDS_ALLOWED 20
+
+struct _pkinit_cred_info {
+ X509 *cert;
+ EVP_PKEY *key;
+#ifndef WITHOUT_PKCS11
+ CK_BYTE_PTR cert_id;
+ int cert_id_len;
+#endif
+};
+typedef struct _pkinit_cred_info * pkinit_cred_info;
+
+struct _pkinit_identity_crypto_context {
+ pkinit_cred_info creds[MAX_CREDS_ALLOWED+1];
+ STACK_OF(X509) *my_certs; /* available user certs */
+ int cert_index; /* cert to use out of available certs*/
+ EVP_PKEY *my_key; /* available user keys if in filesystem */
+ STACK_OF(X509) *trustedCAs; /* available trusted ca certs */
+ STACK_OF(X509) *intermediateCAs; /* available intermediate ca certs */
+ STACK_OF(X509_CRL) *revoked; /* available crls */
+ int pkcs11_method;
+ krb5_prompter_fct prompter;
+ void *prompter_data;
+#ifndef WITHOUT_PKCS11
+ char *p11_module_name;
+ CK_SLOT_ID slotid;
+ char *token_label;
+ char *cert_label;
+ /* These are crypto-specific */
+ void *p11_module;
+ CK_SESSION_HANDLE session;
+ CK_FUNCTION_LIST_PTR p11;
+ CK_BYTE_PTR cert_id;
+ int cert_id_len;
+ CK_MECHANISM_TYPE mech;
+#endif
+};
+
+struct _pkinit_plg_crypto_context {
+ DH *dh_1024;
+ DH *dh_2048;
+ DH *dh_4096;
+ ASN1_OBJECT *id_pkinit_authData;
+ ASN1_OBJECT *id_pkinit_authData9;
+ ASN1_OBJECT *id_pkinit_DHKeyData;
+ ASN1_OBJECT *id_pkinit_rkeyData;
+ ASN1_OBJECT *id_pkinit_san;
+ ASN1_OBJECT *id_ms_san_upn;
+ ASN1_OBJECT *id_pkinit_KPClientAuth;
+ ASN1_OBJECT *id_pkinit_KPKdc;
+ ASN1_OBJECT *id_ms_kp_sc_logon;
+ ASN1_OBJECT *id_kp_serverAuth;
+};
+
+struct _pkinit_req_crypto_context {
+ X509 *received_cert;
+ 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;
+};
+
+static void openssl_init(void);
+
+static krb5_error_code pkinit_init_pkinit_oids(pkinit_plg_crypto_context );
+static void pkinit_fini_pkinit_oids(pkinit_plg_crypto_context );
+
+static krb5_error_code pkinit_init_dh_params(pkinit_plg_crypto_context );
+static void pkinit_fini_dh_params(pkinit_plg_crypto_context );
+
+static krb5_error_code pkinit_init_certs(pkinit_identity_crypto_context ctx);
+static void pkinit_fini_certs(pkinit_identity_crypto_context ctx);
+
+static krb5_error_code pkinit_init_pkcs11(pkinit_identity_crypto_context ctx);
+static void pkinit_fini_pkcs11(pkinit_identity_crypto_context ctx);
+
+static krb5_error_code pkinit_encode_dh_params
+ (BIGNUM *, BIGNUM *, BIGNUM *, unsigned char **, unsigned int *);
+static DH *pkinit_decode_dh_params
+ (DH **, unsigned char **, unsigned int );
+static int pkinit_check_dh_params
+ (BIGNUM * p1, BIGNUM * p2, BIGNUM * g1, BIGNUM * q1);
+
+static krb5_error_code pkinit_sign_data
+ (krb5_context context, pkinit_identity_crypto_context cryptoctx,
+ unsigned char *data, unsigned int data_len,
+ unsigned char **sig, unsigned int *sig_len);
+
+static krb5_error_code create_signature
+ (unsigned char **, unsigned int *, unsigned char *, unsigned int,
+ EVP_PKEY *pkey);
+
+static krb5_error_code pkinit_decode_data
+ (krb5_context context, pkinit_identity_crypto_context cryptoctx,
+ unsigned char *data, unsigned int data_len,
+ unsigned char **decoded, unsigned int *decoded_len);
+
+static krb5_error_code decode_data
+ (unsigned char **, unsigned int *, unsigned char *, unsigned int,
+ EVP_PKEY *pkey, X509 *cert);
+
+#ifdef DEBUG_DH
+static void print_dh(DH *, char *);
+static void print_pubkey(BIGNUM *, char *);
+#endif
+
+static int prepare_enc_data
+ (unsigned char *indata, int indata_len, unsigned char **outdata,
+ int *outdata_len);
+
+static int openssl_callback (int, X509_STORE_CTX *);
+static int openssl_callback_ignore_crls (int, X509_STORE_CTX *);
+
+static int pkcs7_decrypt
+ (krb5_context context, pkinit_identity_crypto_context id_cryptoctx,
+ PKCS7 *p7, BIO *bio);
+
+static BIO * pkcs7_dataDecode
+ (krb5_context context, pkinit_identity_crypto_context id_cryptoctx,
+ PKCS7 *p7);
+
+static ASN1_OBJECT * pkinit_pkcs7type2oid
+ (pkinit_plg_crypto_context plg_cryptoctx, int pkcs7_type);
+
+static krb5_error_code pkinit_create_sequence_of_principal_identifiers
+ (krb5_context context, pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ int type, krb5_data **out_data);
+
+#ifndef WITHOUT_PKCS11
+static krb5_error_code pkinit_find_private_key
+ (pkinit_identity_crypto_context, CK_ATTRIBUTE_TYPE usage,
+ CK_OBJECT_HANDLE *objp);
+static krb5_error_code pkinit_login
+ (krb5_context context, pkinit_identity_crypto_context id_cryptoctx,
+ CK_TOKEN_INFO *tip);
+static krb5_error_code pkinit_open_session
+ (krb5_context context, pkinit_identity_crypto_context id_cryptoctx);
+static void * pkinit_C_LoadModule(const char *modname, CK_FUNCTION_LIST_PTR_PTR p11p);
+static CK_RV pkinit_C_UnloadModule(void *handle);
+#ifdef SILLYDECRYPT
+CK_RV pkinit_C_Decrypt
+ (pkinit_identity_crypto_context id_cryptoctx,
+ CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen,
+ CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen);
+#endif
+
+static krb5_error_code pkinit_sign_data_pkcs11
+ (krb5_context context, pkinit_identity_crypto_context id_cryptoctx,
+ unsigned char *data, unsigned int data_len,
+ unsigned char **sig, unsigned int *sig_len);
+static krb5_error_code pkinit_decode_data_pkcs11
+ (krb5_context context, pkinit_identity_crypto_context id_cryptoctx,
+ unsigned char *data, unsigned int data_len,
+ unsigned char **decoded_data, unsigned int *decoded_data_len);
+#endif /* WITHOUT_PKCS11 */
+
+static krb5_error_code pkinit_sign_data_fs
+ (krb5_context context, pkinit_identity_crypto_context id_cryptoctx,
+ unsigned char *data, unsigned int data_len,
+ unsigned char **sig, unsigned int *sig_len);
+static krb5_error_code pkinit_decode_data_fs
+ (krb5_context context, pkinit_identity_crypto_context id_cryptoctx,
+ unsigned char *data, unsigned int data_len,
+ unsigned char **decoded_data, unsigned int *decoded_data_len);
+
+static krb5_error_code der_decode_data
+ (unsigned char *, long, unsigned char **, long *);
+
+static krb5_error_code
+create_krb5_invalidCertificates(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 *** ids);
+
+static krb5_error_code
+create_identifiers_from_stack(STACK_OF(X509) *sk,
+ krb5_external_principal_identifier *** ids);
+#ifdef LONGHORN_BETA_COMPAT
+static int
+wrap_signeddata(unsigned char *data, unsigned int data_len,
+ unsigned char **out, unsigned int *out_len,
+ int is_longhorn_server);
+#else
+static int
+wrap_signeddata(unsigned char *data, unsigned int data_len,
+ unsigned char **out, unsigned int *out_len);
+#endif
+
+/* This handy macro borrowed from crypto/x509v3/v3_purp.c */
+#define ku_reject(x, usage) \
+ (((x)->ex_flags & EXFLAG_KUSAGE) && !((x)->ex_kusage & (usage)))
+
+static char *
+pkinit_pkcs11_code_to_text(int err);
+
+#endif /* _PKINIT_CRYPTO_OPENSSL_H */
diff --git a/src/plugins/preauth/pkinit/pkinit_identity.c b/src/plugins/preauth/pkinit/pkinit_identity.c
new file mode 100644
index 0000000..227c55d
--- /dev/null
+++ b/src/plugins/preauth/pkinit/pkinit_identity.c
@@ -0,0 +1,668 @@
+/*
+ * COPYRIGHT (C) 2007
+ * THE REGENTS OF THE UNIVERSITY OF MICHIGAN
+ * ALL RIGHTS RESERVED
+ *
+ * Permission is granted to use, copy, create derivative works
+ * and redistribute this software and such derivative works
+ * for any purpose, so long as the name of The University of
+ * Michigan is not used in any advertising or publicity
+ * pertaining to the use of distribution of this software
+ * without specific, written prior authorization. If the
+ * above copyright notice or any other identification of the
+ * University of Michigan is included in any copy of any
+ * portion of this software, then the disclaimer below must
+ * also be included.
+ *
+ * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
+ * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
+ * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
+ * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
+ * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
+ * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGES.
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#include "pkinit.h"
+
+static void
+free_list(char **list)
+{
+ int i;
+
+ if (list == NULL)
+ return;
+
+ for (i = 0; list[i] != NULL; i++)
+ free(list[i]);
+ free(list);
+}
+
+static krb5_error_code
+copy_list(char ***dst, char **src)
+{
+ int i;
+ char **newlist;
+
+ if (dst == NULL)
+ return EINVAL;
+ *dst = NULL;
+
+ if (src == NULL)
+ return 0;
+
+ for (i = 0; src[i] != NULL; i++);
+
+ newlist = calloc(1, (i + 1) * sizeof(*newlist));
+ if (newlist == NULL)
+ return ENOMEM;
+
+ for (i = 0; src[i] != NULL; i++) {
+ newlist[i] = strdup(src[i]);
+ if (newlist[i] == NULL)
+ goto cleanup;
+ }
+ newlist[i] = NULL;
+ *dst = newlist;
+ return 0;
+cleanup:
+ free_list(newlist);
+ return ENOMEM;
+}
+
+char *
+idtype2string(int idtype)
+{
+ switch(idtype) {
+ case IDTYPE_FILE: return "FILE"; break;
+ case IDTYPE_DIR: return "DIR"; break;
+ case IDTYPE_PKCS11: return "PKCS11"; break;
+ case IDTYPE_PKCS12: return "PKCS12"; break;
+ case IDTYPE_ENVVAR: return "ENV"; break;
+ default: return "INVALID"; break;
+ }
+}
+
+char *
+catype2string(int catype)
+{
+ switch(catype) {
+ case CATYPE_ANCHORS: return "ANCHORS"; break;
+ case CATYPE_INTERMEDIATES: return "INTERMEDIATES"; break;
+ case CATYPE_CRLS: return "CRLS"; break;
+ default: return "INVALID"; break;
+ }
+}
+
+krb5_error_code
+pkinit_init_identity_opts(pkinit_identity_opts **idopts)
+{
+ pkinit_identity_opts *opts = NULL;
+
+ *idopts = NULL;
+ opts = (pkinit_identity_opts *) calloc(1, sizeof(pkinit_identity_opts));
+ if (opts == NULL)
+ return ENOMEM;
+
+ opts->identity = NULL;
+ opts->anchors = NULL;
+ opts->intermediates = NULL;
+ opts->crls = NULL;
+ opts->ocsp = NULL;
+ opts->dn_mapping_file = NULL;
+
+ opts->cert_filename = NULL;
+ opts->key_filename = NULL;
+#ifndef WITHOUT_PKCS11
+ opts->p11_module_name = NULL;
+ opts->slotid = PK_NOSLOT;
+ opts->token_label = NULL;
+ opts->cert_id_string = NULL;
+ opts->cert_label = NULL;
+#endif
+
+ *idopts = opts;
+
+ return 0;
+}
+
+krb5_error_code
+pkinit_dup_identity_opts(pkinit_identity_opts *src_opts,
+ pkinit_identity_opts **dest_opts)
+{
+ pkinit_identity_opts *newopts;
+ krb5_error_code retval;
+
+ *dest_opts = NULL;
+ retval = pkinit_init_identity_opts(&newopts);
+ if (retval)
+ return retval;
+
+ retval = ENOMEM;
+
+ if (src_opts->identity != NULL) {
+ newopts->identity = strdup(src_opts->identity);
+ if (newopts->identity == NULL)
+ goto cleanup;
+ }
+
+ retval = copy_list(&newopts->anchors, src_opts->anchors);
+ if (retval)
+ goto cleanup;
+
+ retval = copy_list(&newopts->intermediates,src_opts->intermediates);
+ if (retval)
+ goto cleanup;
+
+ retval = copy_list(&newopts->crls, src_opts->crls);
+ 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)
+ goto cleanup;
+ }
+
+ if (src_opts->key_filename != NULL) {
+ newopts->key_filename = strdup(src_opts->key_filename);
+ if (newopts->key_filename == NULL)
+ goto cleanup;
+ }
+
+#ifndef WITHOUT_PKCS11
+ if (src_opts->p11_module_name != NULL) {
+ newopts->p11_module_name = strdup(src_opts->p11_module_name);
+ if (newopts->p11_module_name == NULL)
+ goto cleanup;
+ }
+
+ newopts->slotid = src_opts->slotid;
+
+ if (src_opts->token_label != NULL) {
+ newopts->token_label = strdup(src_opts->token_label);
+ if (newopts->token_label == NULL)
+ goto cleanup;
+ }
+
+ if (src_opts->cert_id_string != NULL) {
+ newopts->cert_id_string = strdup(src_opts->cert_id_string);
+ if (newopts->cert_id_string == NULL)
+ goto cleanup;
+ }
+
+ if (src_opts->cert_label != NULL) {
+ newopts->cert_label = strdup(src_opts->cert_label);
+ if (newopts->cert_label == NULL)
+ goto cleanup;
+ }
+#endif
+
+
+ *dest_opts = newopts;
+ return 0;
+cleanup:
+ pkinit_fini_identity_opts(newopts);
+ return retval;
+}
+
+void
+pkinit_fini_identity_opts(pkinit_identity_opts *idopts)
+{
+ if (idopts == NULL)
+ return;
+
+ if (idopts->identity != NULL)
+ free(idopts->identity);
+ free_list(idopts->anchors);
+ free_list(idopts->intermediates);
+ free_list(idopts->crls);
+ free_list(idopts->identity_alt);
+
+ if (idopts->cert_filename != NULL)
+ free(idopts->cert_filename);
+ if (idopts->key_filename != NULL)
+ free(idopts->key_filename);
+#ifndef WITHOUT_PKCS11
+ if (idopts->p11_module_name != NULL)
+ free(idopts->p11_module_name);
+ if (idopts->token_label != NULL)
+ free(idopts->token_label);
+ if (idopts->cert_id_string != NULL)
+ free(idopts->cert_id_string);
+ if (idopts->cert_label != NULL)
+ free(idopts->cert_label);
+#endif
+ free(idopts);
+}
+
+#ifndef WITHOUT_PKCS11
+static krb5_error_code
+parse_pkcs11_options(krb5_context context,
+ pkinit_identity_opts *idopts,
+ const char *residual)
+{
+ char *s, *cp, *vp;
+ krb5_error_code retval = ENOMEM;
+
+ if (residual == NULL || residual[0] == '\0')
+ return 0;
+
+ /* Split string into attr=value substrings */
+ s = strdup(residual);
+ if (s == NULL)
+ return retval;
+
+ for ((cp = strtok(s, ":")); cp; (cp = strtok(NULL, ":"))) {
+ vp = strchr(cp, '=');
+
+ /* If there is no "=", this is a pkcs11 module name */
+ if (vp == NULL) {
+ if (idopts->p11_module_name != NULL)
+ free(idopts->p11_module_name);
+ idopts->p11_module_name = strdup(cp);
+ if (idopts->p11_module_name == NULL)
+ goto cleanup;
+ continue;
+ }
+ *vp++ = '\0';
+ if (!strcmp(cp, "module_name")) {
+ if (idopts->p11_module_name != NULL)
+ free(idopts->p11_module_name);
+ idopts->p11_module_name = strdup(vp);
+ if (idopts->p11_module_name == NULL)
+ goto cleanup;
+ } else if (!strcmp(cp, "slotid")) {
+ long slotid = strtol(vp, NULL, 10);
+ if ((slotid == LONG_MIN || slotid == LONG_MAX) && errno != 0) {
+ retval = EINVAL;
+ goto cleanup;
+ }
+ if ((long) (int) slotid != slotid) {
+ retval = EINVAL;
+ goto cleanup;
+ }
+ idopts->slotid = slotid;
+ } else if (!strcmp(cp, "token")) {
+ if (idopts->token_label != NULL)
+ free(idopts->token_label);
+ idopts->token_label = strdup(vp);
+ if (idopts->token_label == NULL)
+ goto cleanup;
+ } else if (!strcmp(cp, "certid")) {
+ if (idopts->cert_id_string != NULL)
+ free(idopts->cert_id_string);
+ idopts->cert_id_string = strdup(vp);
+ if (idopts->cert_id_string == NULL)
+ goto cleanup;
+ } else if (!strcmp(cp, "certlabel")) {
+ if (idopts->cert_label != NULL)
+ free(idopts->cert_label);
+ idopts->cert_label = strdup(vp);
+ if (idopts->cert_label == NULL)
+ goto cleanup;
+ }
+ }
+ retval = 0;
+cleanup:
+ free(s);
+ return retval;
+}
+#endif
+
+static krb5_error_code
+parse_fs_options(krb5_context context,
+ pkinit_identity_opts *idopts,
+ const char *residual)
+{
+ char *certname, *keyname;
+ krb5_error_code retval = ENOMEM;
+
+ if (residual == NULL || residual[0] == '\0')
+ return 0;
+
+ certname = strdup(residual);
+ if (certname == NULL)
+ goto cleanup;
+
+ certname = strtok(certname, ",");
+ keyname = strtok(NULL, ",");
+
+ idopts->cert_filename = strdup(certname);
+ if (idopts->cert_filename == NULL)
+ goto cleanup;
+
+ idopts->key_filename = strdup(keyname ? keyname : certname);
+ if (idopts->key_filename == NULL)
+ goto cleanup;
+
+ retval = 0;
+cleanup:
+ if (certname != NULL)
+ free(certname);
+ return retval;
+}
+
+static krb5_error_code
+parse_pkcs12_options(krb5_context context,
+ pkinit_identity_opts *idopts,
+ const char *residual)
+{
+ krb5_error_code retval = ENOMEM;
+
+ if (residual == NULL || residual[0] == '\0')
+ return 0;
+
+ idopts->cert_filename = strdup(residual);
+ if (idopts->cert_filename == NULL)
+ goto cleanup;
+
+ idopts->key_filename = strdup(residual);
+ if (idopts->key_filename == NULL)
+ goto cleanup;
+
+ pkiDebug("%s: cert_filename '%s' key_filename '%s'\n",
+ __FUNCTION__, idopts->cert_filename,
+ idopts->key_filename);
+ retval = 0;
+cleanup:
+ return retval;
+}
+
+static krb5_error_code
+process_option_identity(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,
+ const char *value)
+{
+ const char *residual;
+ int idtype;
+ krb5_error_code retval = 0;
+
+ pkiDebug("%s: processing value '%s'\n",
+ __FUNCTION__, value ? value : "NULL");
+ if (value == NULL)
+ return EINVAL;
+
+ residual = strchr(value, ':');
+ if (residual != NULL) {
+ unsigned int typelen;
+ residual++; /* skip past colon */
+ typelen = residual - value;
+ if (strncmp(value, "FILE:", typelen) == 0) {
+ idtype = IDTYPE_FILE;
+#ifndef WITHOUT_PKCS11
+ } else if (strncmp(value, "PKCS11:", typelen) == 0) {
+ idtype = IDTYPE_PKCS11;
+#endif
+ } else if (strncmp(value, "PKCS12:", typelen) == 0) {
+ idtype = IDTYPE_PKCS12;
+ } else if (strncmp(value, "DIR:", typelen) == 0) {
+ idtype = IDTYPE_DIR;
+ } else if (strncmp(value, "ENV:", typelen) == 0) {
+ idtype = IDTYPE_ENVVAR;
+ } else {
+ pkiDebug("%s: Unsupported type while processing '%s'\n",
+ __FUNCTION__, value);
+ krb5_set_error_message(context, KRB5_PREAUTH_FAILED,
+ "Unsupported type while processing '%s'\n",
+ value);
+ return KRB5_PREAUTH_FAILED;
+ }
+ } else {
+ idtype = IDTYPE_FILE;
+ residual = value;
+ }
+
+ idopts->idtype = idtype;
+ pkiDebug("%s: idtype is %s\n", __FUNCTION__, idtype2string(idopts->idtype));
+ switch (idtype) {
+ case IDTYPE_ENVVAR:
+ return process_option_identity(context, plg_cryptoctx, req_cryptoctx,
+ idopts, id_cryptoctx, getenv(residual));
+ break;
+ case IDTYPE_FILE:
+ retval = parse_fs_options(context, idopts, residual);
+ break;
+ case IDTYPE_PKCS12:
+ retval = parse_pkcs12_options(context, idopts, residual);
+ break;
+#ifndef WITHOUT_PKCS11
+ case IDTYPE_PKCS11:
+ retval = parse_pkcs11_options(context, idopts, residual);
+ break;
+#endif
+ case IDTYPE_DIR:
+ idopts->cert_filename = strdup(residual);
+ if (idopts->cert_filename == NULL)
+ retval = ENOMEM;
+ break;
+ default:
+ krb5_set_error_message(context, KRB5_PREAUTH_FAILED,
+ "Internal error parsing X509_user_identity\n");
+ retval = EINVAL;
+ break;
+ }
+ return retval;
+}
+
+static krb5_error_code
+process_option_ca_crl(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,
+ const char *value,
+ int catype)
+{
+ char *residual;
+ unsigned int typelen;
+ int idtype;
+
+ pkiDebug("%s: processing catype %s, value '%s'\n",
+ __FUNCTION__, catype2string(catype), value);
+ residual = strchr(value, ':');
+ if (residual == NULL) {
+ pkiDebug("No type given for '%s'\n", value);
+ return EINVAL;
+ }
+ residual++; /* skip past colon */
+ typelen = residual - value;
+ if (strncmp(value, "FILE:", typelen) == 0) {
+ idtype = IDTYPE_FILE;
+ } else if (strncmp(value, "DIR:", typelen) == 0) {
+ idtype = IDTYPE_DIR;
+ } else {
+ return ENOTSUP;
+ }
+ return crypto_load_cas_and_crls(context,
+ plg_cryptoctx,
+ req_cryptoctx,
+ idopts, id_cryptoctx,
+ idtype, catype, residual);
+}
+
+static krb5_error_code
+pkinit_identity_process_option(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 attr,
+ const char *value)
+{
+ krb5_error_code retval = 0;
+
+ switch (attr) {
+ case PKINIT_ID_OPT_USER_IDENTITY:
+ retval = process_option_identity(context, plg_cryptoctx,
+ req_cryptoctx, idopts,
+ id_cryptoctx, value);
+ break;
+ case PKINIT_ID_OPT_ANCHOR_CAS:
+ retval = process_option_ca_crl(context, plg_cryptoctx,
+ req_cryptoctx, idopts,
+ id_cryptoctx, value,
+ CATYPE_ANCHORS);
+ break;
+ case PKINIT_ID_OPT_INTERMEDIATE_CAS:
+ retval = process_option_ca_crl(context, plg_cryptoctx,
+ req_cryptoctx, idopts,
+ id_cryptoctx,
+ value, CATYPE_INTERMEDIATES);
+ break;
+ case PKINIT_ID_OPT_CRLS:
+ retval = process_option_ca_crl(context, plg_cryptoctx,
+ req_cryptoctx, idopts,
+ id_cryptoctx,
+ value, CATYPE_CRLS);
+ break;
+ case PKINIT_ID_OPT_OCSP:
+ retval = ENOTSUP;
+ break;
+ default:
+ retval = EINVAL;
+ break;
+ }
+ return retval;
+}
+
+krb5_error_code
+pkinit_identity_initialize(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 do_matching,
+ krb5_principal princ)
+{
+ krb5_error_code retval = EINVAL;
+ int i;
+
+ pkiDebug("%s: %p %p %p\n", __FUNCTION__, context, idopts, id_cryptoctx);
+ if (idopts == NULL || id_cryptoctx == NULL)
+ goto errout;
+
+ /*
+ * If identity was specified, use that. (For the kdc, this
+ * is specified as pkinit_identity in the kdc.conf. For users,
+ * this is specified on the command line via X509_user_identity.)
+ * If a user did not specify identity on the command line,
+ * then we will try alternatives which may have been specified
+ * in the config file.
+ */
+ if (idopts->identity != NULL) {
+ retval = pkinit_identity_process_option(context, plg_cryptoctx,
+ req_cryptoctx, idopts,
+ id_cryptoctx,
+ PKINIT_ID_OPT_USER_IDENTITY,
+ idopts->identity);
+ } else if (idopts->identity_alt != NULL) {
+ for (i = 0; retval != 0 && idopts->identity_alt[i] != NULL; i++)
+ retval = pkinit_identity_process_option(context, plg_cryptoctx,
+ req_cryptoctx, idopts,
+ id_cryptoctx,
+ PKINIT_ID_OPT_USER_IDENTITY,
+ idopts->identity_alt[i]);
+ } else {
+ pkiDebug("%s: no user identity options specified\n", __FUNCTION__);
+ goto errout;
+ }
+ if (retval)
+ goto errout;
+
+ retval = crypto_load_certs(context, plg_cryptoctx, req_cryptoctx,
+ idopts, id_cryptoctx, princ);
+ if (retval)
+ goto errout;
+
+ if (do_matching) {
+ 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;
+ }
+ } else {
+ /* Tell crypto code to use the "default" */
+ 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;
+ }
+ }
+
+ retval = crypto_free_cert_info(context, plg_cryptoctx, req_cryptoctx,
+ id_cryptoctx);
+ if (retval)
+ goto errout;
+
+ for (i = 0; idopts->anchors != NULL && idopts->anchors[i] != NULL; i++) {
+ retval = pkinit_identity_process_option(context, plg_cryptoctx,
+ req_cryptoctx, idopts,
+ id_cryptoctx,
+ PKINIT_ID_OPT_ANCHOR_CAS,
+ idopts->anchors[i]);
+ if (retval)
+ goto errout;
+ }
+ for (i = 0; idopts->intermediates != NULL
+ && idopts->intermediates[i] != NULL; i++) {
+ retval = pkinit_identity_process_option(context, plg_cryptoctx,
+ req_cryptoctx, idopts,
+ id_cryptoctx,
+ PKINIT_ID_OPT_INTERMEDIATE_CAS,
+ idopts->intermediates[i]);
+ if (retval)
+ goto errout;
+ }
+ for (i = 0; idopts->crls != NULL && idopts->crls[i] != NULL; i++) {
+ retval = pkinit_identity_process_option(context, plg_cryptoctx,
+ req_cryptoctx, idopts,
+ id_cryptoctx,
+ PKINIT_ID_OPT_CRLS,
+ idopts->crls[i]);
+ if (retval)
+ goto errout;
+ }
+ if (idopts->ocsp != NULL) {
+ retval = pkinit_identity_process_option(context, plg_cryptoctx,
+ req_cryptoctx, idopts,
+ id_cryptoctx,
+ PKINIT_ID_OPT_OCSP,
+ idopts->ocsp);
+ if (retval)
+ goto errout;
+ }
+
+errout:
+ return retval;
+}
+
diff --git a/src/plugins/preauth/pkinit/pkinit_lib.c b/src/plugins/preauth/pkinit/pkinit_lib.c
new file mode 100644
index 0000000..f49ef5e
--- /dev/null
+++ b/src/plugins/preauth/pkinit/pkinit_lib.c
@@ -0,0 +1,477 @@
+/*
+ * COPYRIGHT (C) 2006,2007
+ * THE REGENTS OF THE UNIVERSITY OF MICHIGAN
+ * ALL RIGHTS RESERVED
+ *
+ * Permission is granted to use, copy, create derivative works
+ * and redistribute this software and such derivative works
+ * for any purpose, so long as the name of The University of
+ * Michigan is not used in any advertising or publicity
+ * pertaining to the use of distribution of this software
+ * without specific, written prior authorization. If the
+ * above copyright notice or any other identification of the
+ * University of Michigan is included in any copy of any
+ * portion of this software, then the disclaimer below must
+ * also be included.
+ *
+ * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
+ * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
+ * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
+ * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
+ * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
+ * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGES.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "pkinit.h"
+
+#define FAKECERT
+
+const krb5_octet_data
+ dh_oid = { 0, 7, (unsigned char *)"\x2A\x86\x48\xce\x3e\x02\x01" };
+
+
+krb5_error_code
+pkinit_init_req_opts(pkinit_req_opts **reqopts)
+{
+ krb5_error_code retval = ENOMEM;
+ pkinit_req_opts *opts = NULL;
+
+ *reqopts = NULL;
+ opts = (pkinit_req_opts *) calloc(1, sizeof(pkinit_req_opts));
+ if (opts == NULL)
+ return retval;
+
+ opts->require_eku = 1;
+ opts->accept_secondary_eku = 0;
+ opts->allow_upn = 0;
+ opts->dh_or_rsa = DH_PROTOCOL;
+ opts->require_crl_checking = 0;
+ opts->dh_size = PKINIT_DEFAULT_DH_MIN_BITS;
+ opts->win2k_target = 0;
+ opts->win2k_require_cksum = 0;
+
+ *reqopts = opts;
+
+ return 0;
+}
+
+void
+pkinit_fini_req_opts(pkinit_req_opts *opts)
+{
+ if (opts != NULL)
+ free(opts);
+ return;
+}
+
+krb5_error_code
+pkinit_init_plg_opts(pkinit_plg_opts **plgopts)
+{
+ krb5_error_code retval = ENOMEM;
+ pkinit_plg_opts *opts = NULL;
+
+ *plgopts = NULL;
+ opts = (pkinit_plg_opts *) calloc(1, sizeof(pkinit_plg_opts));
+ if (opts == NULL)
+ return retval;
+
+ opts->require_eku = 1;
+ opts->accept_secondary_eku = 0;
+ opts->dh_or_rsa = DH_PROTOCOL;
+ opts->allow_upn = 0;
+ opts->require_crl_checking = 0;
+
+ opts->dh_min_bits = PKINIT_DEFAULT_DH_MIN_BITS;
+
+ *plgopts = opts;
+
+ return 0;
+}
+
+void
+pkinit_fini_plg_opts(pkinit_plg_opts *opts)
+{
+ if (opts != NULL)
+ free(opts);
+ return;
+}
+
+void
+free_krb5_pa_pk_as_req(krb5_pa_pk_as_req **in)
+{
+ if (*in == NULL) return;
+ if ((*in)->signedAuthPack.data != NULL)
+ free((*in)->signedAuthPack.data);
+ if ((*in)->trustedCertifiers != NULL)
+ free_krb5_external_principal_identifier(&(*in)->trustedCertifiers);
+ if ((*in)->kdcPkId.data != NULL)
+ free((*in)->kdcPkId.data);
+ free(*in);
+}
+
+void
+free_krb5_pa_pk_as_req_draft9(krb5_pa_pk_as_req_draft9 **in)
+{
+ if (*in == NULL) return;
+ if ((*in)->signedAuthPack.data != NULL)
+ free((*in)->signedAuthPack.data);
+ if ((*in)->kdcCert.data != NULL)
+ free((*in)->kdcCert.data);
+ if ((*in)->encryptionCert.data != NULL)
+ free((*in)->encryptionCert.data);
+ if ((*in)->trustedCertifiers != NULL)
+ free_krb5_trusted_ca(&(*in)->trustedCertifiers);
+ free(*in);
+}
+
+void
+free_krb5_reply_key_pack(krb5_reply_key_pack **in)
+{
+ if (*in == NULL) return;
+ if ((*in)->replyKey.contents != NULL)
+ free((*in)->replyKey.contents);
+ if ((*in)->asChecksum.contents != NULL)
+ free((*in)->asChecksum.contents);
+ free(*in);
+}
+
+void
+free_krb5_reply_key_pack_draft9(krb5_reply_key_pack_draft9 **in)
+{
+ if (*in == NULL) return;
+ if ((*in)->replyKey.contents != NULL)
+ free((*in)->replyKey.contents);
+ free(*in);
+}
+
+void
+free_krb5_auth_pack(krb5_auth_pack **in)
+{
+ if ((*in) == NULL) return;
+ if ((*in)->clientPublicValue != NULL) {
+ if ((*in)->clientPublicValue->algorithm.algorithm.data != NULL)
+ free((*in)->clientPublicValue->algorithm.algorithm.data);
+ if ((*in)->clientPublicValue->algorithm.parameters.data != NULL)
+ free((*in)->clientPublicValue->algorithm.parameters.data);
+ if ((*in)->clientPublicValue->subjectPublicKey.data != NULL)
+ free((*in)->clientPublicValue->subjectPublicKey.data);
+ free((*in)->clientPublicValue);
+ }
+ if ((*in)->pkAuthenticator.paChecksum.contents != NULL)
+ free((*in)->pkAuthenticator.paChecksum.contents);
+ if ((*in)->supportedCMSTypes != NULL)
+ free_krb5_algorithm_identifiers(&((*in)->supportedCMSTypes));
+ free(*in);
+}
+
+void
+free_krb5_auth_pack_draft9(krb5_context context,
+ krb5_auth_pack_draft9 **in)
+{
+ if ((*in) == NULL) return;
+ krb5_free_principal(context, (*in)->pkAuthenticator.kdcName);
+ free(*in);
+}
+
+void
+free_krb5_pa_pk_as_rep(krb5_pa_pk_as_rep **in)
+{
+ if (*in == NULL) return;
+ switch ((*in)->choice) {
+ case choice_pa_pk_as_rep_dhInfo:
+ if ((*in)->u.dh_Info.dhSignedData.data != NULL)
+ free((*in)->u.dh_Info.dhSignedData.data);
+ break;
+ case choice_pa_pk_as_rep_encKeyPack:
+ if ((*in)->u.encKeyPack.data != NULL)
+ free((*in)->u.encKeyPack.data);
+ break;
+ default:
+ break;
+ }
+ free(*in);
+}
+
+void
+free_krb5_pa_pk_as_rep_draft9(krb5_pa_pk_as_rep_draft9 **in)
+{
+ if (*in == NULL) return;
+ if ((*in)->u.encKeyPack.data != NULL)
+ free((*in)->u.encKeyPack.data);
+ free(*in);
+}
+
+void
+free_krb5_external_principal_identifier(krb5_external_principal_identifier ***in)
+{
+ int i = 0;
+ if (*in == NULL) return;
+ while ((*in)[i] != NULL) {
+ if ((*in)[i]->subjectName.data != NULL)
+ free((*in)[i]->subjectName.data);
+ if ((*in)[i]->issuerAndSerialNumber.data != NULL)
+ free((*in)[i]->issuerAndSerialNumber.data);
+ if ((*in)[i]->subjectKeyIdentifier.data != NULL)
+ free((*in)[i]->subjectKeyIdentifier.data);
+ free((*in)[i]);
+ i++;
+ }
+ free(*in);
+}
+
+void
+free_krb5_trusted_ca(krb5_trusted_ca ***in)
+{
+ int i = 0;
+ if (*in == NULL) return;
+ while ((*in)[i] != NULL) {
+ switch((*in)[i]->choice) {
+ case choice_trusted_cas_principalName:
+ break;
+ case choice_trusted_cas_caName:
+ if ((*in)[i]->u.caName.data != NULL)
+ free((*in)[i]->u.caName.data);
+ break;
+ case choice_trusted_cas_issuerAndSerial:
+ if ((*in)[i]->u.issuerAndSerial.data != NULL)
+ free((*in)[i]->u.issuerAndSerial.data);
+ break;
+ case choice_trusted_cas_UNKNOWN:
+ break;
+ }
+ free((*in)[i]);
+ i++;
+ }
+ free(*in);
+}
+
+void
+free_krb5_typed_data(krb5_typed_data ***in)
+{
+ int i = 0;
+ if (*in == NULL) return;
+ while ((*in)[i] != NULL) {
+ if ((*in)[i]->data != NULL)
+ free((*in)[i]->data);
+ free((*in)[i]);
+ i++;
+ }
+ free(*in);
+}
+
+void
+free_krb5_algorithm_identifier(krb5_algorithm_identifier *in)
+{
+ if (in == NULL)
+ return;
+ if (in->algorithm.data != NULL)
+ free(in->algorithm.data);
+ if (in->parameters.data != NULL)
+ free(in->parameters.data);
+ free(in);
+}
+
+void
+free_krb5_algorithm_identifiers(krb5_algorithm_identifier ***in)
+{
+ int i;
+ if (in == NULL || *in == NULL)
+ return;
+ for (i = 0; (*in)[i] != NULL; i++) {
+ free_krb5_algorithm_identifier((*in)[i]);
+ }
+ free(*in);
+}
+
+void
+free_krb5_subject_pk_info(krb5_subject_pk_info **in)
+{
+ if ((*in) == NULL) return;
+ if ((*in)->algorithm.parameters.data != NULL)
+ free((*in)->algorithm.parameters.data);
+ if ((*in)->subjectPublicKey.data != NULL)
+ free((*in)->subjectPublicKey.data);
+ free(*in);
+}
+
+void
+free_krb5_kdc_dh_key_info(krb5_kdc_dh_key_info **in)
+{
+ if (*in == NULL) return;
+ if ((*in)->subjectPublicKey.data != NULL)
+ free((*in)->subjectPublicKey.data);
+ free(*in);
+}
+
+void
+init_krb5_pa_pk_as_req(krb5_pa_pk_as_req **in)
+{
+ (*in) = malloc(sizeof(krb5_pa_pk_as_req));
+ if ((*in) == NULL) return;
+ (*in)->signedAuthPack.data = NULL;
+ (*in)->signedAuthPack.length = 0;
+ (*in)->trustedCertifiers = NULL;
+ (*in)->kdcPkId.data = NULL;
+ (*in)->kdcPkId.length = 0;
+}
+
+void
+init_krb5_pa_pk_as_req_draft9(krb5_pa_pk_as_req_draft9 **in)
+{
+ (*in) = malloc(sizeof(krb5_pa_pk_as_req_draft9));
+ if ((*in) == NULL) return;
+ (*in)->signedAuthPack.data = NULL;
+ (*in)->signedAuthPack.length = 0;
+ (*in)->trustedCertifiers = NULL;
+ (*in)->kdcCert.data = NULL;
+ (*in)->kdcCert.length = 0;
+ (*in)->encryptionCert.data = NULL;
+ (*in)->encryptionCert.length = 0;
+}
+
+void
+init_krb5_reply_key_pack(krb5_reply_key_pack **in)
+{
+ (*in) = malloc(sizeof(krb5_reply_key_pack));
+ if ((*in) == NULL) return;
+ (*in)->replyKey.contents = NULL;
+ (*in)->replyKey.length = 0;
+ (*in)->asChecksum.contents = NULL;
+ (*in)->asChecksum.length = 0;
+}
+
+void
+init_krb5_reply_key_pack_draft9(krb5_reply_key_pack_draft9 **in)
+{
+ (*in) = malloc(sizeof(krb5_reply_key_pack_draft9));
+ if ((*in) == NULL) return;
+ (*in)->replyKey.contents = NULL;
+ (*in)->replyKey.length = 0;
+}
+
+void
+init_krb5_auth_pack(krb5_auth_pack **in)
+{
+ (*in) = malloc(sizeof(krb5_auth_pack));
+ if ((*in) == NULL) return;
+ (*in)->clientPublicValue = NULL;
+ (*in)->supportedCMSTypes = NULL;
+ (*in)->clientDHNonce.length = 0;
+ (*in)->clientDHNonce.data = NULL;
+ (*in)->pkAuthenticator.paChecksum.contents = NULL;
+}
+
+void
+init_krb5_auth_pack_draft9(krb5_auth_pack_draft9 **in)
+{
+ (*in) = malloc(sizeof(krb5_auth_pack_draft9));
+ if ((*in) == NULL) return;
+ (*in)->clientPublicValue = NULL;
+}
+
+void
+init_krb5_pa_pk_as_rep(krb5_pa_pk_as_rep **in)
+{
+ (*in) = malloc(sizeof(krb5_pa_pk_as_rep));
+ if ((*in) == NULL) return;
+ (*in)->u.dh_Info.serverDHNonce.length = 0;
+ (*in)->u.dh_Info.serverDHNonce.data = NULL;
+ (*in)->u.dh_Info.dhSignedData.length = 0;
+ (*in)->u.dh_Info.dhSignedData.data = NULL;
+ (*in)->u.encKeyPack.length = 0;
+ (*in)->u.encKeyPack.data = NULL;
+}
+
+void
+init_krb5_pa_pk_as_rep_draft9(krb5_pa_pk_as_rep_draft9 **in)
+{
+ (*in) = malloc(sizeof(krb5_pa_pk_as_rep_draft9));
+ if ((*in) == NULL) return;
+ (*in)->u.dhSignedData.length = 0;
+ (*in)->u.dhSignedData.data = NULL;
+ (*in)->u.encKeyPack.length = 0;
+ (*in)->u.encKeyPack.data = NULL;
+}
+
+void
+init_krb5_typed_data(krb5_typed_data **in)
+{
+ (*in) = malloc(sizeof(krb5_typed_data));
+ if ((*in) == NULL) return;
+ (*in)->type = 0;
+ (*in)->length = 0;
+ (*in)->data = NULL;
+}
+
+void
+init_krb5_subject_pk_info(krb5_subject_pk_info **in)
+{
+ (*in) = malloc(sizeof(krb5_subject_pk_info));
+ if ((*in) == NULL) return;
+ (*in)->algorithm.parameters.data = NULL;
+ (*in)->algorithm.parameters.length = 0;
+ (*in)->subjectPublicKey.data = NULL;
+ (*in)->subjectPublicKey.length = 0;
+}
+
+krb5_error_code
+pkinit_copy_krb5_octet_data(krb5_octet_data *dst, const krb5_octet_data *src)
+{
+ if (dst == NULL || src == NULL)
+ return EINVAL;
+ if (src->data == NULL) {
+ dst->data = NULL;
+ dst->length = 0;
+ return 0;
+ }
+ dst->data = malloc(src->length);
+ if (dst->data == NULL)
+ return ENOMEM;
+ memcpy(dst->data, src->data, src->length);
+ dst->length = src->length;
+ return 0;
+}
+
+/* debugging functions */
+void
+print_buffer(unsigned char *buf, unsigned int len)
+{
+ int i = 0;
+ if (len <= 0)
+ return;
+
+ for (i = 0; i < len; i++)
+ pkiDebug("%02x ", buf[i]);
+ pkiDebug("\n");
+}
+
+void
+print_buffer_bin(unsigned char *buf, unsigned int len, char *filename)
+{
+ FILE *f = NULL;
+ int i = 0;
+
+ if (len <= 0 || filename == NULL)
+ return;
+
+ if ((f = fopen(filename, "w")) == NULL)
+ return;
+
+ for (i = 0; i < len; i++)
+ fputc(buf[i], f);
+
+ fclose(f);
+}
diff --git a/src/plugins/preauth/pkinit/pkinit_matching.c b/src/plugins/preauth/pkinit/pkinit_matching.c
new file mode 100644
index 0000000..b790c38
--- /dev/null
+++ b/src/plugins/preauth/pkinit/pkinit_matching.c
@@ -0,0 +1,830 @@
+/*
+ * COPYRIGHT (C) 2007
+ * THE REGENTS OF THE UNIVERSITY OF MICHIGAN
+ * ALL RIGHTS RESERVED
+ *
+ * Permission is granted to use, copy, create derivative works
+ * and redistribute this software and such derivative works
+ * for any purpose, so long as the name of The University of
+ * Michigan is not used in any advertising or publicity
+ * pertaining to the use of distribution of this software
+ * without specific, written prior authorization. If the
+ * above copyright notice or any other identification of the
+ * University of Michigan is included in any copy of any
+ * portion of this software, then the disclaimer below must
+ * also be included.
+ *
+ * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
+ * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
+ * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
+ * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
+ * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
+ * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGES.
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <regex.h>
+#include <krb5.h>
+#include "pkinit.h"
+
+typedef struct _pkinit_cert_info pkinit_cert_info;
+
+typedef enum {
+ kw_undefined = 0,
+ kw_subject = 1,
+ kw_issuer = 2,
+ kw_san = 3,
+ kw_eku = 4,
+ kw_ku = 5
+} keyword_type;
+
+static char *
+keyword2string(unsigned int kw)
+{
+ switch(kw) {
+ case kw_undefined: return "NONE"; break;
+ case kw_subject: return "SUBJECT"; break;
+ case kw_issuer: return "ISSUER"; break;
+ case kw_san: return "SAN"; break;
+ case kw_eku: return "EKU"; break;
+ case kw_ku: return "KU"; break;
+ default: return "INVALID"; break;
+ }
+}
+typedef enum {
+ relation_none = 0,
+ relation_and = 1,
+ relation_or = 2
+} relation_type;
+
+static char *
+relation2string(unsigned int rel)
+{
+ switch(rel) {
+ case relation_none: return "NONE"; break;
+ case relation_and: return "AND"; break;
+ case relation_or: return "OR"; break;
+ default: return "INVALID"; break;
+ }
+}
+
+typedef enum {
+ kwvaltype_undefined = 0,
+ kwvaltype_regexp = 1,
+ kwvaltype_list = 2
+} kw_value_type;
+
+static char *
+kwval2string(unsigned int kwval)
+{
+ switch(kwval) {
+ case kwvaltype_undefined: return "NONE"; break;
+ case kwvaltype_regexp: return "REGEXP"; break;
+ case kwvaltype_list: return "LIST"; break;
+ default: return "INVALID"; break;
+ }
+}
+
+struct keyword_desc {
+ const char *value;
+ size_t length;
+ keyword_type kwtype;
+ kw_value_type kwvaltype;
+} matching_keywords[] = {
+ { "<KU>", 4, kw_ku, kwvaltype_list },
+ { "<EKU>", 5, kw_eku, kwvaltype_list },
+ { "<SAN>", 5, kw_san, kwvaltype_regexp },
+ { "<ISSUER>", 8, kw_issuer, kwvaltype_regexp },
+ { "<SUBJECT>", 9, kw_subject, kwvaltype_regexp },
+ { NULL, 0, kw_undefined, kwvaltype_undefined},
+};
+
+struct ku_desc {
+ const char *value;
+ size_t length;
+ unsigned int bitval;
+};
+
+struct ku_desc ku_keywords[] = {
+ { "digitalSignature", 16, PKINIT_KU_DIGITALSIGNATURE },
+ { "keyEncipherment", 15, PKINIT_KU_KEYENCIPHERMENT },
+ { NULL, 0, 0 },
+};
+
+struct ku_desc eku_keywords[] = {
+ { "pkinit", 6, PKINIT_EKU_PKINIT },
+ { "msScLogin", 9, PKINIT_EKU_MSSCLOGIN },
+ { "clientAuth", 10, PKINIT_EKU_CLIENTAUTH },
+ { "emailProtection", 15, PKINIT_EKU_EMAILPROTECTION },
+ { NULL, 0, 0 },
+};
+
+/* Rule component */
+typedef struct _rule_component {
+ struct _rule_component *next;
+ keyword_type kw_type;
+ kw_value_type kwval_type;
+ regex_t regexp; /* Compiled regular expression */
+ char *regsrc; /* The regular expression source (for debugging) */
+ unsigned int ku_bits;
+ unsigned int eku_bits;
+} rule_component;
+
+/* Set rule components */
+typedef struct _rule_set {
+ relation_type relation;
+ int num_crs;
+ rule_component *crs;
+} rule_set;
+
+static krb5_error_code
+free_rule_component(krb5_context context,
+ rule_component *rc)
+{
+ if (rc == NULL)
+ return 0;
+
+ if (rc->kwval_type == kwvaltype_regexp) {
+ if (rc->regsrc)
+ free(rc->regsrc);
+ regfree(&rc->regexp);
+ }
+ free(rc);
+ return 0;
+}
+
+static krb5_error_code
+free_rule_set(krb5_context context,
+ rule_set *rs)
+{
+ rule_component *rc, *trc;
+
+ if (rs == NULL)
+ return 0;
+ for (rc = rs->crs; rc != NULL;) {
+ trc = rc->next;
+ free_rule_component(context, rc);
+ rc = trc;
+ }
+ free(rs);
+ return 0;
+}
+
+static krb5_error_code
+parse_list_value(krb5_context context,
+ keyword_type type,
+ char *value,
+ rule_component *rc)
+{
+ krb5_error_code retval;
+ char *comma;
+ struct ku_desc *ku = NULL;
+ int found;
+ size_t len;
+ unsigned int *bitptr;
+
+
+ if (value == NULL || value[0] == '\0') {
+ pkiDebug("%s: Missing or empty value for list keyword type %d\n",
+ __FUNCTION__, type);
+ retval = EINVAL;
+ goto out;
+ }
+
+ if (type == kw_eku) {
+ bitptr = &rc->eku_bits;
+ } else if (type == kw_ku) {
+ bitptr = &rc->ku_bits;
+ } else {
+ pkiDebug("%s: Unknown list keyword type %d\n", __FUNCTION__, type);
+ retval = EINVAL;
+ goto out;
+ }
+
+ do {
+ found = 0;
+ comma = strchr(value, ',');
+ if (comma != NULL)
+ len = comma - value;
+ else
+ len = strlen(value);
+
+ if (type == kw_eku) {
+ ku = eku_keywords;
+ } else if (type == kw_ku) {
+ ku = ku_keywords;
+ }
+
+ for (; ku->value != NULL; ku++) {
+ if (strncasecmp(value, ku->value, len) == 0) {
+ *bitptr |= ku->bitval;
+ found = 1;
+ pkiDebug("%s: Found value '%s', bitfield is now 0x%x\n",
+ __FUNCTION__, ku->value, *bitptr);
+ break;
+ }
+ }
+ if (found) {
+ value += ku->length;
+ if (*value == ',')
+ value += 1;
+ } else {
+ pkiDebug("%s: Urecognized value '%s'\n", __FUNCTION__, value);
+ retval = EINVAL;
+ goto out;
+ }
+ } while (found && *value != '\0');
+
+ retval = 0;
+out:
+ pkiDebug("%s: returning %d\n", __FUNCTION__, retval);
+ return retval;
+}
+
+static krb5_error_code
+parse_rule_component(krb5_context context,
+ const char **rule,
+ int *remaining,
+ rule_component **ret_rule)
+{
+ krb5_error_code retval;
+ rule_component *rc = NULL;
+ keyword_type kw_type;
+ kw_value_type kwval_type;
+ char err_buf[128];
+ int ret;
+ struct keyword_desc *kw, *nextkw;
+ char *nk;
+ int found_next_kw = 0;
+ char *value = NULL;
+ size_t len;
+
+ for (kw = matching_keywords; kw->value != NULL; kw++) {
+ if (strncmp(*rule, kw->value, kw->length) == 0) {
+ kw_type = kw->kwtype;
+ kwval_type = kw->kwvaltype;
+ *rule += kw->length;
+ *remaining -= kw->length;
+ break;
+ }
+ }
+ if (kw->value == NULL) {
+ pkiDebug("%s: Missing or invalid keyword in rule '%s'\n",
+ __FUNCTION__, *rule);
+ retval = ENOENT;
+ goto out;
+ }
+
+ pkiDebug("%s: found keyword '%s'\n", __FUNCTION__, kw->value);
+
+ rc = calloc(1, sizeof(*rc));
+ if (rc == NULL) {
+ retval = ENOMEM;
+ goto out;
+ }
+ rc->next = NULL;
+ rc->kw_type = kw_type;
+ rc->kwval_type = kwval_type;
+
+ /*
+ * Before procesing the value for this keyword,
+ * (compiling the regular expression or processing the list)
+ * we need to find the end of it. That means parsing for the
+ * beginning of the next keyword (or the end of the rule).
+ */
+ nk = strchr(*rule, '<');
+ while (nk != NULL) {
+ /* Possibly another keyword, check it out */
+ for (nextkw = matching_keywords; nextkw->value != NULL; nextkw++) {
+ if (strncmp(nk, nextkw->value, nextkw->length) == 0) {
+ /* Found a keyword, nk points to the beginning */
+ found_next_kw = 1;
+ break; /* Need to break out of the while! */
+ }
+ }
+ if (!found_next_kw)
+ nk = strchr(nk+1, '<'); /* keep looking */
+ else
+ break;
+ }
+
+ if (nk != NULL && found_next_kw)
+ len = (nk - *rule);
+ else
+ len = (*remaining);
+
+ if (len == 0) {
+ pkiDebug("%s: Missing value for keyword '%s'\n",
+ __FUNCTION__, kw->value);
+ retval = EINVAL;
+ goto out;
+ }
+
+ value = calloc(1, len+1);
+ if (value == NULL) {
+ retval = ENOMEM;
+ goto out;
+ }
+ memcpy(value, *rule, len);
+ *remaining -= len;
+ *rule += len;
+ pkiDebug("%s: found value '%s'\n", __FUNCTION__, value);
+
+ if (kw->kwvaltype == kwvaltype_regexp) {
+ ret = regcomp(&rc->regexp, value, REG_EXTENDED);
+ if (ret) {
+ regerror(ret, &rc->regexp, err_buf, sizeof(err_buf));
+ pkiDebug("%s: Error compiling reg-exp '%s': %s\n",
+ __FUNCTION__, value, err_buf);
+ retval = ret;
+ goto out;
+ }
+ rc->regsrc = strdup(value);
+ if (rc->regsrc == NULL) {
+ retval = ENOMEM;
+ goto out;
+ }
+ } else if (kw->kwvaltype == kwvaltype_list) {
+ retval = parse_list_value(context, rc->kw_type, value, rc);
+ if (retval) {
+ pkiDebug("%s: Error %d, parsing list values for keyword %s\n",
+ __FUNCTION__, retval, kw->value);
+ goto out;
+ }
+ }
+
+ *ret_rule = rc;
+ retval = 0;
+out:
+ if (value != NULL)
+ free(value);
+ if (retval && rc != NULL)
+ free_rule_component(context, rc);
+ pkiDebug("%s: returning %d\n", __FUNCTION__, retval);
+ return retval;
+}
+
+static krb5_error_code
+parse_rule_set(krb5_context context,
+ const char *rule_in,
+ rule_set **out_rs)
+{
+ const char *rule;
+ int remaining, totlen;
+ krb5_error_code ret, retval;
+ rule_component *rc = NULL, *trc;
+ rule_set *rs;
+
+
+ if (rule_in == NULL)
+ return EINVAL;
+ rule = rule_in;
+ totlen = remaining = strlen(rule);
+
+ rs = calloc(1, sizeof(*rs));
+ if (rs == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+
+ rs->relation = relation_none;
+ if (remaining > 1) {
+ if (rule[0] == '&' && rule[1] == '&') {
+ rs->relation = relation_and;
+ rule += 2;
+ remaining -= 2;
+ } else if (rule_in[0] == '|' && rule_in[1] == '|') {
+ rs->relation = relation_or;
+ rule +=2;
+ remaining -= 2;
+ }
+ }
+ rs->num_crs = 0;
+ while (remaining > 0) {
+ if (rs->relation == relation_none && rs->num_crs > 1) {
+ pkiDebug("%s: Assuming AND relation for multiple components in rule '%s'\n",
+ __FUNCTION__, rule_in);
+ rs->relation = relation_and;
+ }
+ ret = parse_rule_component(context, &rule, &remaining, &rc);
+ if (ret) {
+ retval = ret;
+ goto cleanup;
+ }
+ pkiDebug("%s: After parse_rule_component, remaining %d, rule '%s'\n",
+ __FUNCTION__, remaining, rule);
+ rs->num_crs++;
+
+ /*
+ * Chain the new component on the end (order matters since
+ * we can short-circuit an OR or an AND relation if an
+ * earlier check passes
+ */
+ for (trc = rs->crs; trc != NULL && trc->next != NULL; trc = trc->next);
+ if (trc == NULL)
+ rs->crs = rc;
+ else {
+ trc->next = rc;
+ }
+ }
+
+ *out_rs = rs;
+
+ retval = 0;
+cleanup:
+ if (retval && rs != NULL) {
+ free_rule_set(context, rs);
+ }
+ pkiDebug("%s: returning %d\n", __FUNCTION__, retval);
+ return retval;
+}
+
+static int
+regexp_match(krb5_context context, rule_component *rc, char *value)
+{
+ int code;
+
+ pkiDebug("%s: checking %s rule '%s' with value '%s'\n",
+ __FUNCTION__, keyword2string(rc->kw_type), rc->regsrc, value);
+
+ code = regexec(&rc->regexp, value, 0, NULL, 0);
+
+ pkiDebug("%s: the result is%s a match\n", __FUNCTION__,
+ code == REG_NOMATCH ? " NOT" : "");
+
+ return (code == 0 ? 1: 0);
+}
+
+static int
+component_match(krb5_context context,
+ rule_component *rc,
+ pkinit_cert_matching_data *md)
+{
+ int match = 0;
+ int i;
+ krb5_principal p;
+ char *princ_string;
+
+ switch (rc->kwval_type) {
+ case kwvaltype_regexp:
+ switch (rc->kw_type) {
+ case kw_subject:
+ match = regexp_match(context, rc, md->subject_dn);
+ break;
+ case kw_issuer:
+ match = regexp_match(context, rc, md->issuer_dn);
+ break;
+ case kw_san:
+ if (md->sans == NULL)
+ break;
+ for (i = 0, p = md->sans[i]; p != NULL; p = md->sans[++i]) {
+ krb5_unparse_name(context, p, &princ_string);
+ match = regexp_match(context, rc, princ_string);
+ krb5_free_unparsed_name(context, princ_string);
+ if (match)
+ break;
+ }
+ break;
+ default:
+ pkiDebug("%s: keyword %s, keyword value %s mismatch\n",
+ __FUNCTION__, keyword2string(rc->kw_type),
+ kwval2string(kwvaltype_regexp));
+ break;
+ }
+ break;
+ case kwvaltype_list:
+ switch(rc->kw_type) {
+ case kw_eku:
+ pkiDebug("%s: checking %s: rule 0x%08x, cert 0x%08x\n",
+ __FUNCTION__, keyword2string(rc->kw_type),
+ rc->eku_bits, md->eku_bits);
+ if ((rc->eku_bits & md->eku_bits) == rc->eku_bits)
+ match = 1;
+ break;
+ case kw_ku:
+ pkiDebug("%s: checking %s: rule 0x%08x, cert 0x%08x\n",
+ __FUNCTION__, keyword2string(rc->kw_type),
+ rc->ku_bits, md->ku_bits);
+ if ((rc->ku_bits & md->ku_bits) == rc->ku_bits)
+ match = 1;
+ break;
+ default:
+ pkiDebug("%s: keyword %s, keyword value %s mismatch\n",
+ __FUNCTION__, keyword2string(rc->kw_type),
+ kwval2string(kwvaltype_regexp));
+ break;
+ }
+ break;
+ default:
+ pkiDebug("%s: unknown keyword value type %d\n",
+ __FUNCTION__, rc->kwval_type);
+ break;
+ }
+ pkiDebug("%s: returning match = %d\n", __FUNCTION__, match);
+ return match;
+}
+/*
+ * Returns match_found == 1 only if exactly one certificate matches
+ * the given rule
+ */
+static krb5_error_code
+check_all_certs(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ krb5_principal princ,
+ rule_set *rs, /* rule to check */
+ pkinit_cert_matching_data **matchdata,
+ int *match_found,
+ pkinit_cert_matching_data **matching_cert)
+{
+ krb5_error_code retval;
+ pkinit_cert_matching_data *md;
+ int i;
+ int comp_match = 0;
+ int total_cert_matches = 0;
+ rule_component *rc;
+ int certs_checked = 0;
+ pkinit_cert_matching_data *save_match = NULL;
+
+ if (match_found == NULL || matching_cert == NULL)
+ return EINVAL;
+
+ *matching_cert = NULL;
+ *match_found = 0;
+
+ pkiDebug("%s: matching rule relation is %s with %d components\n",
+ __FUNCTION__, relation2string(rs->relation), rs->num_crs);
+
+ /*
+ * Loop through all the certs available and count
+ * how many match the rule
+ */
+ for (i = 0, md = matchdata[i]; md != NULL; md = matchdata[++i]) {
+ pkiDebug("%s: subject: '%s'\n", __FUNCTION__, md->subject_dn);
+#if 0
+ pkiDebug("%s: issuer: '%s'\n", __FUNCTION__, md->subject_dn);
+ for (j = 0, p = md->sans[j]; p != NULL; p = md->sans[++j]) {
+ char *san_string;
+ krb5_unparse_name(context, p, &san_string);
+ pkiDebug("%s: san: '%s'\n", __FUNCTION__, san_string);
+ krb5_free_unparsed_name(context, san_string);
+ }
+#endif
+ certs_checked++;
+ for (rc = rs->crs; rc != NULL; rc = rc->next) {
+ comp_match = component_match(context, rc, md);
+ if (comp_match) {
+ pkiDebug("%s: match for keyword type %s\n",
+ __FUNCTION__, keyword2string(rc->kw_type));
+ }
+ if (comp_match && rs->relation == relation_or) {
+ pkiDebug("%s: cert matches rule (OR relation)\n",
+ __FUNCTION__);
+ total_cert_matches++;
+ save_match = md;
+ goto nextcert;
+ }
+ if (!comp_match && rs->relation == relation_and) {
+ pkiDebug("%s: cert does not match rule (AND relation)\n",
+ __FUNCTION__);
+ goto nextcert;
+ }
+ }
+ if (rc == NULL && comp_match) {
+ pkiDebug("%s: cert matches rule (AND relation)\n", __FUNCTION__);
+ total_cert_matches++;
+ save_match = md;
+ }
+nextcert:
+ continue;
+ }
+ pkiDebug("%s: After checking %d certs, we found %d matches\n",
+ __FUNCTION__, certs_checked, total_cert_matches);
+ if (total_cert_matches == 1) {
+ *match_found = 1;
+ *matching_cert = save_match;
+ }
+
+ retval = 0;
+
+ pkiDebug("%s: returning %d, match_found %d\n",
+ __FUNCTION__, retval, *match_found);
+ 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,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_crypto_context id_cryptoctx,
+ krb5_principal princ)
+{
+
+ krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ int x;
+ char **rules = NULL;
+ rule_set *rs = NULL;
+ int match_found = 0;
+ pkinit_cert_matching_data **matchdata = NULL;
+ pkinit_cert_matching_data *the_matching_cert = NULL;
+
+ /* If no matching rules, select the default cert and we're done */
+ pkinit_libdefault_strings(context, krb5_princ_realm(context, princ),
+ "pkinit_cert_match", &rules);
+ if (rules == NULL) {
+ pkiDebug("%s: no matching rules found in config file\n", __FUNCTION__);
+ retval = crypto_cert_select_default(context, plg_cryptoctx,
+ req_cryptoctx, id_cryptoctx);
+ goto cleanup;
+ }
+
+ /* parse each rule line one at a time and check all the certs against it */
+ for (x = 0; rules[x] != NULL; x++) {
+ pkiDebug("%s: Processing rule '%s'\n", __FUNCTION__, rules[x]);
+
+ /* Free rules from previous time through... */
+ if (rs != NULL) {
+ free_rule_set(context, rs);
+ rs = NULL;
+ }
+ retval = parse_rule_set(context, rules[x], &rs);
+ if (retval) {
+ if (retval == EINVAL) {
+ pkiDebug("%s: Ignoring invalid rule pkinit_cert_match = '%s'\n",
+ __FUNCTION__, rules[x]);
+ continue;
+ }
+ goto cleanup;
+ }
+
+ /*
+ * Optimize so that we do not get cert info unless we have
+ * valid rules to check. Once obtained, keep it around
+ * until we are done.
+ */
+ if (matchdata == NULL) {
+ retval = obtain_all_cert_matching_data(context, plg_cryptoctx,
+ req_cryptoctx, id_cryptoctx,
+ &matchdata);
+ if (retval || matchdata == NULL) {
+ pkiDebug("%s: Error %d obtaining certificate information\n",
+ __FUNCTION__, retval);
+ retval = ENOENT;
+ goto cleanup;
+ }
+ }
+
+ retval = check_all_certs(context, plg_cryptoctx, req_cryptoctx,
+ id_cryptoctx, princ, rs, matchdata,
+ &match_found, &the_matching_cert);
+ if (retval) {
+ pkiDebug("%s: Error %d, checking certs against rule '%s'\n",
+ __FUNCTION__, retval, rules[x]);
+ goto cleanup;
+ }
+ if (match_found) {
+ pkiDebug("%s: We have an exact match with rule '%s'\n",
+ __FUNCTION__, rules[x]);
+ break;
+ }
+ }
+
+ if (match_found && the_matching_cert != NULL) {
+ pkiDebug("%s: Selecting the matching cert!\n", __FUNCTION__);
+ retval = crypto_cert_select(context, the_matching_cert);
+ if (retval) {
+ pkiDebug("%s: crypto_cert_select error %d, %s\n",
+ __FUNCTION__, retval, error_message(retval));
+ goto cleanup;
+ }
+ } else {
+ 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);
+ return retval;
+}
diff --git a/src/plugins/preauth/pkinit/pkinit_profile.c b/src/plugins/preauth/pkinit/pkinit_profile.c
new file mode 100644
index 0000000..403068a
--- /dev/null
+++ b/src/plugins/preauth/pkinit/pkinit_profile.c
@@ -0,0 +1,376 @@
+/*
+ * COPYRIGHT (C) 2006,2007
+ * THE REGENTS OF THE UNIVERSITY OF MICHIGAN
+ * ALL RIGHTS RESERVED
+ *
+ * Permission is granted to use, copy, create derivative works
+ * and redistribute this software and such derivative works
+ * for any purpose, so long as the name of The University of
+ * Michigan is not used in any advertising or publicity
+ * pertaining to the use of distribution of this software
+ * without specific, written prior authorization. If the
+ * above copyright notice or any other identification of the
+ * University of Michigan is included in any copy of any
+ * portion of this software, then the disclaimer below must
+ * also be included.
+ *
+ * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
+ * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
+ * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
+ * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
+ * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
+ * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGES.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#include "k5-int.h"
+#include "pkinit.h"
+
+/*
+ * Routines for handling profile [config file] options
+ */
+
+/* Forward prototypes */
+static int _krb5_conf_boolean(const char *s);
+
+/*
+ * XXX
+ * The following is duplicated verbatim from src/lib/krb5/krb/get_in_tkt.c,
+ * which is duplicated from somewhere else. :-/
+ * XXX
+ */
+static const char *const conf_yes[] = {
+ "y", "yes", "true", "t", "1", "on",
+ 0,
+};
+
+static const char *const conf_no[] = {
+ "n", "no", "false", "nil", "0", "off",
+ 0,
+};
+
+static int
+_krb5_conf_boolean(const char *s)
+{
+ const char *const *p;
+
+ for(p=conf_yes; *p; p++) {
+ if (strcasecmp(*p,s) == 0)
+ return 1;
+ }
+
+ for(p=conf_no; *p; p++) {
+ if (strcasecmp(*p,s) == 0)
+ return 0;
+ }
+
+ /* Default to "no" */
+ return 0;
+}
+
+/*
+ * XXX
+ * End duplicated code from src/lib/krb5/krb/get_in_tkt.c
+ * XXX
+ */
+
+/*
+ * The following are based on krb5_libdefault_* functions in
+ * src/lib/krb5/krb/get_in_tkt.c
+ * N.B. This assumes that context->default_realm has
+ * already been established.
+ */
+krb5_error_code
+pkinit_kdcdefault_strings(krb5_context context, const char *realmname,
+ const char *option, char ***ret_value)
+{
+ profile_t profile = NULL;
+ const char *names[5];
+ char **values = NULL;
+ krb5_error_code retval;
+
+ if (context == NULL)
+ return KV5M_CONTEXT;
+
+ profile = context->profile;
+
+ if (realmname != NULL) {
+ /*
+ * Try number one:
+ *
+ * [realms]
+ * REALM = {
+ * option = <value>
+ * }
+ */
+
+ names[0] = "realms";
+ names[1] = realmname;
+ names[2] = option;
+ names[3] = 0;
+ retval = profile_get_values(profile, names, &values);
+ if (retval == 0 && values != NULL)
+ goto goodbye;
+ }
+
+ /*
+ * Try number two:
+ *
+ * [kdcdefaults]
+ * option = <value>
+ */
+
+ names[0] = "kdcdefaults";
+ names[1] = option;
+ names[2] = 0;
+ retval = profile_get_values(profile, names, &values);
+ if (retval == 0 && values != NULL)
+ goto goodbye;
+
+goodbye:
+ if (values == NULL)
+ retval = ENOENT;
+
+ *ret_value = values;
+
+ return retval;
+
+}
+
+krb5_error_code
+pkinit_kdcdefault_string(krb5_context context, const char *realmname,
+ const char *option, char **ret_value)
+{
+ krb5_error_code retval;
+ char **values = NULL;
+
+ retval = pkinit_kdcdefault_strings(context, realmname, option, &values);
+ if (retval)
+ return retval;
+
+ if (values[0] == NULL) {
+ retval = ENOENT;
+ } else {
+ *ret_value = malloc(strlen(values[0]) + 1);
+ if (*ret_value == NULL)
+ retval = ENOMEM;
+ else
+ strcpy(*ret_value, values[0]);
+ }
+
+ profile_free_list(values);
+ return retval;
+}
+
+krb5_error_code
+pkinit_kdcdefault_boolean(krb5_context context, const char *realmname,
+ const char *option, int default_value, int *ret_value)
+{
+ char *string = NULL;
+ krb5_error_code retval;
+
+ retval = pkinit_kdcdefault_string(context, realmname, option, &string);
+
+ if (retval == 0) {
+ *ret_value = _krb5_conf_boolean(string);
+ free(string);
+ } else
+ *ret_value = default_value;
+
+ return 0;
+}
+
+krb5_error_code
+pkinit_kdcdefault_integer(krb5_context context, const char *realmname,
+ const char *option, int default_value, int *ret_value)
+{
+ char *string = NULL;
+ krb5_error_code retval;
+
+ retval = pkinit_kdcdefault_string(context, realmname, option, &string);
+
+ if (retval == 0) {
+ char *endptr;
+ long l;
+ l = strtol(string, &endptr, 0);
+ if (endptr == string)
+ *ret_value = default_value;
+ else
+ *ret_value = l;
+ free(string);
+ } else
+ *ret_value = default_value;
+
+ return 0;
+}
+
+
+/*
+ * krb5_libdefault_string() is defined as static in
+ * src/lib/krb5/krb/get_in_tkt.c. Create local versions of
+ * krb5_libdefault_* functions here. We need a libdefaults_strings()
+ * function which is not currently supported there anyway. Also,
+ * add the ability to supply a default value for the boolean and
+ * integer functions.
+ */
+
+krb5_error_code
+pkinit_libdefault_strings(krb5_context context, const krb5_data *realm,
+ const char *option, char ***ret_value)
+{
+ profile_t profile;
+ const char *names[5];
+ char **values = NULL;
+ krb5_error_code retval;
+ char realmstr[1024];
+
+ if (realm != NULL && realm->length > sizeof(realmstr)-1)
+ return EINVAL;
+
+ if (realm != NULL) {
+ strncpy(realmstr, realm->data, realm->length);
+ realmstr[realm->length] = '\0';
+ }
+
+ if (!context || (context->magic != KV5M_CONTEXT))
+ return KV5M_CONTEXT;
+
+ profile = context->profile;
+
+
+ if (realm != NULL) {
+ /*
+ * Try number one:
+ *
+ * [libdefaults]
+ * REALM = {
+ * option = <value>
+ * }
+ */
+
+ names[0] = "libdefaults";
+ names[1] = realmstr;
+ names[2] = option;
+ names[3] = 0;
+ retval = profile_get_values(profile, names, &values);
+ if (retval == 0 && values != NULL && values[0] != NULL)
+ goto goodbye;
+
+ /*
+ * Try number two:
+ *
+ * [realms]
+ * REALM = {
+ * option = <value>
+ * }
+ */
+
+ names[0] = "realms";
+ names[1] = realmstr;
+ names[2] = option;
+ names[3] = 0;
+ retval = profile_get_values(profile, names, &values);
+ if (retval == 0 && values != NULL && values[0] != NULL)
+ goto goodbye;
+ }
+
+ /*
+ * Try number three:
+ *
+ * [libdefaults]
+ * option = <value>
+ */
+
+ names[0] = "libdefaults";
+ names[1] = option;
+ names[2] = 0;
+ retval = profile_get_values(profile, names, &values);
+ if (retval == 0 && values != NULL && values[0] != NULL)
+ goto goodbye;
+
+goodbye:
+ if (values == NULL)
+ return ENOENT;
+
+ *ret_value = values;
+
+ return retval;
+}
+
+krb5_error_code
+pkinit_libdefault_string(krb5_context context, const krb5_data *realm,
+ const char *option, char **ret_value)
+{
+ krb5_error_code retval;
+ char **values = NULL;
+
+ retval = pkinit_libdefault_strings(context, realm, option, &values);
+ if (retval)
+ return retval;
+
+ if (values[0] == NULL) {
+ retval = ENOENT;
+ } else {
+ *ret_value = malloc(strlen(values[0]) + 1);
+ if (*ret_value == NULL)
+ retval = ENOMEM;
+ else
+ strcpy(*ret_value, values[0]);
+ }
+
+ profile_free_list(values);
+ return retval;
+}
+
+krb5_error_code
+pkinit_libdefault_boolean(krb5_context context, const krb5_data *realm,
+ const char *option, int default_value,
+ int *ret_value)
+{
+ char *string = NULL;
+ krb5_error_code retval;
+
+ retval = pkinit_libdefault_string(context, realm, option, &string);
+
+ if (retval == 0) {
+ *ret_value = _krb5_conf_boolean(string);
+ free(string);
+ } else
+ *ret_value = default_value;
+
+ return 0;
+}
+
+krb5_error_code
+pkinit_libdefault_integer(krb5_context context, const krb5_data *realm,
+ const char *option, int default_value,
+ int *ret_value)
+{
+ char *string = NULL;
+ krb5_error_code retval;
+
+ retval = pkinit_libdefault_string(context, realm, option, &string);
+
+ if (retval == 0) {
+ char *endptr;
+ long l;
+ l = strtol(string, &endptr, 0);
+ if (endptr == string)
+ *ret_value = default_value;
+ else
+ *ret_value = l;
+ free(string);
+ }
+
+ return retval;
+}
diff --git a/src/plugins/preauth/pkinit/pkinit_srv.c b/src/plugins/preauth/pkinit/pkinit_srv.c
new file mode 100644
index 0000000..595a3d0
--- /dev/null
+++ b/src/plugins/preauth/pkinit/pkinit_srv.c
@@ -0,0 +1,1399 @@
+/*
+ * COPYRIGHT (C) 2006,2007
+ * THE REGENTS OF THE UNIVERSITY OF MICHIGAN
+ * ALL RIGHTS RESERVED
+ *
+ * Permission is granted to use, copy, create derivative works
+ * and redistribute this software and such derivative works
+ * for any purpose, so long as the name of The University of
+ * Michigan is not used in any advertising or publicity
+ * pertaining to the use of distribution of this software
+ * without specific, written prior authorization. If the
+ * above copyright notice or any other identification of the
+ * University of Michigan is included in any copy of any
+ * portion of this software, then the disclaimer below must
+ * also be included.
+ *
+ * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
+ * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
+ * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
+ * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
+ * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
+ * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGES.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#include "pkinit.h"
+
+static krb5_error_code
+pkinit_server_get_edata(krb5_context context,
+ krb5_kdc_req * request,
+ struct _krb5_db_entry_new * client,
+ struct _krb5_db_entry_new * server,
+ preauth_get_entry_data_proc server_get_entry_data,
+ void *pa_plugin_context,
+ krb5_pa_data * data);
+
+static krb5_error_code
+pkinit_server_verify_padata(krb5_context context,
+ struct _krb5_db_entry_new * client,
+ krb5_data *req_pkt,
+ krb5_kdc_req * request,
+ krb5_enc_tkt_part * enc_tkt_reply,
+ krb5_pa_data * data,
+ preauth_get_entry_data_proc server_get_entry_data,
+ void *pa_plugin_context,
+ void **pa_request_context,
+ krb5_data **e_data,
+ krb5_authdata ***authz_data);
+
+static krb5_error_code
+pkinit_server_return_padata(krb5_context context,
+ krb5_pa_data * padata,
+ struct _krb5_db_entry_new * client,
+ krb5_data *req_pkt,
+ krb5_kdc_req * request,
+ krb5_kdc_rep * reply,
+ struct _krb5_key_data * client_key,
+ krb5_keyblock * encrypting_key,
+ krb5_pa_data ** send_pa,
+ preauth_get_entry_data_proc server_get_entry_data,
+ void *pa_plugin_context,
+ void **pa_request_context);
+
+static int pkinit_server_get_flags
+ (krb5_context kcontext, krb5_preauthtype patype);
+
+static krb5_error_code pkinit_init_kdc_req_context
+ (krb5_context, void **blob);
+
+static void pkinit_fini_kdc_req_context
+ (krb5_context context, void *blob);
+
+static int pkinit_server_plugin_init_realm
+ (krb5_context context, const char *realmname,
+ pkinit_kdc_context *pplgctx);
+
+static void pkinit_server_plugin_fini_realm
+ (krb5_context context, pkinit_kdc_context plgctx);
+
+static int pkinit_server_plugin_init
+ (krb5_context context, void **blob, const char **realmnames);
+
+static void pkinit_server_plugin_fini
+ (krb5_context context, void *blob);
+
+static pkinit_kdc_context pkinit_find_realm_context
+ (krb5_context context, void *pa_plugin_context, krb5_principal princ);
+
+static krb5_error_code
+pkinit_create_edata(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_error_code err_code,
+ krb5_data **e_data)
+{
+ krb5_error_code retval = KRB5KRB_ERR_GENERIC;
+
+ pkiDebug("pkinit_create_edata: creating edata for error %d (%s)\n",
+ err_code, error_message(err_code));
+ switch(err_code) {
+ case KRB5KDC_ERR_CANT_VERIFY_CERTIFICATE:
+ retval = pkinit_create_td_trusted_certifiers(context,
+ plg_cryptoctx, req_cryptoctx, id_cryptoctx, e_data);
+ break;
+ case KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED:
+ retval = pkinit_create_td_dh_parameters(context, plg_cryptoctx,
+ req_cryptoctx, id_cryptoctx, opts, e_data);
+ break;
+ case KRB5KDC_ERR_INVALID_CERTIFICATE:
+ case KRB5KDC_ERR_REVOKED_CERTIFICATE:
+ retval = pkinit_create_td_invalid_certificate(context,
+ plg_cryptoctx, req_cryptoctx, id_cryptoctx, e_data);
+ break;
+ default:
+ pkiDebug("no edata needed for error %d (%s)\n",
+ err_code, error_message(err_code));
+ retval = 0;
+ goto cleanup;
+ }
+
+cleanup:
+
+ return retval;
+}
+
+static krb5_error_code
+pkinit_server_get_edata(krb5_context context,
+ krb5_kdc_req * request,
+ struct _krb5_db_entry_new * client,
+ struct _krb5_db_entry_new * server,
+ preauth_get_entry_data_proc server_get_entry_data,
+ void *pa_plugin_context,
+ krb5_pa_data * data)
+{
+ krb5_error_code retval = 0;
+ pkinit_kdc_context plgctx = NULL;
+
+ pkiDebug("pkinit_server_get_edata: entered!\n");
+
+ /*
+ * If we don't have a realm context for the given realm,
+ * don't tell the client that we support pkinit!
+ */
+ plgctx = pkinit_find_realm_context(context, pa_plugin_context,
+ request->server);
+ if (plgctx == NULL)
+ retval = EINVAL;
+
+ return retval;
+}
+
+static krb5_error_code
+verify_client_san(krb5_context context,
+ pkinit_kdc_context plgctx,
+ pkinit_kdc_req_context reqctx,
+ krb5_principal client,
+ int *valid_san)
+{
+ krb5_error_code retval;
+ krb5_principal *princs = NULL;
+ krb5_principal *upns = NULL;
+ int i;
+#ifdef DEBUG_SAN_INFO
+ char *client_string = NULL, *san_string;
+#endif
+
+ retval = crypto_retrieve_cert_sans(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, plgctx->idctx,
+ &princs,
+ plgctx->opts->allow_upn ? &upns : NULL,
+ NULL);
+ if (retval) {
+ pkiDebug("%s: error from retrieve_certificate_sans()\n", __FUNCTION__);
+ retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
+ goto out;
+ }
+ /* XXX Verify this is consistent with client side XXX */
+#if 0
+ retval = call_san_checking_plugins(context, plgctx, reqctx, princs,
+ upns, NULL, &plugin_decision, &ignore);
+ pkiDebug("%s: call_san_checking_plugins() returned retval %d\n",
+ __FUNCTION__);
+ if (retval) {
+ retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
+ goto cleanup;
+ }
+ pkiDebug("%s: call_san_checking_plugins() returned decision %d\n",
+ __FUNCTION__, plugin_decision);
+ if (plugin_decision != NO_DECISION) {
+ retval = plugin_decision;
+ goto out;
+ }
+#endif
+
+#ifdef DEBUG_SAN_INFO
+ krb5_unparse_name(context, client, &client_string);
+#endif
+ pkiDebug("%s: Checking pkinit sans\n", __FUNCTION__);
+ for (i = 0; princs != NULL && princs[i] != NULL; i++) {
+#ifdef DEBUG_SAN_INFO
+ krb5_unparse_name(context, princs[i], &san_string);
+ pkiDebug("%s: Comparing client '%s' to pkinit san value '%s'\n",
+ __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__);
+ *valid_san = 1;
+ retval = 0;
+ goto out;
+ }
+ }
+ pkiDebug("%s: no pkinit san match found\n", __FUNCTION__);
+ /*
+ * XXX if cert has names but none match, should we
+ * be returning KRB5KDC_ERR_CLIENT_NAME_MISMATCH here?
+ */
+
+ if (upns == NULL) {
+ pkiDebug("%s: no upn sans (or we wouldn't accept them anyway)\n",
+ __FUNCTION__);
+ retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
+ goto out;
+ }
+
+ pkiDebug("%s: Checking upn sans\n", __FUNCTION__);
+ for (i = 0; upns[i] != NULL; i++) {
+#ifdef DEBUG_SAN_INFO
+ krb5_unparse_name(context, upns[i], &san_string);
+ pkiDebug("%s: Comparing client '%s' to upn san value '%s'\n",
+ __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__);
+ *valid_san = 1;
+ retval = 0;
+ goto out;
+ }
+ }
+ pkiDebug("%s: no upn san match found\n", __FUNCTION__);
+
+ /* We found no match */
+ if (princs != NULL || upns != NULL) {
+ *valid_san = 0;
+ /* XXX ??? If there was one or more name in the cert, but
+ * none matched the client name, then return mismatch? */
+ retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
+ }
+ retval = 0;
+
+out:
+ 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);
+ }
+#ifdef DEBUG_SAN_INFO
+ if (client_string != NULL)
+ krb5_free_unparsed_name(context, client_string);
+#endif
+ pkiDebug("%s: returning retval %d, valid_san %d\n",
+ __FUNCTION__, retval, *valid_san);
+ return retval;
+}
+
+static krb5_error_code
+verify_client_eku(krb5_context context,
+ pkinit_kdc_context plgctx,
+ pkinit_kdc_req_context reqctx,
+ int *eku_accepted)
+{
+ krb5_error_code retval;
+
+ *eku_accepted = 0;
+
+ if (plgctx->opts->require_eku == 0) {
+ pkiDebug("%s: configuration requests no EKU checking\n", __FUNCTION__);
+ *eku_accepted = 1;
+ retval = 0;
+ goto out;
+ }
+
+ retval = crypto_check_cert_eku(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, plgctx->idctx,
+ 0, /* kdc cert */
+ plgctx->opts->accept_secondary_eku,
+ eku_accepted);
+ if (retval) {
+ pkiDebug("%s: Error from crypto_check_cert_eku %d (%s)\n",
+ __FUNCTION__, retval, error_message(retval));
+ goto out;
+ }
+
+out:
+ pkiDebug("%s: returning retval %d, eku_accepted %d\n",
+ __FUNCTION__, retval, *eku_accepted);
+ return retval;
+}
+
+static krb5_error_code
+pkinit_server_verify_padata(krb5_context context,
+ struct _krb5_db_entry_new * client,
+ krb5_data *req_pkt,
+ krb5_kdc_req * request,
+ krb5_enc_tkt_part * enc_tkt_reply,
+ krb5_pa_data * data,
+ preauth_get_entry_data_proc server_get_entry_data,
+ void *pa_plugin_context,
+ void **pa_request_context,
+ krb5_data **e_data,
+ krb5_authdata ***authz_data)
+{
+ krb5_error_code retval = 0;
+ krb5_octet_data authp_data = {0, 0, NULL}, krb5_authz = {0, 0, NULL};
+ krb5_data *encoded_pkinit_authz_data = NULL;
+ krb5_pa_pk_as_req *reqp = NULL;
+ krb5_pa_pk_as_req_draft9 *reqp9 = NULL;
+ krb5_auth_pack *auth_pack = NULL;
+ krb5_auth_pack_draft9 *auth_pack9 = NULL;
+ pkinit_kdc_context plgctx = NULL;
+ pkinit_kdc_req_context reqctx;
+ krb5_preauthtype pa_type;
+ krb5_checksum cksum = {0, 0, 0, NULL};
+ krb5_data *der_req = NULL;
+ int valid_eku = 0, valid_san = 0;
+ krb5_authdata **my_authz_data = NULL, *pkinit_authz_data = NULL;
+ krb5_kdc_req *tmp_as_req = NULL;
+ krb5_data k5data;
+
+ pkiDebug("pkinit_verify_padata: entered!\n");
+ if (data == NULL || data->length <= 0 || data->contents == NULL)
+ return 0;
+
+ if (pa_plugin_context == NULL || e_data == NULL)
+ return EINVAL;
+
+ plgctx = pkinit_find_realm_context(context, pa_plugin_context,
+ request->server);
+ if (plgctx == NULL)
+ return 0;
+
+#ifdef DEBUG_ASN1
+ print_buffer_bin(data->contents, data->length, "/tmp/kdc_as_req");
+#endif
+ /* create a per-request context */
+ retval = pkinit_init_kdc_req_context(context, (void **)&reqctx);
+ if (retval)
+ goto cleanup;
+ reqctx->pa_type = data->pa_type;
+
+ PADATA_TO_KRB5DATA(data, &k5data);
+
+ switch ((int)data->pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ pkiDebug("processing KRB5_PADATA_PK_AS_REQ\n");
+ pa_type = (int)data->pa_type;
+ retval = k5int_decode_krb5_pa_pk_as_req(&k5data, &reqp);
+ if (retval) {
+ pkiDebug("decode_krb5_pa_pk_as_req failed\n");
+ goto cleanup;
+ }
+#ifdef DEBUG_ASN1
+ print_buffer_bin(reqp->signedAuthPack.data,
+ reqp->signedAuthPack.length,
+ "/tmp/kdc_signed_data");
+#endif
+ retval = cms_signeddata_verify(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_CLIENT,
+ plgctx->opts->require_crl_checking,
+ reqp->signedAuthPack.data, reqp->signedAuthPack.length,
+ &authp_data.data, &authp_data.length, &krb5_authz.data,
+ &krb5_authz.length);
+ break;
+ case KRB5_PADATA_PK_AS_REP_OLD:
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ pkiDebug("processing KRB5_PADATA_PK_AS_REQ_OLD\n");
+ pa_type = KRB5_PADATA_PK_AS_REQ_OLD;
+ retval = k5int_decode_krb5_pa_pk_as_req_draft9(&k5data, &reqp9);
+ if (retval) {
+ pkiDebug("decode_krb5_pa_pk_as_req_draft9 failed\n");
+ goto cleanup;
+ }
+#ifdef DEBUG_ASN1
+ print_buffer_bin(reqp9->signedAuthPack.data,
+ reqp9->signedAuthPack.length,
+ "/tmp/kdc_signed_data_draft9");
+#endif
+
+ retval = cms_signeddata_verify(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_DRAFT9,
+ plgctx->opts->require_crl_checking,
+ reqp9->signedAuthPack.data, reqp9->signedAuthPack.length,
+ &authp_data.data, &authp_data.length, &krb5_authz.data,
+ &krb5_authz.length);
+ break;
+ default:
+ pkiDebug("unrecognized pa_type = %d\n", data->pa_type);
+ retval = EINVAL;
+ goto cleanup;
+ }
+ if (retval) {
+ pkiDebug("pkcs7_signeddata_verify failed\n");
+ goto cleanup;
+ }
+
+ 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);
+ 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;
+ }
+
+#ifdef DEBUG_ASN1
+ print_buffer_bin(authp_data.data, authp_data.length, "/tmp/kdc_auth_pack");
+#endif
+
+ OCTETDATA_TO_KRB5DATA(&authp_data, &k5data);
+ switch ((int)data->pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ retval = k5int_decode_krb5_auth_pack(&k5data, &auth_pack);
+ if (retval) {
+ pkiDebug("failed to decode krb5_auth_pack\n");
+ goto cleanup;
+ }
+
+ /* check dh parameters */
+ if (auth_pack->clientPublicValue != NULL) {
+ retval = server_check_dh(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, plgctx->idctx,
+ &auth_pack->clientPublicValue->algorithm.parameters,
+ plgctx->opts->dh_min_bits);
+
+ if (retval) {
+ pkiDebug("bad dh parameters\n");
+ goto cleanup;
+ }
+ }
+ /*
+ * The KDC may have modified the request after decoding it.
+ * We need to compute the checksum on the data that
+ * came from the client. Therefore, we use the original
+ * packet contents.
+ */
+ retval = k5int_decode_krb5_as_req(req_pkt, &tmp_as_req);
+ if (retval) {
+ pkiDebug("decode_krb5_as_req returned %d\n", (int)retval);
+ goto cleanup;
+ }
+
+ retval = k5int_encode_krb5_kdc_req_body(tmp_as_req, &der_req);
+ if (retval) {
+ pkiDebug("encode_krb5_kdc_req_body returned %d\n", (int) retval);
+ goto cleanup;
+ }
+ retval = krb5_c_make_checksum(context, CKSUMTYPE_NIST_SHA, NULL,
+ 0, der_req, &cksum);
+ if (retval) {
+ pkiDebug("unable to calculate AS REQ checksum\n");
+ goto cleanup;
+ }
+ if (cksum.length != auth_pack->pkAuthenticator.paChecksum.length ||
+ memcmp(cksum.contents,
+ auth_pack->pkAuthenticator.paChecksum.contents,
+ cksum.length)) {
+ pkiDebug("failed to match the checksum\n");
+#ifdef DEBUG_CKSUM
+ pkiDebug("calculating checksum on buf size (%d)\n",
+ req_pkt->length);
+ print_buffer(req_pkt->data, req_pkt->length);
+ pkiDebug("received checksum type=%d size=%d ",
+ auth_pack->pkAuthenticator.paChecksum.checksum_type,
+ auth_pack->pkAuthenticator.paChecksum.length);
+ print_buffer(auth_pack->pkAuthenticator.paChecksum.contents,
+ auth_pack->pkAuthenticator.paChecksum.length);
+ pkiDebug("expected checksum type=%d size=%d ",
+ cksum.checksum_type, cksum.length);
+ print_buffer(cksum.contents, cksum.length);
+#endif
+
+ retval = KRB5KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED;
+ goto cleanup;
+ }
+
+ /* check if kdcPkId present and match KDC's subjectIdentifier */
+ if (reqp->kdcPkId.data != NULL) {
+ int valid_kdcPkId = 0;
+ retval = pkinit_check_kdc_pkid(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, plgctx->idctx,
+ reqp->kdcPkId.data, reqp->kdcPkId.length, &valid_kdcPkId);
+ if (retval)
+ goto cleanup;
+ if (!valid_kdcPkId)
+ pkiDebug("kdcPkId in AS_REQ does not match KDC's cert"
+ "RFC says to ignore and proceed\n");
+
+ }
+ /* remember the decoded auth_pack for verify_padata routine */
+ reqctx->rcv_auth_pack = auth_pack;
+ auth_pack = NULL;
+ break;
+ case KRB5_PADATA_PK_AS_REP_OLD:
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ retval = k5int_decode_krb5_auth_pack_draft9(&k5data, &auth_pack9);
+ if (retval) {
+ pkiDebug("failed to decode krb5_auth_pack_draft9\n");
+ goto cleanup;
+ }
+ if (auth_pack9->clientPublicValue != NULL) {
+ retval = server_check_dh(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, plgctx->idctx,
+ &auth_pack9->clientPublicValue->algorithm.parameters,
+ plgctx->opts->dh_min_bits);
+
+ if (retval) {
+ pkiDebug("bad dh parameters\n");
+ goto cleanup;
+ }
+ }
+ /* remember the decoded auth_pack for verify_padata routine */
+ reqctx->rcv_auth_pack9 = auth_pack9;
+ auth_pack9 = NULL;
+ break;
+ }
+
+ /* return authorization data to be included in the ticket */
+ switch ((int)data->pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ my_authz_data = malloc(2 * sizeof(*my_authz_data));
+ if (my_authz_data == NULL) {
+ retval = ENOMEM;
+ pkiDebug("Couldn't allocate krb5_authdata ptr array\n");
+ goto cleanup;
+ }
+ my_authz_data[1] = NULL;
+ my_authz_data[0] = malloc(sizeof(krb5_authdata));
+ if (my_authz_data[0] == NULL) {
+ retval = ENOMEM;
+ pkiDebug("Couldn't allocate krb5_authdata\n");
+ free(my_authz_data);
+ goto cleanup;
+ }
+ /* AD-INITIAL-VERIFIED-CAS must be wrapped in AD-IF-RELEVANT */
+ my_authz_data[0]->magic = KV5M_AUTHDATA;
+ my_authz_data[0]->ad_type = KRB5_AUTHDATA_IF_RELEVANT;
+
+ /* create an internal AD-INITIAL-VERIFIED-CAS data */
+ pkinit_authz_data = malloc(sizeof(krb5_authdata));
+ if (pkinit_authz_data == NULL) {
+ retval = ENOMEM;
+ pkiDebug("Couldn't allocate krb5_authdata\n");
+ free(my_authz_data[0]);
+ free(my_authz_data);
+ goto cleanup;
+ }
+ pkinit_authz_data->ad_type = KRB5_AUTHDATA_INITIAL_VERIFIED_CAS;
+ /* content of this ad-type contains the certification
+ path with which the client certificate was validated
+ */
+ pkinit_authz_data->contents = krb5_authz.data;
+ pkinit_authz_data->length = krb5_authz.length;
+ retval = k5int_encode_krb5_authdata_elt(pkinit_authz_data,
+ &encoded_pkinit_authz_data);
+#ifdef DEBUG_ASN1
+ print_buffer_bin((unsigned char *)encoded_pkinit_authz_data->data,
+ encoded_pkinit_authz_data->length,
+ "/tmp/kdc_pkinit_authz_data");
+#endif
+ free(pkinit_authz_data);
+ if (retval) {
+ pkiDebug("k5int_encode_krb5_authdata_elt failed\n");
+ free(my_authz_data[0]);
+ free(my_authz_data);
+ goto cleanup;
+ }
+
+ my_authz_data[0]->contents =
+ (krb5_octet *) encoded_pkinit_authz_data->data;
+ my_authz_data[0]->length = encoded_pkinit_authz_data->length;
+ *authz_data = my_authz_data;
+ pkiDebug("Returning %d bytes of authorization data\n",
+ krb5_authz.length);
+ encoded_pkinit_authz_data->data = NULL; /* Don't free during cleanup*/
+ free(encoded_pkinit_authz_data);
+ break;
+ default:
+ *authz_data = NULL;
+ }
+ /* remember to set the PREAUTH flag in the reply */
+ enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
+ *pa_request_context = reqctx;
+ reqctx = NULL;
+
+ cleanup:
+ if (retval && data->pa_type == KRB5_PADATA_PK_AS_REQ) {
+ pkiDebug("pkinit_verify_padata failed: creating e-data\n");
+ if (pkinit_create_edata(context, plgctx->cryptoctx, reqctx->cryptoctx,
+ plgctx->idctx, plgctx->opts, retval, e_data))
+ pkiDebug("pkinit_create_edata failed\n");
+ }
+
+ switch ((int)data->pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ free_krb5_pa_pk_as_req(&reqp);
+ if (cksum.contents != NULL)
+ free(cksum.contents);
+ if (der_req != NULL)
+ krb5_free_data(context, der_req);
+ break;
+ case KRB5_PADATA_PK_AS_REP_OLD:
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ free_krb5_pa_pk_as_req_draft9(&reqp9);
+ }
+ if (tmp_as_req != NULL)
+ k5int_krb5_free_kdc_req(context, tmp_as_req);
+ if (authp_data.data != NULL)
+ free(authp_data.data);
+ if (krb5_authz.data != NULL)
+ free(krb5_authz.data);
+ if (reqctx != NULL)
+ pkinit_fini_kdc_req_context(context, reqctx);
+ if (auth_pack != NULL)
+ free_krb5_auth_pack(&auth_pack);
+ if (auth_pack9 != NULL)
+ free_krb5_auth_pack_draft9(context, &auth_pack9);
+
+ return retval;
+}
+
+static krb5_error_code
+pkinit_server_return_padata(krb5_context context,
+ krb5_pa_data * padata,
+ struct _krb5_db_entry_new * client,
+ krb5_data *req_pkt,
+ krb5_kdc_req * request,
+ krb5_kdc_rep * reply,
+ struct _krb5_key_data * client_key,
+ krb5_keyblock * encrypting_key,
+ krb5_pa_data ** send_pa,
+ preauth_get_entry_data_proc server_get_entry_data,
+ void *pa_plugin_context,
+ void **pa_request_context)
+{
+ krb5_error_code retval = 0;
+ krb5_data scratch = {0, 0, NULL};
+ krb5_pa_pk_as_req *reqp = NULL;
+ krb5_pa_pk_as_req_draft9 *reqp9 = NULL;
+ int i = 0;
+
+ unsigned char *subjectPublicKey = NULL;
+ unsigned char *dh_pubkey = NULL, *server_key = NULL;
+ unsigned int subjectPublicKey_len = 0;
+ unsigned int server_key_len = 0, dh_pubkey_len = 0;
+
+ krb5_kdc_dh_key_info dhkey_info;
+ krb5_data *encoded_dhkey_info = NULL;
+ krb5_pa_pk_as_rep *rep = NULL;
+ krb5_pa_pk_as_rep_draft9 *rep9 = NULL;
+ krb5_data *out_data = NULL;
+
+ krb5_enctype enctype = -1;
+
+ krb5_reply_key_pack *key_pack = NULL;
+ krb5_reply_key_pack_draft9 *key_pack9 = NULL;
+ krb5_data *encoded_key_pack = NULL;
+ unsigned int num_types;
+ krb5_cksumtype *cksum_types = NULL;
+
+ pkinit_kdc_context plgctx;
+ pkinit_kdc_req_context reqctx;
+
+ int fixed_keypack = 0;
+
+ *send_pa = NULL;
+ if (padata == NULL || padata->length <= 0 || padata->contents == NULL)
+ return 0;
+
+ if (pa_request_context == NULL || *pa_request_context == NULL) {
+ pkiDebug("missing request context \n");
+ return EINVAL;
+ }
+
+ plgctx = pkinit_find_realm_context(context, pa_plugin_context,
+ request->server);
+ if (plgctx == NULL) {
+ pkiDebug("Unable to locate correct realm context\n");
+ return ENOENT;
+ }
+
+ pkiDebug("pkinit_return_padata: entered!\n");
+ reqctx = (pkinit_kdc_req_context)*pa_request_context;
+
+ if (encrypting_key->contents) {
+ free(encrypting_key->contents);
+ encrypting_key->length = 0;
+ encrypting_key->contents = NULL;
+ }
+
+ for(i = 0; i < request->nktypes; i++) {
+ enctype = request->ktype[i];
+ if (!krb5_c_valid_enctype(enctype))
+ continue;
+ else {
+ pkiDebug("KDC picked etype = %d\n", enctype);
+ break;
+ }
+ }
+
+ if (i == request->nktypes) {
+ retval = KRB5KDC_ERR_ETYPE_NOSUPP;
+ goto cleanup;
+ }
+
+ switch((int)reqctx->pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ init_krb5_pa_pk_as_rep(&rep);
+ if (rep == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ /* let's assume it's RSA. we'll reset it to DH if needed */
+ rep->choice = choice_pa_pk_as_rep_encKeyPack;
+ break;
+ case KRB5_PADATA_PK_AS_REP_OLD:
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ init_krb5_pa_pk_as_rep_draft9(&rep9);
+ if (rep9 == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ rep9->choice = choice_pa_pk_as_rep_draft9_encKeyPack;
+ break;
+ default:
+ retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ goto cleanup;
+ }
+
+ if (reqctx->rcv_auth_pack != NULL &&
+ reqctx->rcv_auth_pack->clientPublicValue != NULL) {
+ subjectPublicKey =
+ reqctx->rcv_auth_pack->clientPublicValue->subjectPublicKey.data;
+ subjectPublicKey_len =
+ reqctx->rcv_auth_pack->clientPublicValue->subjectPublicKey.length;
+ rep->choice = choice_pa_pk_as_rep_dhInfo;
+ } else if (reqctx->rcv_auth_pack9 != NULL &&
+ reqctx->rcv_auth_pack9->clientPublicValue != NULL) {
+ subjectPublicKey =
+ reqctx->rcv_auth_pack9->clientPublicValue->subjectPublicKey.data;
+ subjectPublicKey_len =
+ reqctx->rcv_auth_pack9->clientPublicValue->subjectPublicKey.length;
+ rep9->choice = choice_pa_pk_as_rep_draft9_dhSignedData;
+ }
+
+ /* if this DH, then process finish computing DH key */
+ if (rep != NULL && (rep->choice == choice_pa_pk_as_rep_dhInfo ||
+ rep->choice == choice_pa_pk_as_rep_draft9_dhSignedData)) {
+ pkiDebug("received DH key delivery AS REQ\n");
+ retval = server_process_dh(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, plgctx->idctx, subjectPublicKey,
+ subjectPublicKey_len, &dh_pubkey, &dh_pubkey_len,
+ &server_key, &server_key_len);
+ if (retval) {
+ pkiDebug("failed to process/create dh paramters\n");
+ goto cleanup;
+ }
+ }
+
+ if ((rep9 != NULL &&
+ rep9->choice == choice_pa_pk_as_rep_draft9_dhSignedData) ||
+ (rep != NULL && rep->choice == choice_pa_pk_as_rep_dhInfo)) {
+ retval = pkinit_octetstring2key(context, enctype, server_key,
+ server_key_len, encrypting_key);
+ if (retval) {
+ pkiDebug("pkinit_octetstring2key failed: %s\n",
+ error_message(retval));
+ goto cleanup;
+ }
+
+ dhkey_info.subjectPublicKey.length = dh_pubkey_len;
+ dhkey_info.subjectPublicKey.data = dh_pubkey;
+ dhkey_info.nonce = request->nonce;
+ dhkey_info.dhKeyExpiration = 0;
+
+ retval = k5int_encode_krb5_kdc_dh_key_info(&dhkey_info,
+ &encoded_dhkey_info);
+ if (retval) {
+ pkiDebug("encode_krb5_kdc_dh_key_info failed\n");
+ goto cleanup;
+ }
+#ifdef DEBUG_ASN1
+ print_buffer_bin((unsigned char *)encoded_dhkey_info->data,
+ encoded_dhkey_info->length,
+ "/tmp/kdc_dh_key_info");
+#endif
+
+ switch ((int)padata->pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ retval = cms_signeddata_create(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_SERVER, 1,
+ (unsigned char *)encoded_dhkey_info->data,
+ encoded_dhkey_info->length,
+ &rep->u.dh_Info.dhSignedData.data,
+ &rep->u.dh_Info.dhSignedData.length);
+ if (retval) {
+ pkiDebug("failed to create pkcs7 signed data\n");
+ goto cleanup;
+ }
+ break;
+ case KRB5_PADATA_PK_AS_REP_OLD:
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ retval = cms_signeddata_create(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_DRAFT9, 1,
+ (unsigned char *)encoded_dhkey_info->data,
+ encoded_dhkey_info->length,
+ &rep9->u.dhSignedData.data,
+ &rep9->u.dhSignedData.length);
+ if (retval) {
+ pkiDebug("failed to create pkcs7 signed data\n");
+ goto cleanup;
+ }
+ break;
+ }
+ } else {
+ pkiDebug("received RSA key delivery AS REQ\n");
+
+ retval = krb5_c_make_random_key(context, enctype, encrypting_key);
+ if (retval) {
+ pkiDebug("unable to make a session key\n");
+ goto cleanup;
+ }
+
+ /* check if PA_TYPE of 132 is present which means the client is
+ * requesting that a checksum is send back instead of the nonce
+ */
+ for (i = 0; request->padata[i] != NULL; i++) {
+ pkiDebug("%s: Checking pa_type 0x%08x\n",
+ __FUNCTION__, request->padata[i]->pa_type);
+ if (request->padata[i]->pa_type == 132)
+ fixed_keypack = 1;
+ }
+ pkiDebug("%s: return checksum instead of nonce = %d\n",
+ __FUNCTION__, fixed_keypack);
+
+ /* if this is an RFC reply or draft9 client requested a checksum
+ * in the reply instead of the nonce, create an RFC-style keypack
+ */
+ if ((int)padata->pa_type == KRB5_PADATA_PK_AS_REQ || fixed_keypack) {
+ init_krb5_reply_key_pack(&key_pack);
+ if (key_pack == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ /* retrieve checksums for a given enctype of the reply key */
+ retval = krb5_c_keyed_checksum_types(context,
+ encrypting_key->enctype, &num_types, &cksum_types);
+ if (retval)
+ goto cleanup;
+
+ /* pick the first of acceptable enctypes for the checksum */
+ retval = krb5_c_make_checksum(context, cksum_types[0],
+ encrypting_key, KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
+ req_pkt, &key_pack->asChecksum);
+ if (retval) {
+ pkiDebug("unable to calculate AS REQ checksum\n");
+ goto cleanup;
+ }
+#ifdef DEBUG_CKSUM
+ pkiDebug("calculating checksum on buf size = %d\n", req_pkt->length);
+ print_buffer(req_pkt->data, req_pkt->length);
+ pkiDebug("checksum size = %d\n", key_pack->asChecksum.length);
+ print_buffer(key_pack->asChecksum.contents,
+ key_pack->asChecksum.length);
+ pkiDebug("encrypting key (%d)\n", encrypting_key->length);
+ print_buffer(encrypting_key->contents, encrypting_key->length);
+#endif
+
+ krb5_copy_keyblock_contents(context, encrypting_key,
+ &key_pack->replyKey);
+
+ retval = k5int_encode_krb5_reply_key_pack(key_pack,
+ &encoded_key_pack);
+ if (retval) {
+ pkiDebug("failed to encode reply_key_pack\n");
+ goto cleanup;
+ }
+ }
+
+ switch ((int)padata->pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ rep->choice = choice_pa_pk_as_rep_encKeyPack;
+ retval = cms_envelopeddata_create(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, plgctx->idctx, padata->pa_type, 1,
+ (unsigned char *)encoded_key_pack->data,
+ encoded_key_pack->length,
+ &rep->u.encKeyPack.data, &rep->u.encKeyPack.length);
+ break;
+ case KRB5_PADATA_PK_AS_REP_OLD:
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ /* if the request is from the broken draft9 client that
+ * expects back a nonce, create it now
+ */
+ if (!fixed_keypack) {
+ init_krb5_reply_key_pack_draft9(&key_pack9);
+ if (key_pack9 == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ key_pack9->nonce = reqctx->rcv_auth_pack9->pkAuthenticator.nonce;
+ krb5_copy_keyblock_contents(context, encrypting_key,
+ &key_pack9->replyKey);
+
+ retval = k5int_encode_krb5_reply_key_pack_draft9(key_pack9,
+ &encoded_key_pack);
+ if (retval) {
+ pkiDebug("failed to encode reply_key_pack\n");
+ goto cleanup;
+ }
+ }
+
+ rep9->choice = choice_pa_pk_as_rep_draft9_encKeyPack;
+ retval = cms_envelopeddata_create(context, plgctx->cryptoctx,
+ reqctx->cryptoctx, plgctx->idctx, padata->pa_type, 1,
+ (unsigned char *)encoded_key_pack->data,
+ encoded_key_pack->length,
+ &rep9->u.encKeyPack.data, &rep9->u.encKeyPack.length);
+ break;
+ }
+ if (retval) {
+ pkiDebug("failed to create pkcs7 enveloped data: %s\n",
+ error_message(retval));
+ goto cleanup;
+ }
+#ifdef DEBUG_ASN1
+ print_buffer_bin((unsigned char *)encoded_key_pack->data,
+ encoded_key_pack->length,
+ "/tmp/kdc_key_pack");
+ switch ((int)padata->pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ print_buffer_bin(rep->u.encKeyPack.data,
+ rep->u.encKeyPack.length,
+ "/tmp/kdc_enc_key_pack");
+ break;
+ case KRB5_PADATA_PK_AS_REP_OLD:
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ print_buffer_bin(rep9->u.encKeyPack.data,
+ rep9->u.encKeyPack.length,
+ "/tmp/kdc_enc_key_pack");
+ break;
+ }
+#endif
+ }
+
+ switch ((int)padata->pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ retval = k5int_encode_krb5_pa_pk_as_rep(rep, &out_data);
+ break;
+ case KRB5_PADATA_PK_AS_REP_OLD:
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ retval = k5int_encode_krb5_pa_pk_as_rep_draft9(rep9, &out_data);
+ break;
+ }
+ if (retval) {
+ pkiDebug("failed to encode AS_REP\n");
+ goto cleanup;
+ }
+#ifdef DEBUG_ASN1
+ if (out_data != NULL)
+ print_buffer_bin((unsigned char *)out_data->data, out_data->length,
+ "/tmp/kdc_as_rep");
+#endif
+
+ *send_pa = (krb5_pa_data *) malloc(sizeof(krb5_pa_data));
+ if (*send_pa == NULL) {
+ retval = ENOMEM;
+ free(out_data->data);
+ free(out_data);
+ out_data = NULL;
+ goto cleanup;
+ }
+ (*send_pa)->magic = KV5M_PA_DATA;
+ switch ((int)padata->pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ (*send_pa)->pa_type = KRB5_PADATA_PK_AS_REP;
+ break;
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ case KRB5_PADATA_PK_AS_REP_OLD:
+ (*send_pa)->pa_type = KRB5_PADATA_PK_AS_REP_OLD;
+ break;
+ }
+ (*send_pa)->length = out_data->length;
+ (*send_pa)->contents = (krb5_octet *) out_data->data;
+
+
+ cleanup:
+ pkinit_fini_kdc_req_context(context, reqctx);
+ if (scratch.data != NULL)
+ free(scratch.data);
+ if (out_data != NULL)
+ free(out_data);
+ if (encoded_dhkey_info != NULL)
+ krb5_free_data(context, encoded_dhkey_info);
+ if (encoded_key_pack != NULL)
+ krb5_free_data(context, encoded_key_pack);
+ if (dh_pubkey != NULL)
+ free(dh_pubkey);
+ if (server_key != NULL)
+ free(server_key);
+ if (cksum_types != NULL)
+ free(cksum_types);
+
+ switch ((int)padata->pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ free_krb5_pa_pk_as_req(&reqp);
+ free_krb5_pa_pk_as_rep(&rep);
+ free_krb5_reply_key_pack(&key_pack);
+ break;
+ case KRB5_PADATA_PK_AS_REP_OLD:
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ free_krb5_pa_pk_as_req_draft9(&reqp9);
+ free_krb5_pa_pk_as_rep_draft9(&rep9);
+ if (!fixed_keypack)
+ free_krb5_reply_key_pack_draft9(&key_pack9);
+ else
+ free_krb5_reply_key_pack(&key_pack);
+ break;
+ }
+
+ if (retval)
+ pkiDebug("pkinit_verify_padata failure");
+
+ return retval;
+}
+
+static int
+pkinit_server_get_flags(krb5_context kcontext, krb5_preauthtype patype)
+{
+ return PA_SUFFICIENT | PA_REPLACES_KEY;
+}
+
+static krb5_preauthtype supported_server_pa_types[] = {
+ KRB5_PADATA_PK_AS_REQ,
+ KRB5_PADATA_PK_AS_REQ_OLD,
+ KRB5_PADATA_PK_AS_REP_OLD,
+ 0
+};
+
+static void
+pkinit_fini_kdc_profile(krb5_context context, pkinit_kdc_context plgctx)
+{
+ /*
+ * There is nothing currently allocated by pkinit_init_kdc_profile()
+ * which needs to be freed here.
+ */
+}
+
+static krb5_error_code
+pkinit_init_kdc_profile(krb5_context context, pkinit_kdc_context plgctx)
+{
+ krb5_error_code retval;
+ char *eku_string = NULL;
+
+ pkiDebug("%s: entered for realm %s\n", __FUNCTION__, plgctx->realmname);
+ retval = pkinit_kdcdefault_string(context, plgctx->realmname,
+ "pkinit_identity",
+ &plgctx->idopts->identity);
+ if (retval != 0 || NULL == plgctx->idopts->identity) {
+ retval = EINVAL;
+ krb5_set_error_message(context, retval,
+ "No pkinit_identity supplied for realm %s",
+ plgctx->realmname);
+ goto errout;
+ }
+
+ retval = pkinit_kdcdefault_strings(context, plgctx->realmname,
+ "pkinit_anchors",
+ &plgctx->idopts->anchors);
+ if (retval != 0 || NULL == plgctx->idopts->anchors) {
+ retval = EINVAL;
+ krb5_set_error_message(context, retval,
+ "No pkinit_anchors supplied for realm %s",
+ plgctx->realmname);
+ goto errout;
+ }
+
+ pkinit_kdcdefault_strings(context, plgctx->realmname,
+ "pkinit_pool",
+ &plgctx->idopts->intermediates);
+
+ pkinit_kdcdefault_strings(context, plgctx->realmname,
+ "pkinit_revoke",
+ &plgctx->idopts->crls);
+
+ pkinit_kdcdefault_string(context, plgctx->realmname,
+ "pkinit_kdc_ocsp",
+ &plgctx->idopts->ocsp);
+
+ pkinit_kdcdefault_string(context, plgctx->realmname,
+ "pkinit_mappings_file",
+ &plgctx->idopts->dn_mapping_file);
+
+ pkinit_kdcdefault_integer(context, plgctx->realmname,
+ "pkinit_dh_min_bits",
+ PKINIT_DEFAULT_DH_MIN_BITS,
+ &plgctx->opts->dh_min_bits);
+ if (plgctx->opts->dh_min_bits < 1024) {
+ pkiDebug("%s: invalid value (%d) for pkinit_dh_min_bits, "
+ "using default value (%d) instead\n", __FUNCTION__,
+ plgctx->opts->dh_min_bits, PKINIT_DEFAULT_DH_MIN_BITS);
+ plgctx->opts->dh_min_bits = PKINIT_DEFAULT_DH_MIN_BITS;
+ }
+
+ pkinit_kdcdefault_boolean(context, plgctx->realmname,
+ "pkinit_allow_upn",
+ 0, &plgctx->opts->allow_upn);
+
+ pkinit_kdcdefault_boolean(context, plgctx->realmname,
+ "pkinit_require_crl_checking",
+ 0, &plgctx->opts->require_crl_checking);
+
+ pkinit_kdcdefault_string(context, plgctx->realmname,
+ "pkinit_eku_checking",
+ &eku_string);
+ if (eku_string != NULL) {
+ if (strcasecmp(eku_string, "kpClientAuth") == 0) {
+ plgctx->opts->require_eku = 1;
+ plgctx->opts->accept_secondary_eku = 0;
+ } else if (strcasecmp(eku_string, "scLogin") == 0) {
+ plgctx->opts->require_eku = 1;
+ plgctx->opts->accept_secondary_eku = 1;
+ } else if (strcasecmp(eku_string, "none") == 0) {
+ plgctx->opts->require_eku = 0;
+ plgctx->opts->accept_secondary_eku = 0;
+ } else {
+ pkiDebug("%s: Invalid value for pkinit_eku_checking: '%s'\n",
+ __FUNCTION__, eku_string);
+ }
+ free(eku_string);
+ }
+
+
+ return 0;
+errout:
+ pkinit_fini_kdc_profile(context, plgctx);
+ return retval;
+}
+
+static pkinit_kdc_context
+pkinit_find_realm_context(krb5_context context, void *pa_plugin_context,
+ krb5_principal princ)
+{
+ int i;
+ pkinit_kdc_context *realm_contexts = pa_plugin_context;
+
+ if (pa_plugin_context == NULL)
+ return NULL;
+
+ for (i = 0; realm_contexts[i] != NULL; i++) {
+ pkinit_kdc_context p = realm_contexts[i];
+
+ if ((p->realmname_len == princ->realm.length) &&
+ (strncmp(p->realmname, princ->realm.data, p->realmname_len) == 0)) {
+ pkiDebug("%s: returning context at %p for realm '%s'\n",
+ __FUNCTION__, p, p->realmname);
+ return p;
+ }
+ }
+ pkiDebug("%s: unable to find realm context for realm '%.*s'\n",
+ __FUNCTION__, princ->realm.length, princ->realm.data);
+ return NULL;
+}
+
+static int
+pkinit_server_plugin_init_realm(krb5_context context, const char *realmname,
+ pkinit_kdc_context *pplgctx)
+{
+ krb5_error_code retval = ENOMEM;
+ pkinit_kdc_context plgctx = NULL;
+
+ *pplgctx = NULL;
+
+ plgctx = (pkinit_kdc_context) calloc(1, sizeof(*plgctx));
+ if (plgctx == NULL)
+ goto errout;
+
+ pkiDebug("%s: initializing context at %p for realm '%s'\n",
+ __FUNCTION__, plgctx, realmname);
+ memset(plgctx, 0, sizeof(*plgctx));
+ plgctx->magic = PKINIT_CTX_MAGIC;
+
+ plgctx->realmname = strdup(realmname);
+ if (plgctx->realmname == NULL)
+ goto errout;
+ plgctx->realmname_len = strlen(plgctx->realmname);
+
+ retval = pkinit_init_plg_crypto(&plgctx->cryptoctx);
+ if (retval)
+ goto errout;
+
+ retval = pkinit_init_plg_opts(&plgctx->opts);
+ if (retval)
+ goto errout;
+
+ retval = pkinit_init_identity_crypto(&plgctx->idctx);
+ if (retval)
+ goto errout;
+
+ retval = pkinit_init_identity_opts(&plgctx->idopts);
+ if (retval)
+ goto errout;
+
+ retval = pkinit_init_kdc_profile(context, plgctx);
+ if (retval)
+ goto errout;
+
+ retval = pkinit_identity_initialize(context, plgctx->cryptoctx, NULL,
+ plgctx->idopts, plgctx->idctx, 0, NULL);
+ if (retval)
+ goto errout;
+
+ pkiDebug("%s: returning context at %p for realm '%s'\n",
+ __FUNCTION__, plgctx, realmname);
+ *pplgctx = plgctx;
+ retval = 0;
+
+errout:
+ if (retval)
+ pkinit_server_plugin_fini_realm(context, plgctx);
+
+ return retval;
+}
+
+static int
+pkinit_server_plugin_init(krb5_context context, void **blob,
+ const char **realmnames)
+{
+ krb5_error_code retval = ENOMEM;
+ pkinit_kdc_context plgctx, *realm_contexts = NULL;
+ int i, j;
+ size_t numrealms;
+
+ retval = pkinit_accessor_init();
+ if (retval)
+ return retval;
+
+ /* Determine how many realms we may need to support */
+ for (i = 0; realmnames[i] != NULL; i++) {};
+ numrealms = i;
+
+ realm_contexts = (pkinit_kdc_context *)
+ calloc(numrealms+1, sizeof(pkinit_kdc_context));
+ if (realm_contexts == NULL)
+ 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)
+ realm_contexts[j++] = plgctx;
+ }
+
+ if (j == 0) {
+ retval = EINVAL;
+ krb5_set_error_message(context, retval, "No realms configured "
+ "correctly for pkinit support");
+ goto errout;
+ }
+
+ *blob = realm_contexts;
+ retval = 0;
+ pkiDebug("%s: returning context at %p\n", __FUNCTION__, realm_contexts);
+
+errout:
+ if (retval)
+ pkinit_server_plugin_fini(context, realm_contexts);
+
+ return retval;
+}
+
+static void
+pkinit_server_plugin_fini_realm(krb5_context context, pkinit_kdc_context plgctx)
+{
+ if (plgctx == NULL)
+ return;
+
+ pkinit_fini_kdc_profile(context, plgctx);
+ pkinit_fini_identity_opts(plgctx->idopts);
+ pkinit_fini_identity_crypto(plgctx->idctx);
+ pkinit_fini_plg_crypto(plgctx->cryptoctx);
+ pkinit_fini_plg_opts(plgctx->opts);
+ free(plgctx->realmname);
+ free(plgctx);
+}
+
+static void
+pkinit_server_plugin_fini(krb5_context context, void *blob)
+{
+ pkinit_kdc_context *realm_contexts = blob;
+ 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 krb5_error_code
+pkinit_init_kdc_req_context(krb5_context context, void **ctx)
+{
+ krb5_error_code retval = ENOMEM;
+ pkinit_kdc_req_context reqctx = NULL;
+
+ reqctx = (pkinit_kdc_req_context)malloc(sizeof(*reqctx));
+ if (reqctx == NULL)
+ return retval;
+ memset(reqctx, 0, sizeof(*reqctx));
+ reqctx->magic = PKINIT_CTX_MAGIC;
+
+ retval = pkinit_init_req_crypto(&reqctx->cryptoctx);
+ if (retval)
+ goto cleanup;
+ reqctx->rcv_auth_pack = NULL;
+ reqctx->rcv_auth_pack9 = NULL;
+
+ pkiDebug("%s: returning reqctx at %p\n", __FUNCTION__, reqctx);
+ *ctx = reqctx;
+ retval = 0;
+cleanup:
+ if (retval)
+ pkinit_fini_kdc_req_context(context, reqctx);
+
+ return retval;
+}
+
+static void
+pkinit_fini_kdc_req_context(krb5_context context, void *ctx)
+{
+ pkinit_kdc_req_context reqctx = (pkinit_kdc_req_context)ctx;
+
+ if (reqctx == NULL || reqctx->magic != PKINIT_CTX_MAGIC) {
+ pkiDebug("pkinit_fini_kdc_req_context: got bad reqctx (%p)!\n", reqctx);
+ return;
+ }
+ pkiDebug("%s: freeing reqctx at %p\n", __FUNCTION__, reqctx);
+
+ pkinit_fini_req_crypto(reqctx->cryptoctx);
+ if (reqctx->rcv_auth_pack != NULL)
+ free_krb5_auth_pack(&reqctx->rcv_auth_pack);
+ if (reqctx->rcv_auth_pack9 != NULL)
+ free_krb5_auth_pack_draft9(context, &reqctx->rcv_auth_pack9);
+
+ free(reqctx);
+}
+
+struct krb5plugin_preauth_server_ftable_v1 preauthentication_server_1 = {
+ "pkinit", /* name */
+ supported_server_pa_types, /* pa_type_list */
+ pkinit_server_plugin_init, /* (*init_proc) */
+ pkinit_server_plugin_fini, /* (*fini_proc) */
+ pkinit_server_get_flags, /* (*flags_proc) */
+ pkinit_server_get_edata, /* (*edata_proc) */
+ pkinit_server_verify_padata,/* (*verify_proc) */
+ pkinit_server_return_padata,/* (*return_proc) */
+ NULL, /* (*freepa_reqcontext_proc) */
+};
diff --git a/src/plugins/preauth/wpse/wpse_main.c b/src/plugins/preauth/wpse/wpse_main.c
index fd6ff92..22dfd13 100644
--- a/src/plugins/preauth/wpse/wpse_main.c
+++ b/src/plugins/preauth/wpse/wpse_main.c
@@ -30,7 +30,7 @@
/* Worst. Preauthentication. Scheme. Ever. */
-#ident "$Id$"
+#ident "$Id: wpse_main.c,v 1.3 2007/01/02 22:33:51 kwc Exp $"
#include "autoconf.h"
@@ -101,9 +101,9 @@ client_process(krb5_context kcontext,
void *gak_data,
krb5_data *salt, krb5_data *s2kparams,
krb5_keyblock *as_key,
- krb5_pa_data **out_pa_data)
+ krb5_pa_data ***out_pa_data)
{
- krb5_pa_data *send_pa;
+ krb5_pa_data **send_pa;
krb5_int32 nnonce, enctype;
krb5_keyblock *kb;
krb5_error_code status;
@@ -121,19 +121,26 @@ client_process(krb5_context kcontext,
if (pa_data->length == 0) {
/* Create preauth data. */
- send_pa = malloc(sizeof(krb5_pa_data));
+ send_pa = malloc(2 * sizeof(krb5_pa_data *));
if (send_pa == NULL)
return ENOMEM;
- send_pa->pa_type = KRB5_PADATA_WPSE_REQ;
- send_pa->length = 4;
- send_pa->contents = malloc(4);
- if (send_pa->contents == NULL) {
+ send_pa[1] = NULL; /* Terminate list */
+ send_pa[0] = malloc(sizeof(krb5_pa_data));
+ if (send_pa[0] == NULL) {
+ free(send_pa);
+ return ENOMEM;
+ }
+ send_pa[0]->pa_type = KRB5_PADATA_WPSE_REQ;
+ send_pa[0]->length = 4;
+ send_pa[0]->contents = malloc(4);
+ if (send_pa[0]->contents == NULL) {
+ free(send_pa[0]);
free(send_pa);
return ENOMEM;
}
/* Store the preauth data. */
nnonce = htonl(request->nonce);
- memcpy(send_pa->contents, &nnonce, 4);
+ memcpy(send_pa[0]->contents, &nnonce, 4);
*out_pa_data = send_pa;
} else {
/* A reply from the KDC. Conventionally this would be
@@ -262,11 +269,16 @@ server_verify(krb5_context kcontext,
preauth_get_entry_data_proc server_get_entry_data,
void *pa_module_context,
void **pa_request_context,
- krb5_data **e_data)
+ krb5_data **e_data,
+ krb5_authdata ***authz_data)
{
krb5_int32 nnonce;
krb5_data *test_edata;
+ krb5_authdata **my_authz_data;
+#ifdef DEBUG
+ fprintf(stderr, "wpse: server_verify()!\n");
+#endif
/* Verify the preauth data. */
if (data->length != 4)
return KRB5KDC_ERR_PREAUTH_FAILED;
@@ -282,6 +294,54 @@ server_verify(krb5_context kcontext,
if (*pa_request_context == NULL)
*pa_request_context = malloc(4);
+ /*
+ * Return some junk authorization data just to exercise the
+ * code path handling the returned authorization data.
+ *
+ * NOTE that this is NOT VALID authorization data!
+ */
+#ifdef DEBUG
+ fprintf(stderr, "wpse: doing authorization data!\n");
+#endif
+#if 1 /* USE_5000_AD */
+#define AD_ALLOC_SIZE 5000
+ /* ad_header consists of a sequence tag (0x30) and length (0x82 0x1384)
+ * followed by octet string tag (0x04) and length (0x82 0x1380) */
+ krb5_octet ad_header[] = {0x30, 0x82, 0x13, 0x84, 0x04, 0x82, 0x13, 0x80};
+#else
+#define AD_ALLOC_SIZE 100
+ /* ad_header consists of a sequence tag (0x30) and length (0x62)
+ * followed by octet string tag (0x04) and length (0x60) */
+ krb5_octet ad_header[] = {0x30, 0x62, 0x04, 0x60};
+#endif
+ my_authz_data = malloc(2 * sizeof(*my_authz_data));
+ if (my_authz_data != NULL) {
+ my_authz_data[1] = NULL;
+ my_authz_data[0] = malloc(sizeof(krb5_authdata));
+ if (my_authz_data[0] == NULL) {
+ free(my_authz_data);
+ return ENOMEM;
+ }
+ my_authz_data[0]->contents = malloc(AD_ALLOC_SIZE);
+ if (my_authz_data[0]->contents == NULL) {
+ free(my_authz_data[0]);
+ free(my_authz_data);
+ return ENOMEM;
+ }
+ memset(my_authz_data[0]->contents, '\0', AD_ALLOC_SIZE);
+ my_authz_data[0]->magic = KV5M_AUTHDATA;
+ my_authz_data[0]->ad_type = 1;
+ my_authz_data[0]->length = AD_ALLOC_SIZE;
+ memcpy(my_authz_data[0]->contents, ad_header, sizeof(ad_header));
+ sprintf(my_authz_data[0]->contents + sizeof(ad_header),
+ "wpse authorization data: %d bytes worth!\n", AD_ALLOC_SIZE);
+ *authz_data = my_authz_data;
+#ifdef DEBUG
+ fprintf(stderr, "Returning %d bytes of authorization data\n",
+ AD_ALLOC_SIZE);
+#endif
+ }
+
/* Return edata to exercise code that handles edata... */
test_edata = malloc(sizeof(*test_edata));
if (test_edata != NULL) {
@@ -366,6 +426,7 @@ server_return(krb5_context kcontext,
krb5_free_keyblock_contents(kcontext, encrypting_key);
krb5_copy_keyblock_contents(kcontext, kb, encrypting_key);
+
/* Clean up. */
krb5_free_keyblock(kcontext, kb);
@@ -375,13 +436,13 @@ server_return(krb5_context kcontext,
static int
server_get_flags(krb5_context kcontext, krb5_preauthtype pa_type)
{
- return PA_HARDWARE | PA_REPLACES_KEY;
+ return PA_HARDWARE | PA_REPLACES_KEY | PA_SUFFICIENT;
}
static krb5_preauthtype supported_client_pa_types[] = {KRB5_PADATA_WPSE_REQ, 0};
static krb5_preauthtype supported_server_pa_types[] = {KRB5_PADATA_WPSE_REQ, 0};
-struct krb5plugin_preauth_client_ftable_v0 preauthentication_client_0 = {
+struct krb5plugin_preauth_client_ftable_v1 preauthentication_client_1 = {
"wpse", /* name */
&supported_client_pa_types[0], /* pa_type_list */
NULL, /* enctype_list */
@@ -395,7 +456,7 @@ struct krb5plugin_preauth_client_ftable_v0 preauthentication_client_0 = {
client_gic_opt /* get init creds opts function */
};
-struct krb5plugin_preauth_server_ftable_v0 preauthentication_server_0 = {
+struct krb5plugin_preauth_server_ftable_v1 preauthentication_server_1 = {
"wpse",
&supported_server_pa_types[0],
NULL,