aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Coffman <kwc@citi.umich.edu>2007-05-29 22:20:17 +0000
committerKevin Coffman <kwc@citi.umich.edu>2007-05-29 22:20:17 +0000
commit0e661a5c3a09f10dd85537d51c9a767ac9f1137e (patch)
treeec8817cf46d7380a33375ca5e86c758b742765b7
parente1904aa474318b5020ca64829ad3d40ea132d871 (diff)
downloadkrb5-0e661a5c3a09f10dd85537d51c9a767ac9f1137e.zip
krb5-0e661a5c3a09f10dd85537d51c9a767ac9f1137e.tar.gz
krb5-0e661a5c3a09f10dd85537d51c9a767ac9f1137e.tar.bz2
Remove some old code from src/lib/krb5/asn.1/asn1_k_decode.c and
src/lib/krb5/krb/preauth2.c Add new file pkinit_matching.c which implements the ability for an admin to configure things such that the "right" certificate from several available ones will be used for pkinit. Fix a couple of things the Solaris compiler doesn't like. git-svn-id: svn://anonsvn.mit.edu/krb5/users/coffman/pkinit@19562 dc483132-0cff-0310-8789-dd5450dbe970
-rw-r--r--src/lib/krb5/asn.1/asn1_k_decode.c1
-rw-r--r--src/lib/krb5/krb/preauth2.c19
-rw-r--r--src/plugins/preauth/pkinit/Makefile.in7
-rw-r--r--src/plugins/preauth/pkinit/pkinit.h27
-rw-r--r--src/plugins/preauth/pkinit/pkinit_clnt.c38
-rw-r--r--src/plugins/preauth/pkinit/pkinit_crypto.h150
-rw-r--r--src/plugins/preauth/pkinit/pkinit_crypto_openssl.c729
-rw-r--r--src/plugins/preauth/pkinit/pkinit_crypto_openssl.h42
-rw-r--r--src/plugins/preauth/pkinit/pkinit_identity.c108
-rw-r--r--src/plugins/preauth/pkinit/pkinit_matching.c827
-rw-r--r--src/plugins/preauth/pkinit/pkinit_srv.c130
11 files changed, 1812 insertions, 266 deletions
diff --git a/src/lib/krb5/asn.1/asn1_k_decode.c b/src/lib/krb5/asn.1/asn1_k_decode.c
index 9117ff3..67c7f30 100644
--- a/src/lib/krb5/asn.1/asn1_k_decode.c
+++ b/src/lib/krb5/asn.1/asn1_k_decode.c
@@ -1248,7 +1248,6 @@ asn1_error_code asn1_decode_trusted_ca(asn1buf *buf, krb5_trusted_ca *val)
{
setup();
{ begin_choice();
- fprintf(stderr, "%s: ********************** The CHOICE is %d **********************\n", __FUNCTION__, tagnum);
if (tagnum == choice_trusted_cas_principalName) {
val->choice = choice_trusted_cas_principalName;
asn1_decode_krb5_principal_name(&subbuf, &(val->u.principalName));
diff --git a/src/lib/krb5/krb/preauth2.c b/src/lib/krb5/krb/preauth2.c
index 5cd81d8..18f1727 100644
--- a/src/lib/krb5/krb/preauth2.c
+++ b/src/lib/krb5/krb/preauth2.c
@@ -563,30 +563,11 @@ krb5_run_preauth_plugins(krb5_context kcontext,
/* Save the new preauth data item. */
if (out_pa_data != NULL) {
int i;
-#if 0 /* draft9 checksum hack */
- /*
- * this is a temporary hack for doing "fixed" draft9 client.
- * the client needs to send a pa_type 132 to the win2k kdc
- * then the kdc will reply back with a checksum instead
- * of the nonce
- */
- krb5_pa_data *tmp = NULL;
- tmp = malloc(sizeof(krb5_pa_data));
- if (tmp != NULL) {
- tmp->pa_type=132;
- tmp->length = 0;
- tmp->contents = NULL;
- }
-#endif
for (i = 0; out_pa_data[i] != NULL; i++);
ret = grow_pa_list(out_pa_list, out_pa_list_size, out_pa_data, i);
free(out_pa_data);
if (ret != 0)
return ret;
-#if 0 /* draft9 checksum hack */
- if (tmp != NULL)
- grow_pa_list(out_pa_list, out_pa_list_size, &tmp, 1);
-#endif
}
break;
}
diff --git a/src/plugins/preauth/pkinit/Makefile.in b/src/plugins/preauth/pkinit/Makefile.in
index 74dce37..1f7bd2f 100644
--- a/src/plugins/preauth/pkinit/Makefile.in
+++ b/src/plugins/preauth/pkinit/Makefile.in
@@ -33,6 +33,7 @@ STLIBOBJS= \
pkinit_clnt.o \
pkinit_profile.o \
pkinit_identity.o \
+ pkinit_matching.o \
pkinit_crypto_openssl.o
SRCS= \
@@ -42,6 +43,7 @@ SRCS= \
$(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)
@@ -95,6 +97,11 @@ 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 \
diff --git a/src/plugins/preauth/pkinit/pkinit.h b/src/plugins/preauth/pkinit/pkinit.h
index 352294e..5546de5 100644
--- a/src/plugins/preauth/pkinit/pkinit.h
+++ b/src/plugins/preauth/pkinit/pkinit.h
@@ -61,17 +61,11 @@
#define pkiDebug(args...)
#endif
-extern int longhorn; /* XXX Talking to a Longhorn server? */
-
-#define IDTYPE_FILE 1
-#define IDTYPE_DIR 2
-#define IDTYPE_PKCS11 3
-#define IDTYPE_ENVVAR 4
-#define IDTYPE_PKCS12 5
+/* Solaris compiler doesn't grok __FUNCTION__
+ * hack for now. Fix all the uses eventually. */
+#define __FUNCTION__ __func__
-#define CATYPE_ANCHORS 1
-#define CATYPE_INTERMEDIATES 2
-#define CATYPE_CRLS 3
+extern int longhorn; /* XXX Talking to a Longhorn server? */
/* Macros to deal with converting between various data types... */
#define PADATA_TO_KRB5DATA(pad, k5d) \
@@ -245,8 +239,19 @@ krb5_error_code pkinit_dup_identity_opts(pkinit_identity_opts *src_opts,
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 */
+ 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
diff --git a/src/plugins/preauth/pkinit/pkinit_clnt.c b/src/plugins/preauth/pkinit/pkinit_clnt.c
index 29c340d..818de0c 100644
--- a/src/plugins/preauth/pkinit/pkinit_clnt.c
+++ b/src/plugins/preauth/pkinit/pkinit_clnt.c
@@ -47,7 +47,7 @@ int longhorn = 0; /* XXX Talking to a Longhorn server? */
krb5_error_code pkinit_client_process
(krb5_context context, void *plugin_context, void *request_context,
- krb5_get_init_creds_opt *opt,
+ 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,
@@ -59,7 +59,7 @@ krb5_error_code pkinit_client_process
krb5_error_code pkinit_client_tryagain
(krb5_context context, void *plugin_context, void *request_context,
- krb5_get_init_creds_opt *opt,
+ 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,
@@ -81,7 +81,7 @@ krb5_error_code pa_pkinit_gen_req
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 *opt);
+ krb5_get_init_creds_opt *gic_opt);
krb5_error_code pkinit_as_req_create
(krb5_context context, pkinit_context plgctx,
@@ -120,7 +120,7 @@ pa_pkinit_gen_req(krb5_context context,
krb5_pa_data *** out_padata,
krb5_prompter_fct prompter,
void *prompter_data,
- krb5_get_init_creds_opt *opt)
+ krb5_get_init_creds_opt *gic_opt)
{
krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
@@ -130,7 +130,6 @@ pa_pkinit_gen_req(krb5_context context,
krb5_ui_4 nonce = 0;
krb5_checksum cksum;
krb5_data *der_req = NULL;
- char *server_principal = NULL;
krb5_pa_data **return_pa_data = NULL;
cksum.contents = NULL;
@@ -144,16 +143,12 @@ pa_pkinit_gen_req(krb5_context context,
return KRB5KDC_ERR_PREAUTH_FAILED;
}
- /* XXX Does this belong elsewhere ? */
- retval = krb5_unparse_name(context, request->server, &server_principal);
- if (retval)
- return retval;
-
- retval = pkinit_get_kdc_cert(context, plgctx->cryptoctx,
- reqctx->cryptoctx, reqctx->idctx, server_principal, opt);
- free(server_principal);
- if (retval)
+ 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 = encode_krb5_kdc_req_body(request, &der_req);
@@ -965,7 +960,7 @@ krb5_error_code
pkinit_client_process(krb5_context context,
void *plugin_context,
void *request_context,
- krb5_get_init_creds_opt *opt,
+ 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,
@@ -1023,8 +1018,9 @@ pkinit_client_process(krb5_context context,
if (processing_request) {
pkinit_client_profile(context, plgctx, reqctx, request);
pkinit_identity_set_prompter(reqctx->idctx, prompter, prompter_data);
- retval = pkinit_identity_initialize(context, reqctx->idopts,
- reqctx->idctx);
+ 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));
@@ -1032,7 +1028,7 @@ pkinit_client_process(krb5_context context,
}
retval = pa_pkinit_gen_req(context, plgctx, reqctx, request,
in_padata, out_padata, prompter,
- prompter_data, opt);
+ prompter_data, gic_opt);
} else {
/*
* Get the enctype of the reply.
@@ -1061,7 +1057,7 @@ krb5_error_code
pkinit_client_tryagain(krb5_context context,
void *plugin_context,
void *request_context,
- krb5_get_init_creds_opt *opt,
+ 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,
@@ -1141,7 +1137,7 @@ pkinit_client_tryagain(krb5_context context,
if (do_again) {
retval = pa_pkinit_gen_req(context, plgctx, reqctx, request, in_padata,
- out_padata, prompter, prompter_data, opt);
+ out_padata, prompter, prompter_data, gic_opt);
if (retval)
goto cleanup;
}
@@ -1421,7 +1417,7 @@ handle_gic_opt(krb5_context context,
static krb5_error_code
pkinit_client_gic_opt(krb5_context context,
void *plugin_context,
- krb5_get_init_creds_opt *opt,
+ krb5_get_init_creds_opt *gic_opt,
const char *attr,
const char *value)
{
diff --git a/src/plugins/preauth/pkinit/pkinit_crypto.h b/src/plugins/preauth/pkinit/pkinit_crypto.h
index f984462..dfb31c1 100644
--- a/src/plugins/preauth/pkinit/pkinit_crypto.h
+++ b/src/plugins/preauth/pkinit/pkinit_crypto.h
@@ -52,6 +52,57 @@ enum cms_msg_types {
};
/*
+ * 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 *);
@@ -362,8 +413,90 @@ krb5_error_code crypto_load_certs
pkinit_req_crypto_context req_cryptoctx, /* IN */
pkinit_identity_opts *idopts, /* IN */
pkinit_identity_crypto_context id_cryptoctx, /* IN/OUT */
- const char *principal, /* IN */
- krb5_get_init_creds_opt *opt); /* IN */
+ 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
@@ -383,11 +516,16 @@ krb5_error_code crypto_load_cas_and_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, pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context id_cryptoctx,
- const char *principal, krb5_get_init_creds_opt *opt);
+ (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
diff --git a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
index 8850454..b45104b 100644
--- a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
+++ b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
@@ -164,8 +164,6 @@ unsigned char pkinit_4096_dhprime[4096/8] = {
static int pkinit_oids_refs = 0;
-#define MAX_CREDS_ALLOWED 20
-
krb5_error_code
pkinit_init_plg_crypto(pkinit_plg_crypto_context *cryptoctx) {
@@ -434,7 +432,10 @@ 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;
@@ -3343,8 +3344,7 @@ 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,
- const char *principal,
- krb5_get_init_creds_opt *opt)
+ krb5_principal princ)
{
krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
@@ -3359,9 +3359,7 @@ pkinit_get_certs_pkcs12(krb5_context context,
pkinit_req_crypto_context req_cryptoctx,
pkinit_identity_opts *idopts,
pkinit_identity_crypto_context id_cryptoctx,
- const char *principal,
- krb5_get_init_creds_opt *opt,
- pkinit_cred_info *creds)
+ krb5_principal princ)
{
krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
X509 *x = NULL;
@@ -3438,14 +3436,16 @@ pkinit_get_certs_pkcs12(krb5_context context,
goto cleanup;
}
}
- creds[0] = malloc(sizeof(struct _pkinit_cred_info));
- if (creds[0] == NULL)
+ id_cryptoctx->creds[0] = malloc(sizeof(struct _pkinit_cred_info));
+ if (id_cryptoctx->creds[0] == NULL)
goto cleanup;
- creds[0]->cert = x;
- creds[0]->cert_id = NULL;
- creds[0]->cert_id_len = 0;
- creds[0]->key = y;
- creds[1] = NULL;
+ 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;
@@ -3462,18 +3462,60 @@ cleanup:
}
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 = ENOMEM;
+ X509 *x = NULL;
+ EVP_PKEY *y = NULL;
+
+ /* load the certificate */
+ x = get_cert(certname);
+ if (x == NULL) {
+ pkiDebug("failed to load user's certificate from '%s'\n", certname);
+ goto cleanup;
+ }
+ y = get_key(keyname);
+ if (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)
+ 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,
- const char *principal,
- krb5_get_init_creds_opt *opt,
- pkinit_cred_info *creds)
+ krb5_principal princ)
{
krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
- X509 *x = NULL;
- EVP_PKEY *y = NULL;
if (idopts->cert_filename == NULL) {
pkiDebug("%s: failed to get user's cert location\n", __FUNCTION__);
@@ -3481,43 +3523,101 @@ pkinit_get_certs_fs(krb5_context context,
}
if (idopts->key_filename == NULL) {
- pkiDebug("%s: failed to get user's private key location\n", __FUNCTION__);
+ pkiDebug("%s: failed to get user's private key location\n",
+ __FUNCTION__);
goto cleanup;
}
- /* load the certificate */
- if ((x = get_cert(idopts->cert_filename)) == NULL) {
- pkiDebug("failed to load user's certificate from '%s'\n",
- idopts->cert_filename);
- 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;
}
- else {
- /* add the private key */
- if ((y = get_key(idopts->key_filename)) == NULL) {
- pkiDebug("failed to load user's private key from '%s'\n",
- idopts->key_filename);
- goto cleanup;
+
+ 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;
}
- creds[0] = malloc(sizeof(struct _pkinit_cred_info));
- if (creds[0] == NULL)
+ if (i == 0) {
+ pkiDebug("%s: No cert/key pairs found in directory '%s'\n",
+ __FUNCTION__, idopts->cert_filename);
+ retval = ENOENT;
goto cleanup;
- creds[0]->cert = x;
- creds[0]->cert_id = NULL;
- creds[0]->cert_id_len = 0;
- creds[0]->key = y;
- creds[1] = NULL;
+ }
-retval = 0;
+ retval = 0;
+
+ cleanup:
+ if (d)
+ closedir(d);
-cleanup:
- if (retval) {
- if (x != NULL)
- X509_free(x);
- if (y != NULL)
- EVP_PKEY_free(y);
- }
return retval;
}
@@ -3528,9 +3628,7 @@ pkinit_get_certs_pkcs11(krb5_context context,
pkinit_req_crypto_context req_cryptoctx,
pkinit_identity_opts *idopts,
pkinit_identity_crypto_context id_cryptoctx,
- const char *principal,
- krb5_get_init_creds_opt *opt,
- pkinit_cred_info *creds)
+ krb5_principal princ)
{
#ifdef PKINIT_USE_MECH_LIST
CK_MECHANISM_TYPE_PTR mechp;
@@ -3547,10 +3645,6 @@ pkinit_get_certs_pkcs11(krb5_context context,
unsigned int nattrs;
X509 *x = NULL;
- if (principal == NULL) {
- return KRB5_PRINC_NOMATCH; /* XXX ??? */
- }
-
/* Copy stuff from idopts -> id_cryptoctx */
if (idopts->p11_module_name != NULL) {
id_cryptoctx->p11_module_name = strdup(idopts->p11_module_name);
@@ -3630,8 +3724,7 @@ pkinit_get_certs_pkcs11(krb5_context context,
}
free(mechp);
- pkiDebug("got %d mechs; reading certs for '%s' from card\n",
- (int) count, principal);
+ pkiDebug("got %d mechs from card\n", (int) count);
#endif
cls = CKO_CERTIFICATE;
@@ -3673,7 +3766,7 @@ pkinit_get_certs_pkcs11(krb5_context context,
/* Look for x.509 cert */
if ((r = id_cryptoctx->p11->C_FindObjects(id_cryptoctx->session,
&obj, 1, &count)) != CKR_OK || count <= 0) {
- creds[i] = NULL;
+ id_cryptoctx->creds[i] = NULL;
break;
}
@@ -3718,13 +3811,13 @@ pkinit_get_certs_pkcs11(krb5_context context,
x = d2i_X509(NULL, &cp, (int) attrs[0].ulValueLen);
if (x == NULL)
return KRB5KDC_ERR_PREAUTH_FAILED;
- creds[i] = malloc(sizeof(struct _pkinit_cred_info));
- if (creds[i] == NULL)
+ id_cryptoctx->creds[i] = malloc(sizeof(struct _pkinit_cred_info));
+ if (id_cryptoctx->creds[i] == NULL)
return KRB5KDC_ERR_PREAUTH_FAILED;
- creds[i]->cert = x;
- creds[i]->key = NULL;
- creds[i]->cert_id = cert_id;
- creds[i]->cert_id_len = attrs[1].ulValueLen;
+ 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);
@@ -3734,44 +3827,76 @@ pkinit_get_certs_pkcs11(krb5_context context,
}
#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]);
+ }
+ }
+ 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,
- const char *principal,
- krb5_get_init_creds_opt *opt)
+ krb5_principal princ)
{
- pkinit_cred_info *creds = NULL;
krb5_error_code retval;
- creds = calloc(MAX_CREDS_ALLOWED + 1, sizeof(*creds));
- if (creds == NULL) {
- retval = ENOMEM;
- goto cleanup;
- }
-
switch(idopts->idtype) {
case IDTYPE_FILE:
retval = pkinit_get_certs_fs(context, plg_cryptoctx,
req_cryptoctx, idopts,
- id_cryptoctx,
- principal, opt, creds);
+ 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,
- principal, opt, creds);
+ id_cryptoctx, princ);
break;
#endif
case IDTYPE_PKCS12:
retval = pkinit_get_certs_pkcs12(context, plg_cryptoctx,
req_cryptoctx, idopts,
- id_cryptoctx,
- principal, opt, creds);
+ id_cryptoctx, princ);
break;
default:
retval = EINVAL;
@@ -3779,68 +3904,434 @@ crypto_load_certs(krb5_context context,
if (retval)
goto cleanup;
- /* pick the right certificate/key from the retrieved certs */
- retval = pkinit_match_cert(context, plg_cryptoctx, req_cryptoctx,
- id_cryptoctx, principal, opt, creds);
cleanup:
- if (creds)
- free(creds);
return retval;
}
-/* XXX This needs to move above "the line"! */
+/*
+ * 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
-pkinit_match_cert(krb5_context context,
- pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context id_cryptoctx,
- const char *principal,
- krb5_get_init_creds_opt *opt,
- pkinit_cred_info *creds)
+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;
- krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
- int i, creds_index = 0;
+ if (ret_ku_bits == NULL && ret_eku_bits == NULL)
+ return EINVAL;
- /* add code to find the right certificate */
+ 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) {
+ /* XXX free existing cert stack! */
+ return 9999;
+ }
+ 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)
+{
+ /* copy the selected cert into our id_cryptoctx */
+ if (id_cryptoctx->my_certs != NULL) {
+ /* XXX free existing cert stack! */
+ return 9999;
+ }
id_cryptoctx->my_certs = sk_X509_new_null();
- sk_X509_push(id_cryptoctx->my_certs, creds[creds_index]->cert);
+ 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 = creds[creds_index]->key;
+ 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 = creds[creds_index]->cert_id;
- id_cryptoctx->cert_id_len = creds[creds_index]->cert_id_len;
+ 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
+ return 0;
+}
- /* free unused cred_infos XXX cred_fini() instead! */
- for (i = 0; ; i++) {
- if (creds[i] == NULL) break;
- else if (i == creds_index) {
- free (creds[i]);
- } else {
- if (creds[i]->cert != NULL)
- X509_free(creds[i]->cert);
- if (creds[i]->key != NULL)
- EVP_PKEY_free(creds[i]->key);
-#ifndef WITHOUT_PKCS11
- if (creds[i]->cert_id != NULL)
- free(creds[i]->cert_id);
-#endif
- free(creds[i]);
- }
- }
- retval = 0;
-cleanup:
- return retval;
-}
static krb5_error_code
load_cas_and_crls(krb5_context context,
@@ -4028,8 +4519,10 @@ load_cas_and_crls_dir(krb5_context context,
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);
diff --git a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.h b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.h
index 1bafc31..9976bf3 100644
--- a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.h
+++ b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.h
@@ -48,8 +48,20 @@
#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 */
@@ -95,13 +107,24 @@ struct _pkinit_req_crypto_context {
DH *dh;
};
-struct _pkinit_cred_info {
- X509 *cert;
- EVP_PKEY *key;
- CK_BYTE_PTR cert_id;
- int cert_id_len;
+#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;
};
-typedef struct _pkinit_cred_info * pkinit_cred_info;
static void openssl_init(void);
@@ -202,13 +225,6 @@ static krb5_error_code pkinit_decode_data_pkcs11
unsigned char **decoded_data, unsigned int *decoded_data_len);
#endif /* WITHOUT_PKCS11 */
-static krb5_error_code pkinit_match_cert
- (krb5_context context, pkinit_plg_crypto_context plg_cryptoctx,
- pkinit_req_crypto_context req_cryptoctx,
- pkinit_identity_crypto_context id_cryptoctx,
- const char *principal, krb5_get_init_creds_opt *opt,
- pkinit_cred_info *creds);
-
static krb5_error_code pkinit_sign_data_fs
(krb5_context context, pkinit_identity_crypto_context id_cryptoctx,
unsigned char *data, unsigned int data_len,
diff --git a/src/plugins/preauth/pkinit/pkinit_identity.c b/src/plugins/preauth/pkinit/pkinit_identity.c
index 7bae5f7..267e858 100644
--- a/src/plugins/preauth/pkinit/pkinit_identity.c
+++ b/src/plugins/preauth/pkinit/pkinit_identity.c
@@ -165,6 +165,7 @@ pkinit_dup_identity_opts(pkinit_identity_opts *src_opts,
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)
@@ -190,6 +191,7 @@ pkinit_dup_identity_opts(pkinit_identity_opts *src_opts,
if (newopts->cert_label == NULL)
goto cleanup;
}
+#endif
*dest_opts = newopts;
@@ -365,6 +367,8 @@ cleanup:
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)
@@ -412,8 +416,8 @@ process_option_identity(krb5_context context,
pkiDebug("%s: idtype is %d\n", __FUNCTION__, idopts->idtype);
switch (idtype) {
case IDTYPE_ENVVAR:
- return process_option_identity(context, idopts, id_cryptoctx,
- getenv(residual));
+ 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);
@@ -427,8 +431,9 @@ process_option_identity(krb5_context context,
break;
#endif
case IDTYPE_DIR:
- pkiDebug("DIR: not supported for user_identity '%s'\n", value);
- retval = ENOTSUP;
+ idopts->cert_filename = strdup(residual);
+ if (idopts->cert_filename == NULL)
+ retval = ENOMEM;
break;
default:
krb5_set_error_message(context, KRB5_PREAUTH_FAILED,
@@ -436,18 +441,13 @@ process_option_identity(krb5_context context,
retval = EINVAL;
break;
}
- retval = crypto_load_certs(context,
- NULL, /* XXX plg_cryptoctx */
- NULL, /* XXX req_cryptoctx */
- idopts,
- id_cryptoctx,
- "<not-supplied>",/* XXX need principal */
- NULL /* XXX need gic opts */);
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,
@@ -474,14 +474,16 @@ process_option_ca_crl(krb5_context context,
return ENOTSUP;
}
return crypto_load_cas_and_crls(context,
- NULL, /* XXX plg_cryptoctx */
- NULL, /* XXX req_cryptoctx */
+ 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,
@@ -491,19 +493,26 @@ pkinit_identity_process_option(krb5_context context,
switch (attr) {
case PKINIT_ID_OPT_USER_IDENTITY:
- retval = process_option_identity(context, idopts, id_cryptoctx,
- value);
+ 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, idopts, id_cryptoctx,
- value, CATYPE_ANCHORS);
+ 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, idopts, id_cryptoctx,
+ 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, idopts, id_cryptoctx,
+ retval = process_option_ca_crl(context, plg_cryptoctx,
+ req_cryptoctx, idopts,
+ id_cryptoctx,
value, CATYPE_CRLS);
break;
case PKINIT_ID_OPT_OCSP:
@@ -518,8 +527,12 @@ pkinit_identity_process_option(krb5_context context,
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)
+ pkinit_identity_crypto_context id_cryptoctx,
+ int do_matching,
+ krb5_principal princ)
{
krb5_error_code retval = EINVAL;
int i;
@@ -537,12 +550,15 @@ pkinit_identity_initialize(krb5_context context,
* in the config file.
*/
if (idopts->identity != NULL) {
- retval = pkinit_identity_process_option(context, idopts, id_cryptoctx,
+ 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, idopts,
+ retval = pkinit_identity_process_option(context, plg_cryptoctx,
+ req_cryptoctx, idopts,
id_cryptoctx,
PKINIT_ID_OPT_USER_IDENTITY,
idopts->identity_alt[i]);
@@ -553,8 +569,42 @@ pkinit_identity_initialize(krb5_context context,
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, idopts, id_cryptoctx,
+ retval = pkinit_identity_process_option(context, plg_cryptoctx,
+ req_cryptoctx, idopts,
+ id_cryptoctx,
PKINIT_ID_OPT_ANCHOR_CAS,
idopts->anchors[i]);
if (retval)
@@ -562,21 +612,27 @@ pkinit_identity_initialize(krb5_context context,
}
for (i = 0; idopts->intermediates != NULL
&& idopts->intermediates[i] != NULL; i++) {
- retval = pkinit_identity_process_option(context, idopts, id_cryptoctx,
+ 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, idopts, id_cryptoctx,
+ 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, idopts, id_cryptoctx,
+ retval = pkinit_identity_process_option(context, plg_cryptoctx,
+ req_cryptoctx, idopts,
+ id_cryptoctx,
PKINIT_ID_OPT_OCSP,
idopts->ocsp);
if (retval)
diff --git a/src/plugins/preauth/pkinit/pkinit_matching.c b/src/plugins/preauth/pkinit/pkinit_matching.c
new file mode 100644
index 0000000..ebd57f5
--- /dev/null
+++ b/src/plugins/preauth/pkinit/pkinit_matching.c
@@ -0,0 +1,827 @@
+/*
+ * 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;
+ 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, *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:
+ 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;
+
+ 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;
+
+ /* 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) {
+ fprintf(stderr, "Ignoring invalid rule pkinit_cert_match = '%s'\n", 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_srv.c b/src/plugins/preauth/pkinit/pkinit_srv.c
index 2554b73..63f47a6 100644
--- a/src/plugins/preauth/pkinit/pkinit_srv.c
+++ b/src/plugins/preauth/pkinit/pkinit_srv.c
@@ -657,6 +657,8 @@ pkinit_server_return_padata(krb5_context context,
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;
@@ -814,49 +816,66 @@ pkinit_server_return_padata(krb5_context context,
goto cleanup;
}
- switch ((int)padata->pa_type) {
- case KRB5_PADATA_PK_AS_REQ:
- 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;
+ /* 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
+ /* XXX The asn1 encoding from Apple seems to be incorrect? */
+ || request->padata[i]->pa_type == 0xffffff84)
+ 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],
+ /* 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;
- }
+ 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);
+ 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);
+ 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;
- }
+ 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,
@@ -866,21 +885,26 @@ pkinit_server_return_padata(krb5_context context,
break;
case KRB5_PADATA_PK_AS_REP_OLD:
case KRB5_PADATA_PK_AS_REQ_OLD:
- 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,
+ /* 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;
- }
+ 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,
@@ -982,7 +1006,10 @@ pkinit_server_return_padata(krb5_context context,
case KRB5_PADATA_PK_AS_REQ_OLD:
free_krb5_pa_pk_as_req_draft9(&reqp9);
free_krb5_pa_pk_as_rep_draft9(&rep9);
- free_krb5_reply_key_pack_draft9(&key_pack9);
+ if (!fixed_keypack)
+ free_krb5_reply_key_pack_draft9(&key_pack9);
+ else
+ free_krb5_reply_key_pack(&key_pack);
break;
}
@@ -1162,7 +1189,8 @@ pkinit_server_plugin_init_realm(krb5_context context, const char *realmname,
if (retval)
goto errout;
- retval = pkinit_identity_initialize(context, plgctx->idopts, plgctx->idctx);
+ retval = pkinit_identity_initialize(context, plgctx->cryptoctx, NULL,
+ plgctx->idopts, plgctx->idctx, 0, NULL);
if (retval)
goto errout;