aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Coffman <kwc@citi.umich.edu>2006-11-22 16:58:01 +0000
committerKevin Coffman <kwc@citi.umich.edu>2006-11-22 16:58:01 +0000
commitb9075d633057c81eaf50e090ca502bedf268ce16 (patch)
tree28e5d2940a52bd24343ea6761c7a8478d039d432
parent652a4ec2ce33105fc2c7591594818ff8b376a89c (diff)
downloadkrb5-b9075d633057c81eaf50e090ca502bedf268ce16.zip
krb5-b9075d633057c81eaf50e090ca502bedf268ce16.tar.gz
krb5-b9075d633057c81eaf50e090ca502bedf268ce16.tar.bz2
Initial commit of the pkinit code
git-svn-id: svn://anonsvn.mit.edu/krb5/users/coffman/pkinit@18861 dc483132-0cff-0310-8789-dd5450dbe970
-rw-r--r--src/Makefile.in4
-rw-r--r--src/configure.in2
-rw-r--r--src/include/k5-int-pkinit.h248
-rw-r--r--src/include/k5-int.h24
-rw-r--r--src/include/krb5/krb5.hin6
-rw-r--r--src/lib/krb5/asn.1/asn1_k_decode.c646
-rw-r--r--src/lib/krb5/asn.1/asn1_k_decode.h40
-rw-r--r--src/lib/krb5/asn.1/asn1_k_encode.c390
-rw-r--r--src/lib/krb5/asn.1/asn1_k_encode.h61
-rw-r--r--src/lib/krb5/asn.1/krb5_decode.c110
-rw-r--r--src/lib/krb5/asn.1/krb5_encode.c91
-rw-r--r--src/lib/krb5/libkrb5.exports19
-rw-r--r--src/plugins/preauth/pkinit/Makefile.in63
-rw-r--r--src/plugins/preauth/pkinit/README52
-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/pkinit.exports2
-rw-r--r--src/plugins/preauth/pkinit/pkinit.h142
-rw-r--r--src/plugins/preauth/pkinit/pkinit_clnt.c1684
-rw-r--r--src/plugins/preauth/pkinit/pkinit_lib.c1776
-rw-r--r--src/plugins/preauth/pkinit/pkinit_srv.c1019
21 files changed, 6414 insertions, 2 deletions
diff --git a/src/Makefile.in b/src/Makefile.in
index 94829d0..150acc3 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -6,6 +6,7 @@ mydir=.
# Don't build sample by default: plugins/locate/python plugins/preauth/wpse plugins/preauth/cksum_body
SUBDIRS=util include lib @krb524@ kdc kadmin @ldap_plugin_dir@ slave clients \
plugins/kdb/db2 \
+ plugins/preauth/pkinit \
appl tests \
config-files gen-manpages
BUILDTOP=$(REL)$(C)
@@ -67,7 +68,7 @@ INSTALLMKDIRS = $(KRB5ROOT) $(KRB5MANROOT) $(KRB5OTHERMKDIRS) \
$(ADMIN_BINDIR) $(SERVER_BINDIR) $(CLIENT_BINDIR) \
$(ADMIN_MANDIR) $(SERVER_MANDIR) $(CLIENT_MANDIR) \
$(FILE_MANDIR) $(KRB5_LIBDIR) $(KRB5_INCDIR) \
- $(KRB5_DB_MODULE_DIR) \
+ $(KRB5_DB_MODULE_DIR) $(KRB5_PA_MODULE_DIR) \
$(KRB5_LIBKRB5_MODULE_DIR) \
$(KRB5_INCSUBDIRS) $(datadir) $(EXAMPLEDIR)
@@ -99,6 +100,7 @@ fake-install:
(w=`pwd`; cd util && $(MAKE) install DESTDIR="$$w/util/fakedest")
(w=`pwd`; cd lib && $(MAKE) install DESTDIR="$$w/util/fakedest")
(w=`pwd`; cd plugins/kdb/db2 && $(MAKE) install DESTDIR="$$w/util/fakedest")
+ (w=`pwd`; cd plugins/preauth/pkinit && $(MAKE) install DESTDIR="$$w/util/fakedest")
# (w=`pwd`; cd plugins/locate/python && $(MAKE) install DESTDIR="$$w/util/fakedest")
diff --git a/src/configure.in b/src/configure.in
index 8bd8170..a397c56 100644
--- a/src/configure.in
+++ b/src/configure.in
@@ -920,7 +920,7 @@ if test -n "$OPENLDAP_PLUGIN"; then
fi
AC_SUBST(ldap_plugin_dir)
-AC_CONFIG_SUBDIRS(lib/apputils plugins/kdb/db2 plugins/preauth/wpse plugins/preauth/cksum_body appl tests)
+AC_CONFIG_SUBDIRS(lib/apputils plugins/kdb/db2 plugins/preauth/wpse plugins/preauth/cksum_body plugins/preauth/pkinit appl tests)
dnl
if false; then
AC_CHECK_HEADERS(Python.h python2.3/Python.h)
diff --git a/src/include/k5-int-pkinit.h b/src/include/k5-int-pkinit.h
new file mode 100644
index 0000000..d65712c
--- /dev/null
+++ b/src/include/k5-int-pkinit.h
@@ -0,0 +1,248 @@
+/*
+ * COPYRIGHT (C) 2006
+ * 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 _KRB5_INT_PKINIT_H
+#define _KRB5_INT_PKINIT_H
+
+/*
+ * pkinit structures
+ */
+
+/* PKAuthenticator */
+typedef struct _krb5_pk_authenticator {
+ krb5_int32 cusec; /* (0..999999) */
+ krb5_timestamp ctime;
+ krb5_int32 nonce; /* (0..4294967295) */
+ krb5_checksum paChecksum;
+} krb5_pk_authenticator;
+
+/* PKAuthenticator draft9 */
+typedef struct _krb5_pk_authenticator_draft9 {
+ krb5_principal kdcName;
+ krb5_octet_data kdcRealm;
+ krb5_int32 cusec; /* (0..999999) */
+ krb5_timestamp ctime;
+ krb5_int32 nonce; /* (0..4294967295) */
+} krb5_pk_authenticator_draft9;
+
+/* AlgorithmIdentifier */
+typedef struct _krb5_algorithm_identifier {
+ krb5_octet_data algorithm; /* OID */
+ krb5_octet_data parameters; /* Optional */
+} krb5_algorithm_identifier;
+
+/* SubjectPublicKeyInfo */
+typedef struct _krb5_subject_pk_info {
+ krb5_algorithm_identifier algorithm;
+ krb5_octet_data subjectPublicKey; /* BIT STRING */
+} krb5_subject_pk_info;
+
+/* AuthPack */
+typedef struct _krb5_auth_pack {
+ krb5_pk_authenticator pkAuthenticator;
+ krb5_subject_pk_info *clientPublicValue; /* Optional */
+ krb5_algorithm_identifier **supportedCMSTypes; /* Optional */
+ krb5_octet_data clientDHNonce; /* Optional */
+} krb5_auth_pack;
+
+/* AuthPack draft9 */
+typedef struct _krb5_auth_pack_draft9 {
+ krb5_pk_authenticator_draft9 pkAuthenticator;
+ krb5_subject_pk_info *clientPublicValue; /* Optional */
+} krb5_auth_pack_draft9;
+
+/* ExternalPrincipalIdentifier */
+typedef struct _krb5_external_principal_identifier {
+ krb5_octet_data subjectName; /* Optional */
+ krb5_octet_data issuerAndSerialNumber; /* Optional */
+ krb5_octet_data subjectKeyIdentifier; /* Optional */
+} krb5_external_principal_identifier;
+
+/* TrustedCas */
+typedef struct _krb5_trusted_ca {
+ enum {
+ choice_trusted_cas_UNKNOWN = -1,
+ choice_trusted_cas_principalName = 0,
+ choice_trusted_cas_caName = 1,
+ choice_trusted_cas_issuerAndSerial = 2,
+ } choice;
+ union {
+ krb5_principal principalName;
+ krb5_octet_data caName; /* fully-qualified X.500 "Name" as defined by X.509 (der-encoded) */
+ krb5_octet_data issuerAndSerial; /* Optional -- IssuerAndSerialNumber (der-encoded) */
+ } u;
+} krb5_trusted_ca;
+
+/* PA-PK-AS-REQ (Draft 9 -- PA TYPE 14) */
+typedef struct _krb5_pa_pk_as_req_draft9 {
+ krb5_octet_data signedAuthPack;
+ krb5_trusted_ca **trustedCertifiers; /* Optional array */
+ krb5_octet_data kdcCert; /* Optional */
+ krb5_octet_data encryptionCert;
+} krb5_pa_pk_as_req_draft9;
+
+/* PA-PK-AS-REQ (rfc4556 -- PA TYPE 16) */
+typedef struct _krb5_pa_pk_as_req {
+ krb5_octet_data signedAuthPack;
+ krb5_external_principal_identifier **trustedCertifiers; /* Optional array */
+ krb5_octet_data kdcPkId; /* Optional */
+} krb5_pa_pk_as_req;
+
+/* DHRepInfo */
+typedef struct _krb5_dh_rep_info {
+ krb5_octet_data dhSignedData;
+ krb5_octet_data serverDHNonce; /* Optional */
+} krb5_dh_rep_info;
+
+/* KDCDHKeyInfo */
+typedef struct _krb5_kdc_dh_key_info {
+ krb5_octet_data subjectPublicKey; /* BIT STRING */
+ krb5_int32 nonce; /* (0..4294967295) */
+ krb5_timestamp dhKeyExpiration; /* Optional */
+} krb5_kdc_dh_key_info;
+
+/* KDCDHKeyInfo draft9*/
+typedef struct _krb5_kdc_dh_key_info_draft9 {
+ krb5_octet_data subjectPublicKey; /* BIT STRING */
+ krb5_int32 nonce; /* (0..4294967295) */
+} krb5_kdc_dh_key_info_draft9;
+
+/* ReplyKeyPack */
+typedef struct _krb5_reply_key_pack {
+ krb5_keyblock replyKey;
+ krb5_checksum asChecksum;
+} krb5_reply_key_pack;
+
+/* ReplyKeyPack */
+typedef struct _krb5_reply_key_pack_draft9 {
+ krb5_keyblock replyKey;
+ krb5_int32 nonce;
+} krb5_reply_key_pack_draft9;
+
+/* PA-PK-AS-REP (Draft 9 -- PA TYPE 15) */
+typedef struct _krb5_pa_pk_as_rep_draft9 {
+ enum {
+ choice_pa_pk_as_rep_draft9_UNKNOWN = -1,
+ choice_pa_pk_as_rep_draft9_dhSignedData = 0,
+ choice_pa_pk_as_rep_draft9_encKeyPack = 1,
+ } choice;
+ union {
+ krb5_octet_data dhSignedData;
+ krb5_octet_data encKeyPack;
+ } u;
+} krb5_pa_pk_as_rep_draft9;
+
+/* PA-PK-AS-REP (rfc4556 -- PA TYPE 17) */
+typedef struct _krb5_pa_pk_as_rep {
+ enum {
+ choice_pa_pk_as_rep_UNKNOWN = -1,
+ choice_pa_pk_as_rep_dhInfo = 0,
+ choice_pa_pk_as_rep_encKeyPack = 1,
+ } choice;
+ union {
+ krb5_dh_rep_info dh_Info;
+ krb5_octet_data encKeyPack;
+ } u;
+} krb5_pa_pk_as_rep;
+
+/*
+ * Begin "asn1.h"
+ */
+
+/*************************************************************************
+ * Prototypes for pkinit asn.1 encode routines
+ *************************************************************************/
+
+krb5_error_code encode_krb5_pa_pk_as_req
+ (const krb5_pa_pk_as_req *rep, krb5_data **code);
+
+krb5_error_code encode_krb5_pa_pk_as_req_draft9
+ (const krb5_pa_pk_as_req_draft9 *rep, krb5_data **code);
+
+krb5_error_code encode_krb5_pa_pk_as_rep
+ (const krb5_pa_pk_as_rep *rep, krb5_data **code);
+
+krb5_error_code encode_krb5_pa_pk_as_rep_draft9
+ (const krb5_pa_pk_as_rep_draft9 *rep, krb5_data **code);
+
+krb5_error_code encode_krb5_auth_pack
+ (const krb5_auth_pack *rep, krb5_data **code);
+
+krb5_error_code encode_krb5_auth_pack_draft9
+ (const krb5_auth_pack_draft9 *rep, krb5_data **code);
+
+krb5_error_code encode_krb5_kdc_dh_key_info
+ (const krb5_kdc_dh_key_info *rep, krb5_data **code);
+
+krb5_error_code encode_krb5_reply_key_pack
+ (const krb5_reply_key_pack *, krb5_data **code);
+
+krb5_error_code encode_krb5_reply_key_pack_draft9
+ (const krb5_reply_key_pack_draft9 *, krb5_data **code);
+
+krb5_error_code encode_krb5_td_trusted_certifiers
+ (const krb5_external_principal_identifier **, krb5_data **code);
+
+/*************************************************************************
+ * Prototypes for pkinit asn.1 decode routines
+ *************************************************************************/
+
+krb5_error_code decode_krb5_pa_pk_as_req
+ (const krb5_data *, krb5_pa_pk_as_req **);
+
+krb5_error_code decode_krb5_pa_pk_as_req_draft9
+ (const krb5_data *, krb5_pa_pk_as_req_draft9 **);
+
+krb5_error_code decode_krb5_pa_pk_as_rep
+ (const krb5_data *, krb5_pa_pk_as_rep **);
+
+krb5_error_code decode_krb5_pa_pk_as_rep_draft9
+ (const krb5_data *, krb5_pa_pk_as_rep_draft9 **);
+
+krb5_error_code decode_krb5_auth_pack
+ (const krb5_data *, krb5_auth_pack **);
+
+krb5_error_code decode_krb5_auth_pack_draft9
+ (const krb5_data *, krb5_auth_pack_draft9 **);
+
+krb5_error_code decode_krb5_kdc_dh_key_info
+ (const krb5_data *, krb5_kdc_dh_key_info **);
+
+krb5_error_code decode_krb5_principal_name
+ (const krb5_data *, krb5_principal_data **);
+
+krb5_error_code decode_krb5_reply_key_pack
+ (const krb5_data *, krb5_reply_key_pack **);
+
+krb5_error_code decode_krb5_reply_key_pack_draft9
+ (const krb5_data *, krb5_reply_key_pack_draft9 **);
+
+
+#endif /* _KRB5_INT_PKINIT_H */
diff --git a/src/include/k5-int.h b/src/include/k5-int.h
index 6568ab1..d8787b6 100644
--- a/src/include/k5-int.h
+++ b/src/include/k5-int.h
@@ -248,6 +248,23 @@ typedef INT64_TYPE krb5_int64;
/* in e-text) */
#define KRB_ERR_FIELD_TOOLONG 61 /* Field is too long for impl. */
+/* PKINIT server-reported errors */
+#define KDC_ERR_CLIENT_NOT_TRUSTED 62 /* client cert not trusted */
+#define KDC_ERR_INVALID_SIG 64 /* client signature verify failed */
+#define KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED 65 /* invalid Diffie-Hellman parameters */
+#define KDC_ERR_CANT_VERIFY_CERTIFICATE 70 /* client cert not verifiable to */
+ /* trusted root cert */
+#define KDC_ERR_INVALID_CERTIFICATE 71 /* client cert had invalid signature */
+#define KDC_ERR_REVOKED_CERTIFICATE 72 /* client cert was revoked */
+#define KDC_ERR_REVOCATION_STATUS_UNKNOWN 73 /* client cert revoked, reason unknown */
+#define KDC_ERR_CLIENT_NAME_MISMATCH 75 /* mismatch between client cert and */
+ /* principal name */
+#define KDC_ERR_INCONSISTENT_KEY_PURPOSE 77 /* bad extended key use */
+#define KDC_ERR_DIGEST_IN_CERT_NOT_ACCEPTED 78 /* bad digest algorithm in client cert */
+#define KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED 79 /* missing paChecksum in PA-PK-AS-REQ */
+#define KDC_ERR_DIGEST_IN_SIGNED_DATA_NOT_ACCEPTED 80 /* bad digest algorithm in SignedData */
+#define KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED 81
+
#endif /* KRB5_ERRORS__ */
/*
* End "k5-errors.h"
@@ -413,6 +430,13 @@ typedef struct _krb5_enc_sam_response_enc_2 {
} krb5_enc_sam_response_enc_2;
/*
+ * Keep the pkinit definitions in a separate file so that the plugin
+ * only has to include k5-int-pkinit.h rather than k5-int.h
+ */
+
+#include "k5-int-pkinit.h"
+
+/*
* Begin "ext-proto.h"
*/
#ifndef KRB5_EXT_PROTO__
diff --git a/src/include/krb5/krb5.hin b/src/include/krb5/krb5.hin
index 9d9920e..424325f 100644
--- a/src/include/krb5/krb5.hin
+++ b/src/include/krb5/krb5.hin
@@ -205,6 +205,12 @@ typedef struct _krb5_data {
char *data;
} krb5_data;
+typedef struct _krb5_octet_data {
+ krb5_magic magic;
+ unsigned int length;
+ krb5_octet *data;
+} krb5_octet_data;
+
/*
* Hack length for crypto library to use the afs_string_to_key It is
* equivalent to -1 without possible sign extension
diff --git a/src/lib/krb5/asn.1/asn1_k_decode.c b/src/lib/krb5/asn.1/asn1_k_decode.c
index 3ffb701..507851b 100644
--- a/src/lib/krb5/asn.1/asn1_k_decode.c
+++ b/src/lib/krb5/asn.1/asn1_k_decode.c
@@ -176,6 +176,15 @@
if (retval) return retval; \
next_tag()
+#define begin_structure_no_tag() \
+ asn1buf subbuf; \
+ int seqindef; \
+ int indef; \
+ retval = asn1_get_sequence(buf, &length, &seqindef); \
+ if (retval) return retval; \
+ retval = asn1buf_imbed(&subbuf, buf, length, seqindef); \
+ if (retval) return retval; \
+
/* skip trailing garbage */
#define end_structure() \
retval = asn1buf_sync(buf, &subbuf, asn1class, tagnum, \
@@ -183,6 +192,93 @@
if (retval) return retval
/*
+ * begin_choice
+ *
+ * Declares some variables for decoding CHOICE types. This is meant
+ * to be called in an inner block that ends with a call to
+ * end_choice().
+ */
+#define begin_choice() \
+ asn1buf subbuf; \
+ int seqindef; \
+ int indef; \
+ taginfo t; \
+ retval = asn1_get_tag_2(buf, &t); \
+ if(t.asn1class != CONTEXT_SPECIFIC || \
+ t.construction != CONSTRUCTED) \
+ return ASN1_BAD_ID; \
+ length = t.length; \
+ seqindef = t.indef; \
+ retval = asn1buf_imbed(&subbuf, buf, length, seqindef); \
+ if (retval) return retval
+
+/* skip trailing garbage */
+#define end_choice() \
+ retval = asn1buf_sync(buf, &subbuf, t.asn1class, t.tagnum, \
+ length, t.indef, seqindef); \
+ if (retval) return retval
+
+#define begin_explicit_choice() \
+ asn1buf subbuf; \
+ int seqindef; \
+ int indef; \
+ taginfo t; \
+ if (buf == NULL || buf->base == NULL || \
+ buf->bound - buf->next + 1 <= 0) { \
+ t.tagnum = ASN1_TAGNUM_CEILING; \
+ t.asn1class = UNIVERSAL; \
+ t.construction = PRIMITIVE; \
+ t.length = 0; \
+ t.indef = 0; \
+ retval = 0; \
+ } else { \
+ asn1_tagnum tn=0; \
+ asn1_octet o; \
+ retval = asn1buf_remove_octet(buf,&o); \
+ if (retval) return ASN1_BAD_ID; \
+ t.asn1class = (asn1_class)(o&0xC0); \
+ t.construction = (asn1_construction)(o&0x20); \
+ if ((o&0x1F) != 0x1F) { \
+ t.tagnum = (asn1_tagnum)(o&0x1F); \
+ } else { \
+ do { \
+ retval = asn1buf_remove_octet(buf,&o); \
+ if (retval) return ASN1_BAD_ID; \
+ tn = (tn<<7) + (asn1_tagnum)(o&0x7F); \
+ } while(o&0x80); \
+ t.tagnum = tn; \
+ } \
+ t.indef = 0; \
+ retval = asn1buf_remove_octet(buf,&o); \
+ if(retval) return ASN1_BAD_ID; \
+ if ((o&0x80) == 0) { \
+ t.length = (int)(o&0x7F); \
+ } else { \
+ int num, len = 0; \
+ for (num = (int)(o&0x7F); num>0; num--) { \
+ retval = asn1buf_remove_octet(buf,&o); \
+ if(retval) return ASN1_BAD_ID; \
+ len = (len<<8) + (int)o; \
+ } \
+ if (len < 0) return ASN1_BAD_ID; \
+ if (!len) t.indef = 1; \
+ t.length = len; \
+ } \
+ } \
+ tagnum = t.tagnum; \
+ taglen = t.length; \
+ indef = t.indef; \
+ length = t.length; \
+ seqindef = t.indef; \
+ retval = asn1buf_imbed(&subbuf, buf, length, seqindef); \
+ if (retval) return retval
+
+/* skip trailing garbage */
+#define end_explicit_choice() \
+ retval = asn1buf_sync(buf, &subbuf, t.asn1class, t.tagnum, \
+ length, t.indef, seqindef); \
+ if (retval) return retval
+/*
* sequence_of
*
* Declares some variables for decoding SEQUENCE OF types. This is
@@ -1092,3 +1188,553 @@ asn1_error_code asn1_decode_predicted_sam_response(asn1buf *buf, krb5_predicted_
}
cleanup();
}
+
+/* PKINIT */
+
+/*
+ * The implicit tagging of the members of this sequence require special processing
+ */
+asn1_error_code asn1_decode_external_principal_identifier(asn1buf *buf, krb5_external_principal_identifier *val)
+{
+ setup();
+ {
+ char *start, *end;
+ size_t alloclen;
+ begin_structure();
+ val->subjectName.data = NULL;
+ val->subjectName.length = 0;
+ val->issuerAndSerialNumber.data = NULL;
+ val->issuerAndSerialNumber.length = 0;
+ val->subjectKeyIdentifier.data = NULL;
+ val->subjectKeyIdentifier.length = 0;
+ if (tagnum == 0) {
+ if (asn1class != CONTEXT_SPECIFIC || construction != PRIMITIVE)
+ return ASN1_BAD_ID;
+ start = subbuf.next;
+ sequence_of_no_tagvars(&subbuf);
+ end_sequence_of_no_tagvars(&subbuf);
+ end = subbuf.next;
+ alloclen = end - start;
+ val->subjectName.data = malloc(alloclen);
+ if (val->subjectName.data == NULL)
+ return ENOMEM;
+ val->subjectName.length = alloclen;
+ memcpy(val->subjectName.data, start, alloclen);
+ next_tag();
+ }
+ if (tagnum == 1) {
+ if (asn1class != CONTEXT_SPECIFIC || construction != PRIMITIVE)
+ return ASN1_BAD_ID;
+ start = subbuf.next;
+ sequence_of_no_tagvars(&subbuf);
+ end_sequence_of_no_tagvars(&subbuf);
+ end = subbuf.next;
+ alloclen = end - start;
+ val->issuerAndSerialNumber.data = malloc(alloclen);
+ if (val->issuerAndSerialNumber.data == NULL)
+ return ENOMEM;
+ val->issuerAndSerialNumber.length = alloclen;
+ memcpy(val->issuerAndSerialNumber.data, start, alloclen);
+ next_tag();
+ }
+ if (tagnum == 2) {
+ if (asn1class != CONTEXT_SPECIFIC || construction != PRIMITIVE)
+ return ASN1_BAD_ID;
+ start = subbuf.next;
+ val->subjectKeyIdentifier.data = malloc(taglen);
+ if (val->subjectKeyIdentifier.data == NULL)
+ return ENOMEM;
+ val->subjectKeyIdentifier.length = taglen;
+ memcpy(val->subjectKeyIdentifier.data, start, taglen);
+ /* next_tag(); XXX ??? */
+ }
+ /*
+ opt_lenfield(val->subjectName.length, val->subjectName.data, 0, asn1_decode_octetstring);
+ opt_lenfield(val->issuerAndSerialNumber.length, val->issuerAndSerialNumber.data, 1, asn1_decode_octetstring);
+ opt_lenfield(val->subjectKeyIdentifier.length, val->subjectKeyIdentifier.data, 2, asn1_decode_octetstring);
+ */
+ end_structure();
+ }
+ cleanup();
+}
+
+asn1_error_code asn1_decode_sequence_of_external_principal_identifier(asn1buf *buf, krb5_external_principal_identifier ***val)
+{
+ decode_array_body(krb5_external_principal_identifier,asn1_decode_external_principal_identifier);
+}
+
+/*
+ * The implicit tagging of the members of this sequence require special processing
+ */
+asn1_error_code asn1_decode_pa_pk_as_req(asn1buf *buf, krb5_pa_pk_as_req *val)
+{
+ setup();
+ {
+ char *start, *end;
+ size_t alloclen;
+ begin_structure();
+ if (tagnum != 0) return ASN1_MISSING_FIELD;
+ if (asn1class != CONTEXT_SPECIFIC || construction != PRIMITIVE)
+ return ASN1_BAD_ID;
+ start = subbuf.next;
+ {
+ sequence_of_no_tagvars(&subbuf);
+ unused_var(size);
+ end_sequence_of_no_tagvars(&subbuf);
+ }
+ end = subbuf.next;
+ alloclen = end - start;
+ val->signedAuthPack.data = malloc(alloclen);
+ if (val->signedAuthPack.data == NULL)
+ return ENOMEM;
+ val->signedAuthPack.length = alloclen;
+ memcpy(val->signedAuthPack.data, start, alloclen);
+ next_tag();
+
+ opt_field(val->trustedCertifiers, 1, asn1_decode_sequence_of_external_principal_identifier, NULL);
+ if (tagnum == 2) {
+ start = subbuf.next;
+ {
+ sequence_of_no_tagvars(&subbuf);
+ unused_var(size);
+ end_sequence_of_no_tagvars(&subbuf);
+ }
+ end = subbuf.next;
+ alloclen = end - start;
+ val->kdcPkId.data = malloc(alloclen);
+ if (val->kdcPkId.data == NULL)
+ return ENOMEM;
+ val->kdcPkId.length = alloclen;
+ memcpy(val->kdcPkId.data, start, alloclen);
+#if 0
+ get_lenfield_body(val->kdcPkId.length, val->kdcPkId.data, asn1_decode_octetstring);
+#endif
+ } else { val->kdcPkId.length = 0; val->kdcPkId.data = 0; }
+
+ end_structure();
+ }
+ cleanup();
+}
+
+#if 1
+asn1_error_code asn1_decode_trusted_ca(asn1buf *buf, krb5_trusted_ca *val)
+{
+ setup();
+ {
+ char *start, *end;
+ size_t alloclen;
+
+ begin_explicit_choice();
+ if (t.tagnum == choice_trusted_cas_principalName) {
+ val->choice = choice_trusted_cas_principalName;
+ } else if (t.tagnum == choice_trusted_cas_caName) {
+ val->choice = choice_trusted_cas_caName;
+ start = subbuf.next;
+ {
+ sequence_of_no_tagvars(&subbuf);
+ unused_var(size);
+ end_sequence_of_no_tagvars(&subbuf);
+ }
+ end = subbuf.next;
+ alloclen = end - start;
+ val->u.caName.data = malloc(alloclen);
+ if (val->u.caName.data == NULL)
+ return ENOMEM;
+ memcpy(val->u.caName.data, start, alloclen);
+ val->u.caName.length = alloclen;
+ next_tag();
+ } else if (t.tagnum == choice_trusted_cas_issuerAndSerial) {
+ val->choice = choice_trusted_cas_issuerAndSerial;
+ start = subbuf.next;
+ {
+ sequence_of_no_tagvars(&subbuf);
+ unused_var(size);
+ end_sequence_of_no_tagvars(&subbuf);
+ }
+ end = subbuf.next;
+ alloclen = end - start;
+ val->u.issuerAndSerial.data = malloc(alloclen);
+ if (val->u.issuerAndSerial.data == NULL)
+ return ENOMEM;
+ memcpy(val->u.issuerAndSerial.data, start, alloclen);
+ val->u.issuerAndSerial.length = alloclen;
+ next_tag();
+ } else return ASN1_BAD_ID;
+ end_explicit_choice();
+ }
+ cleanup();
+}
+#else
+asn1_error_code asn1_decode_trusted_ca(asn1buf *buf, krb5_trusted_ca *val)
+{
+ setup();
+ { begin_structure();
+ get_field(val->u.principalName, 0, asn1_decode_realm);
+ get_field(val->u.principalName, 1, asn1_decode_principal_name);
+ opt_lenfield(val->u.caName.length, val->u.caName.data, 2, asn1_decode_octetstring); /* XXX This is likely wrong! */
+ opt_lenfield(val->u.issuerAndSerial.length, val->u.issuerAndSerial.data, 3, asn1_decode_octetstring); /* XXX This is likely wrong! */
+ end_structure();
+ }
+ cleanup();
+}
+#endif
+asn1_error_code asn1_decode_sequence_of_trusted_ca(asn1buf *buf, krb5_trusted_ca ***val)
+{
+ decode_array_body(krb5_trusted_ca, asn1_decode_trusted_ca);
+}
+
+asn1_error_code asn1_decode_pa_pk_as_req_draft9(asn1buf *buf, krb5_pa_pk_as_req_draft9 *val)
+{
+ char *start, *end;
+ size_t alloclen;
+ setup();
+ { begin_structure();
+#if 0
+ get_lenfield(val->signedAuthPack.length, val->signedAuthPack.data, 0, asn1_decode_octetstring);
+#else
+ if (tagnum != 0) return ASN1_MISSING_FIELD;
+ if (asn1class != CONTEXT_SPECIFIC || construction != PRIMITIVE)
+ return ASN1_BAD_ID;
+ start = subbuf.next;
+ {
+ sequence_of_no_tagvars(&subbuf);
+ unused_var(size);
+ end_sequence_of_no_tagvars(&subbuf);
+ }
+ end = subbuf.next;
+ alloclen = end - start;
+ val->signedAuthPack.data = malloc(alloclen);
+ if (val->signedAuthPack.data == NULL)
+ return ENOMEM;
+ val->signedAuthPack.length = alloclen;
+ memcpy(val->signedAuthPack.data, start, alloclen);
+ next_tag();
+#endif
+ opt_field(val->trustedCertifiers, 1, asn1_decode_sequence_of_trusted_ca, NULL);
+ opt_lenfield(val->kdcCert.length, val->kdcCert.data, 2, asn1_decode_octetstring);
+ opt_lenfield(val->encryptionCert.length, val->encryptionCert.data, 2, asn1_decode_octetstring);
+ end_structure();
+ }
+ cleanup();
+}
+
+asn1_error_code asn1_decode_dh_rep_info(asn1buf *buf, krb5_dh_rep_info *val)
+{
+ setup();
+ { begin_structure();
+
+ retval = asn1buf_remove_octetstring(&subbuf, taglen,
+ &val->dhSignedData.data);
+ if (retval) return retval;
+ val->dhSignedData.length = taglen;
+ if (!taglen && indef) {
+ get_eoc();
+ }
+ next_tag();
+
+ opt_lenfield(val->serverDHNonce.length, val->serverDHNonce.data, 1, asn1_decode_octetstring);
+ end_structure();
+ }
+ cleanup();
+}
+
+asn1_error_code asn1_decode_pk_authenticator(asn1buf *buf, krb5_pk_authenticator *val)
+{
+ setup();
+ { begin_structure();
+ get_field(val->cusec, 0, asn1_decode_int32);
+ get_field(val->ctime, 1, asn1_decode_kerberos_time);
+ get_field(val->nonce, 2, asn1_decode_int32);
+ opt_lenfield(val->paChecksum.length, val->paChecksum.contents, 3, asn1_decode_octetstring);
+ end_structure();
+ }
+ cleanup();
+}
+
+asn1_error_code asn1_decode_pk_authenticator_draft9(asn1buf *buf, krb5_pk_authenticator_draft9 *val)
+{
+ setup();
+ { begin_structure();
+ alloc_field(val->kdcName,krb5_principal_data);
+ get_field(val->kdcName, 0, asn1_decode_principal_name);
+ get_field(val->kdcName, 1, asn1_decode_realm);
+ get_field(val->cusec, 2, asn1_decode_int32);
+ get_field(val->ctime, 3, asn1_decode_kerberos_time);
+ get_field(val->nonce, 4, asn1_decode_int32);
+ end_structure();
+ }
+ cleanup();
+}
+
+#if 0
+asn1_error_code asn1_decode_subject_pk_info(asn1buf *buf, krb5_subject_pk_info *val)
+{
+ setup();
+ { begin_structure();
+ fprintf(stderr, "tagnum=%d tagexpect=%d\n", tagnum, 0);
+ get_field(val->algorithm, 16, asn1_decode_algorithm_identifier);
+ get_lenfield(val->subjectPublicKey.length, val->subjectPublicKey.data, 3, asn1_decode_octetstring);
+ end_structure();
+ }
+ cleanup();
+}
+#endif
+asn1_error_code asn1_decode_alg_identifier(asn1buf *buf, krb5_algorithm_identifier *val) {
+
+ setup();
+ { begin_structure_no_tag();
+ retval = asn1_decode_oid(&subbuf, &val->algorithm.length,
+ &val->algorithm.data);
+ if(retval) return retval;
+
+ val->parameters.length = 0;
+ val->parameters.data = NULL;
+
+ if(length > subbuf.next - subbuf.base) {
+ int size = length - (subbuf.next - subbuf.base);
+ retval = asn1buf_remove_octetstring(&subbuf, size,
+ &val->parameters.data);
+ if(retval) return retval;
+ val->parameters.length = size;
+ }
+
+ end_structure();
+ }
+ cleanup();
+}
+
+asn1_error_code asn1_decode_subject_pk_info(asn1buf *buf, krb5_subject_pk_info *val)
+{
+ asn1_octet o;
+ setup();
+ { begin_structure_no_tag();
+
+#if 0
+ if (tagnum > (tagexpect)) return ASN1_MISSING_FIELD;
+ if (tagnum < (tagexpect)) return ASN1_MISPLACED_FIELD;
+ if ((asn1class != CONTEXT_SPECIFIC || construction != CONSTRUCTED)
+ && (tagnum || taglen || asn1class != UNIVERSAL))
+ return ASN1_BAD_ID;
+#endif
+ retval = asn1_decode_alg_identifier(&subbuf, &val->algorithm);
+ if (retval) return retval;
+
+#if 0
+ retval = asn1_decode_octetstring(&subbuf, &val->subjectPublicKey.length,
+ &val->subjectPublicKey.data);
+ if(retval) return retval;
+#else
+ next_tag();
+ retval = asn1buf_remove_octet(&subbuf, &o);
+ if(retval) return retval;
+
+ val->subjectPublicKey.length = 0;
+ val->subjectPublicKey.data = NULL;
+ retval = asn1buf_remove_octetstring(&subbuf, taglen - 1,
+ &val->subjectPublicKey.data);
+ if(retval) return retval;
+ val->subjectPublicKey.length = taglen - 1;
+#endif
+ end_structure();
+ }
+ cleanup();
+}
+
+
+asn1_error_code asn1_decode_algorithm_identifier(asn1buf *buf, krb5_algorithm_identifier *val)
+{
+ setup();
+ { begin_structure();
+ get_lenfield(val->algorithm.length, val->algorithm.data, 0, asn1_decode_oid);
+ opt_lenfield(val->parameters.length, val->parameters.data, 1, asn1_decode_octetstring);
+ end_structure();
+ }
+ cleanup();
+}
+
+asn1_error_code asn1_decode_sequence_of_AlgorithmIdentifier(asn1buf *buf, krb5_algorithm_identifier ***val)
+{
+ decode_array_body(krb5_algorithm_identifier, asn1_decode_algorithm_identifier);
+}
+
+asn1_error_code asn1_decode_kdc_dh_key_info (asn1buf *buf, krb5_kdc_dh_key_info *val)
+{
+ setup();
+ { begin_structure();
+ retval = asn1buf_remove_octetstring(&subbuf, taglen, &val->subjectPublicKey.data);
+ if(retval) return retval;
+ val->subjectPublicKey.length = taglen;
+ next_tag();
+ get_field(val->nonce, 1, asn1_decode_int32);
+ opt_field(val->dhKeyExpiration, 2, asn1_decode_kerberos_time, 0);
+ end_structure();
+ }
+ cleanup();
+}
+
+asn1_error_code asn1_decode_reply_key_pack (asn1buf *buf, krb5_reply_key_pack *val)
+{
+ setup();
+ { begin_structure();
+ get_field(val->replyKey, 0, asn1_decode_encryption_key);
+ get_field(val->asChecksum, 1, asn1_decode_checksum);
+ end_structure();
+ }
+ cleanup();
+}
+
+asn1_error_code asn1_decode_reply_key_pack_draft9 (asn1buf *buf, krb5_reply_key_pack_draft9 *val)
+{
+ setup();
+ { begin_structure();
+ get_field(val->replyKey, 0, asn1_decode_encryption_key);
+ get_field(val->nonce, 1, asn1_decode_int32);
+ end_structure();
+ }
+ cleanup();
+}
+
+
+asn1_error_code asn1_decode_krb5_principal_name (asn1buf *buf, krb5_principal *val)
+{
+ setup();
+ { begin_structure();
+ get_field(*val, 0, asn1_decode_realm);
+ get_field(*val, 1, asn1_decode_principal_name);
+ end_structure();
+ }
+ cleanup();
+}
+
+asn1_error_code asn1_decode_auth_pack(asn1buf *buf, krb5_auth_pack *val)
+{
+ setup();
+ { begin_structure();
+ get_field(val->pkAuthenticator, 0, asn1_decode_pk_authenticator);
+ if (tagnum == 1) { alloc_field(val->clientPublicValue, krb5_subject_pk_info); }
+#if 0
+ opt_field(val->clientPublicValue, 1, asn1_decode_subject_pk_info, NULL);
+#else
+ /* can't call opt_field because it does decoder(&subbuf, &(val)); */
+ if (asn1buf_remains(&subbuf, seqindef)) {
+ if ((asn1class != CONTEXT_SPECIFIC || construction != CONSTRUCTED)
+ && (tagnum || taglen || asn1class != UNIVERSAL))
+ return ASN1_BAD_ID;
+ if (tagnum == 1) {
+ retval = asn1_decode_subject_pk_info(&subbuf,
+ val->clientPublicValue);
+ if (!taglen && indef) { get_eoc(); }
+ next_tag();
+ } else val->clientPublicValue = NULL;
+ }
+#endif
+
+ opt_field(val->supportedCMSTypes, 2, asn1_decode_sequence_of_AlgorithmIdentifier, NULL);
+ opt_lenfield(val->clientDHNonce.length, val->clientDHNonce.data, 3, asn1_decode_octetstring);
+ end_structure();
+ }
+ cleanup();
+}
+
+asn1_error_code asn1_decode_auth_pack_draft9(asn1buf *buf, krb5_auth_pack_draft9 *val)
+{
+ setup();
+ { begin_structure();
+ get_field(val->pkAuthenticator, 0, asn1_decode_pk_authenticator_draft9);
+ if (tagnum == 1) {
+ alloc_field(val->clientPublicValue, krb5_subject_pk_info);
+ /* can't call opt_field because it does decoder(&subbuf, &(val)); */
+ if (asn1buf_remains(&subbuf, seqindef)) {
+ if ((asn1class != CONTEXT_SPECIFIC || construction != CONSTRUCTED)
+ && (tagnum || taglen || asn1class != UNIVERSAL))
+ return ASN1_BAD_ID;
+ if (tagnum == 1) {
+ retval = asn1_decode_subject_pk_info(&subbuf,
+ val->clientPublicValue);
+ if (!taglen && indef) { get_eoc(); }
+ next_tag();
+ } else val->clientPublicValue = NULL;
+ }
+ }
+ end_structure();
+ }
+ cleanup();
+}
+
+#if 0
+asn1_error_code asn1_decode_pa_pk_as_rep(asn1buf *buf, krb5_pa_pk_as_rep *val)
+{
+ setup();
+ { begin_choice();
+ if (t.tagnum == choice_pa_pk_as_rep_dhInfo) {
+ val->choice = choice_pa_pk_as_rep_dhInfo;
+ get_field_body(val->u.dh_Info, asn1_decode_dh_rep_info);
+ val->u.encKeyPack.data = NULL;
+ } else if (t.tagnum == choice_pa_pk_as_rep_encKeyPack) {
+ val->choice = choice_pa_pk_as_rep_encKeyPack;
+ get_lenfield(val->u.encKeyPack.length, val->u.encKeyPack.data,
+ choice_pa_pk_as_rep_encKeyPack, asn1_decode_octetstring);
+ val->u.dh_Info.dhSignedData.data = NULL;
+ } else {
+ val->choice = choice_pa_pk_as_rep_UNKNOWN;
+ }
+ end_choice();
+ }
+ cleanup();
+}
+#else
+asn1_error_code asn1_decode_pa_pk_as_rep(asn1buf *buf, krb5_pa_pk_as_rep *val)
+{
+ setup();
+ { begin_explicit_choice();
+ if (t.tagnum == choice_pa_pk_as_rep_dhInfo) {
+ if(t.construction != CONSTRUCTED) return ASN1_BAD_ID;
+ val->choice = choice_pa_pk_as_rep_dhInfo;
+ get_field_body(val->u.dh_Info, asn1_decode_dh_rep_info);
+ } else if (t.tagnum == choice_pa_pk_as_rep_encKeyPack) {
+ char *start, *end;
+ size_t alloclen;
+ if(t.construction != PRIMITIVE) return ASN1_BAD_ID;
+ val->choice = choice_pa_pk_as_rep_encKeyPack;
+ start = subbuf.next;
+ {
+ sequence_of_no_tagvars(&subbuf);
+ unused_var(size);
+ end_sequence_of_no_tagvars(&subbuf);
+ }
+ end = subbuf.next;
+ alloclen = end - start;
+ val->u.encKeyPack.data = malloc(alloclen);
+ if (val->u.encKeyPack.data == NULL)
+ return ENOMEM;
+ val->u.encKeyPack.length = alloclen;
+ memcpy(val->u.encKeyPack.data, start, alloclen);
+ next_tag();
+ } else {
+ val->choice = choice_pa_pk_as_rep_UNKNOWN;
+ }
+ end_explicit_choice();
+ }
+ cleanup();
+}
+#endif
+
+asn1_error_code asn1_decode_pa_pk_as_rep_draft9(asn1buf *buf, krb5_pa_pk_as_rep_draft9 *val)
+{
+ setup();
+ { begin_structure();
+ if (tagnum == choice_pa_pk_as_rep_draft9_dhSignedData) {
+ val->choice = choice_pa_pk_as_rep_draft9_dhSignedData;
+ get_lenfield(val->u.dhSignedData.length, val->u.dhSignedData.data,
+ choice_pa_pk_as_rep_draft9_dhSignedData, asn1_decode_octetstring);
+ } else if (tagnum == choice_pa_pk_as_rep_draft9_encKeyPack) {
+ val->choice = choice_pa_pk_as_rep_draft9_encKeyPack;
+ get_lenfield(val->u.encKeyPack.length, val->u.encKeyPack.data,
+ choice_pa_pk_as_rep_draft9_encKeyPack, asn1_decode_octetstring);
+ } else {
+ val->choice = choice_pa_pk_as_rep_UNKNOWN;
+ }
+ end_structure();
+ }
+ cleanup();
+}
diff --git a/src/lib/krb5/asn.1/asn1_k_decode.h b/src/lib/krb5/asn.1/asn1_k_decode.h
index 22e43fd..fc8636a 100644
--- a/src/lib/krb5/asn.1/asn1_k_decode.h
+++ b/src/lib/krb5/asn.1/asn1_k_decode.h
@@ -159,6 +159,40 @@ asn1_error_code asn1_decode_sam_response_2
(asn1buf *buf, krb5_sam_response_2 *val);
asn1_error_code asn1_decode_predicted_sam_response
(asn1buf *buf, krb5_predicted_sam_response *val);
+asn1_error_code asn1_decode_external_principal_identifier
+ (asn1buf *buf, krb5_external_principal_identifier *val);
+asn1_error_code asn1_decode_pa_pk_as_req
+ (asn1buf *buf, krb5_pa_pk_as_req *val);
+asn1_error_code asn1_decode_trusted_ca
+ (asn1buf *buf, krb5_trusted_ca *val);
+asn1_error_code asn1_decode_pa_pk_as_req_draft9
+ (asn1buf *buf, krb5_pa_pk_as_req_draft9 *val);
+asn1_error_code asn1_decode_dh_rep_info
+ (asn1buf *buf, krb5_dh_rep_info *val);
+asn1_error_code asn1_decode_pk_authenticator
+ (asn1buf *buf, krb5_pk_authenticator *val);
+asn1_error_code asn1_decode_pk_authenticator_draft9
+ (asn1buf *buf, krb5_pk_authenticator_draft9 *val);
+asn1_error_code asn1_decode_subject_pk_info
+ (asn1buf *buf, krb5_subject_pk_info *val);
+asn1_error_code asn1_decode_algorithm_identifier
+ (asn1buf *buf, krb5_algorithm_identifier *val);
+asn1_error_code asn1_decode_auth_pack
+ (asn1buf *buf, krb5_auth_pack *val);
+asn1_error_code asn1_decode_auth_pack_draft9
+ (asn1buf *buf, krb5_auth_pack_draft9 *val);
+asn1_error_code asn1_decode_pa_pk_as_rep
+ (asn1buf *buf, krb5_pa_pk_as_rep *val);
+asn1_error_code asn1_decode_pa_pk_as_rep_draft9
+ (asn1buf *buf, krb5_pa_pk_as_rep_draft9 *val);
+asn1_error_code asn1_decode_kdc_dh_key_info
+ (asn1buf *buf, krb5_kdc_dh_key_info *val);
+asn1_error_code asn1_decode_krb5_principal_name
+ (asn1buf *buf, krb5_principal *val);
+asn1_error_code asn1_decode_reply_key_pack
+ (asn1buf *buf, krb5_reply_key_pack *val);
+asn1_error_code asn1_decode_reply_key_pack_draft9
+ (asn1buf *buf, krb5_reply_key_pack_draft9 *val);
/* arrays */
asn1_error_code asn1_decode_authorization_data
@@ -187,6 +221,12 @@ asn1_error_code asn1_decode_etype_info
(asn1buf *buf, krb5_etype_info_entry ***val);
asn1_error_code asn1_decode_etype_info2
(asn1buf *buf, krb5_etype_info_entry ***val, krb5_boolean v1_3_behavior);
+asn1_error_code asn1_decode_sequence_of_external_principal_identifier
+ (asn1buf *buf, krb5_external_principal_identifier ***val);
+asn1_error_code asn1_decode_sequence_of_trusted_ca
+ (asn1buf *buf, krb5_trusted_ca ***val);
+asn1_error_code asn1_decode_sequence_of_AlgorithmIdentifier
+ (asn1buf *buf, krb5_algorithm_identifier ***val);
#endif
diff --git a/src/lib/krb5/asn.1/asn1_k_encode.c b/src/lib/krb5/asn.1/asn1_k_encode.c
index 00cfab0..1af1d23 100644
--- a/src/lib/krb5/asn.1/asn1_k_encode.c
+++ b/src/lib/krb5/asn.1/asn1_k_encode.c
@@ -103,6 +103,49 @@
return retval; }\
sum += length; }
+/* asn1_addfield -- add a field, or component, to the encoding */
+#define asn1_addfield_implicit(value,tag,encoder)\
+{ retval = encoder(buf,value,&length);\
+ if(retval){\
+ asn1buf_destroy(&buf);\
+ return retval; }\
+ sum += length;\
+ retval = asn1_make_tag(buf,CONTEXT_SPECIFIC,PRIMITIVE,tag,length,&length); \
+ if(retval){\
+ asn1buf_destroy(&buf);\
+ return retval; }\
+ sum += length; }
+
+/* asn1_addlenfield -- add a field whose length must be separately specified */
+#define asn1_insert_implicit_octetstring(len,value,tag)\
+{ retval = asn1buf_insert_octetstring(buf,len,value);\
+ if(retval){\
+ asn1buf_destroy(&buf);\
+ return retval; }\
+ sum += len;\
+ retval = asn1_make_tag(buf,CONTEXT_SPECIFIC,PRIMITIVE,tag,len,&length); \
+ if(retval){\
+ asn1buf_destroy(&buf);\
+ return retval; }\
+ sum += length; }
+
+#define asn1_insert_implicit_bitstring(len,value,tag)\
+{ retval = asn1buf_insert_octetstring(buf,len,value);\
+ if(retval){\
+ asn1buf_destroy(&buf);\
+ return retval; }\
+ sum += len;\
+ retval = asn1buf_insert_octet(buf, 0);\
+ if(retval){\
+ asn1buf_destroy(&buf);\
+ return retval; }\
+ sum++;\
+ retval = asn1_make_tag(buf,UNIVERSAL,PRIMITIVE,tag,len+1,&length); \
+ if(retval){\
+ asn1buf_destroy(&buf);\
+ return retval; }\
+ sum += length; }
+
/* form a sequence (by adding a sequence header to the current encoding) */
#define asn1_makeseq()\
retval = asn1_make_sequence(buf,sum,&length);\
@@ -959,3 +1002,350 @@ asn1_error_code asn1_encode_krb_saved_safe_body(asn1buf *buf, const krb5_data *b
*retlen = body->length;
return 0;
}
+
+/*
+ * PKINIT
+ */
+
+asn1_error_code asn1_encode_pk_authenticator(asn1buf *buf, const krb5_pk_authenticator *val, unsigned int *retlen)
+{
+ asn1_setup();
+
+ asn1_addlenfield(val->paChecksum.length, val->paChecksum.contents, 3, asn1_encode_octetstring);
+ asn1_addfield(val->nonce, 2, asn1_encode_integer);
+ asn1_addfield(val->ctime, 1, asn1_encode_kerberos_time);
+ asn1_addfield(val->cusec, 0, asn1_encode_integer);
+
+ asn1_makeseq();
+ asn1_cleanup();
+}
+
+asn1_error_code asn1_encode_pk_authenticator_draft9(asn1buf *buf, const krb5_pk_authenticator_draft9 *val, unsigned int *retlen)
+{
+ asn1_setup();
+
+ asn1_addfield(val->nonce, 4, asn1_encode_integer);
+ asn1_addfield(val->ctime, 3, asn1_encode_kerberos_time);
+ asn1_addfield(val->cusec, 2, asn1_encode_integer);
+ asn1_addfield(val->kdcName, 1, asn1_encode_realm);
+ asn1_addfield(val->kdcName, 0, asn1_encode_principal_name);
+
+ asn1_makeseq();
+ asn1_cleanup();
+}
+
+
+asn1_error_code asn1_encode_algorithm_identifier(asn1buf *buf, const krb5_algorithm_identifier *val, unsigned int *retlen)
+{
+ asn1_setup();
+
+ if (val->parameters.length != 0)
+ asn1_addlenfield(val->parameters.length, val->parameters.data, 1, asn1_encode_octetstring);
+ asn1_addlenfield(val->algorithm.length, val->algorithm.data, 0, asn1_encode_octetstring);
+
+ asn1_makeseq();
+ asn1_cleanup();
+}
+
+asn1_error_code asn1_encode_subject_pk_info(asn1buf *buf, const krb5_subject_pk_info *val, unsigned int *retlen)
+{
+ asn1_setup();
+
+ asn1_insert_implicit_bitstring(val->subjectPublicKey.length,val->subjectPublicKey.data,ASN1_BITSTRING);
+
+ if (val->algorithm.parameters.length != 0) {
+ retval = asn1buf_insert_octetstring(buf, val->algorithm.parameters.length,
+ val->algorithm.parameters.data);
+ if(retval) {
+ asn1buf_destroy(&buf);
+ return retval;
+ }
+ sum += val->algorithm.parameters.length;
+ }
+
+ retval = asn1_encode_oid(buf, val->algorithm.algorithm.length,
+ val->algorithm.algorithm.data,
+ &length);
+
+ if(retval) {
+ asn1buf_destroy(&buf);
+ return retval;
+ }
+ sum += length;
+
+ retval = asn1_make_etag(buf, UNIVERSAL, ASN1_SEQUENCE,
+ val->algorithm.parameters.length + length,
+ &length);
+
+ if(retval) {
+ asn1buf_destroy(&buf);
+ return retval;
+ }
+ sum += length;
+
+ asn1_makeseq();
+ asn1_cleanup();
+}
+
+asn1_error_code asn1_encode_sequence_of_algorithm_identifier(asn1buf *buf, const krb5_algorithm_identifier **val, unsigned int *retlen)
+{
+ asn1_setup();
+ int i;
+
+ if(val == NULL || val[0] == NULL) return ASN1_MISSING_FIELD;
+
+ for(i=0; val[i] != NULL; i++);
+ for(i--; i>=0; i--){
+ retval = asn1_encode_algorithm_identifier(buf,val[i],&length);
+ if(retval) return retval;
+ sum += length;
+ }
+ asn1_makeseq();
+
+ asn1_cleanup();
+}
+
+asn1_error_code asn1_encode_auth_pack(asn1buf *buf, const krb5_auth_pack *val, unsigned int *retlen)
+{
+ asn1_setup();
+
+ if (val->clientDHNonce.length != 0)
+ asn1_addlenfield(val->clientDHNonce.length, val->clientDHNonce.data, 3, asn1_encode_octetstring);
+ if (val->supportedCMSTypes != NULL)
+ asn1_addfield(val->supportedCMSTypes,2,asn1_encode_sequence_of_algorithm_identifier);
+ if (val->clientPublicValue != NULL)
+ asn1_addfield(val->clientPublicValue,1,asn1_encode_subject_pk_info);
+ asn1_addfield(&(val->pkAuthenticator),0,asn1_encode_pk_authenticator);
+
+ asn1_makeseq();
+ asn1_cleanup();
+}
+
+asn1_error_code asn1_encode_auth_pack_draft9(asn1buf *buf, const krb5_auth_pack_draft9 *val, unsigned int *retlen)
+{
+ asn1_setup();
+
+ if (val->clientPublicValue != NULL)
+ asn1_addfield(val->clientPublicValue, 1, asn1_encode_subject_pk_info);
+ asn1_addfield(&(val->pkAuthenticator), 0, asn1_encode_pk_authenticator_draft9);
+
+ asn1_makeseq();
+ asn1_cleanup();
+}
+
+asn1_error_code asn1_encode_external_principal_identifier(asn1buf *buf, const krb5_external_principal_identifier *val, unsigned int *retlen)
+{
+ asn1_setup();
+
+ /* Verify there is something to encode */
+ if (val->subjectKeyIdentifier.length == 0 && val->issuerAndSerialNumber.length == 0 && val->subjectName.length == 0)
+ return ASN1_MISSING_FIELD;
+
+ if (val->subjectKeyIdentifier.length != 0)
+ asn1_insert_implicit_octetstring(val->subjectKeyIdentifier.length,val->subjectKeyIdentifier.data,2);
+
+ if (val->issuerAndSerialNumber.length != 0)
+ asn1_insert_implicit_octetstring(val->issuerAndSerialNumber.length,val->issuerAndSerialNumber.data,1);
+
+ if (val->subjectName.length != 0)
+ asn1_insert_implicit_octetstring(val->subjectName.length,val->subjectName.data,0);
+
+ asn1_makeseq();
+ asn1_cleanup();
+}
+
+asn1_error_code asn1_encode_sequence_of_external_principal_identifier(asn1buf *buf, const krb5_external_principal_identifier **val, unsigned int *retlen)
+{
+ asn1_setup();
+ int i;
+
+ if(val == NULL || val[0] == NULL) return ASN1_MISSING_FIELD;
+
+ for(i=0; val[i] != NULL; i++);
+ for(i--; i>=0; i--){
+ retval = asn1_encode_external_principal_identifier(buf,val[i],&length);
+ if(retval) return retval;
+ sum += length;
+ }
+ asn1_makeseq();
+
+ asn1_cleanup();
+}
+
+asn1_error_code asn1_encode_pa_pk_as_req(asn1buf *buf, const krb5_pa_pk_as_req *val, unsigned int *retlen)
+{
+ asn1_setup();
+
+ if (val->kdcPkId.length != 0)
+ asn1_insert_implicit_octetstring(val->kdcPkId.length,val->kdcPkId.data,2);
+
+ if (val->trustedCertifiers != NULL)
+ asn1_addfield(val->trustedCertifiers,1,asn1_encode_sequence_of_external_principal_identifier);
+
+ asn1_insert_implicit_octetstring(val->signedAuthPack.length,val->signedAuthPack.data,0);
+
+ asn1_makeseq();
+ asn1_cleanup();
+}
+
+asn1_error_code asn1_encode_trusted_ca(asn1buf *buf, const krb5_trusted_ca *val, unsigned int *retlen)
+{
+ asn1_setup();
+
+ switch (val->choice) {
+ case choice_trusted_cas_issuerAndSerial:
+ asn1_insert_implicit_octetstring(val->u.issuerAndSerial.length,val->u.issuerAndSerial.data,2);
+ break;
+ case choice_trusted_cas_caName:
+ asn1_insert_implicit_octetstring(val->u.caName.length,val->u.caName.data,1);
+ break;
+ case choice_trusted_cas_principalName:
+ asn1_addfield_implicit(val->u.principalName,0,asn1_encode_principal_name);
+ break;
+ default:
+ return ASN1_MISSING_FIELD;
+ }
+
+ asn1_cleanup();
+}
+
+asn1_error_code asn1_encode_sequence_of_trusted_ca(asn1buf *buf, const krb5_trusted_ca **val, unsigned int *retlen)
+{
+ asn1_setup();
+ int i;
+
+ if(val == NULL || val[0] == NULL) return ASN1_MISSING_FIELD;
+
+ for(i=0; val[i] != NULL; i++);
+ for(i--; i>=0; i--){
+ retval = asn1_encode_trusted_ca(buf,val[i],&length);
+ if(retval) return retval;
+ sum += length;
+ }
+ asn1_makeseq();
+ asn1_cleanup();
+}
+
+asn1_error_code asn1_encode_pa_pk_as_req_draft9(asn1buf *buf, const krb5_pa_pk_as_req_draft9 *val, unsigned int *retlen)
+{
+ asn1_setup();
+
+ if (val->encryptionCert.length != 0)
+ asn1_insert_implicit_octetstring(val->encryptionCert.length,val->encryptionCert.data,3);
+
+ if (val->kdcCert.length != 0)
+ asn1_insert_implicit_octetstring(val->kdcCert.length,val->kdcCert.data,2);
+
+ if (val->trustedCertifiers != NULL)
+ asn1_addfield(val->trustedCertifiers,1,asn1_encode_sequence_of_trusted_ca);
+
+ asn1_insert_implicit_octetstring(val->signedAuthPack.length,val->signedAuthPack.data,0);
+
+ asn1_makeseq();
+ asn1_cleanup();
+}
+
+asn1_error_code asn1_encode_dh_rep_info(asn1buf *buf, const krb5_dh_rep_info *val, unsigned int *retlen)
+{
+ asn1_setup();
+
+ if (val->serverDHNonce.length != 0)
+ asn1_insert_implicit_octetstring(val->serverDHNonce.length,val->serverDHNonce.data,1);
+
+ asn1_insert_implicit_octetstring(val->dhSignedData.length,val->dhSignedData.data,0);
+
+ asn1_makeseq();
+ asn1_cleanup();
+}
+
+asn1_error_code asn1_encode_kdc_dh_key_info(asn1buf *buf, const krb5_kdc_dh_key_info *val, unsigned int *retlen)
+{
+ asn1_setup();
+
+ if (val->dhKeyExpiration != 0)
+ asn1_addfield(val->dhKeyExpiration, 2, asn1_encode_kerberos_time);
+ asn1_addfield(val->nonce, 1, asn1_encode_integer);
+
+ asn1_insert_implicit_bitstring(val->subjectPublicKey.length,val->subjectPublicKey.data,3);
+ retval = asn1_make_etag(buf, CONTEXT_SPECIFIC, 0,
+ val->subjectPublicKey.length + 1 + length,
+ &length);
+ if(retval) {
+ asn1buf_destroy(&buf);
+ return retval;
+ }
+ sum += length;
+
+ asn1_makeseq();
+ asn1_cleanup();
+}
+
+asn1_error_code asn1_encode_reply_key_pack(asn1buf *buf, const krb5_reply_key_pack *val, unsigned int *retlen)
+{
+ asn1_setup();
+
+ asn1_addfield(&(val->asChecksum), 1, asn1_encode_checksum);
+ asn1_addfield(&(val->replyKey), 0, asn1_encode_encryption_key);
+
+ asn1_makeseq();
+ asn1_cleanup();
+}
+
+asn1_error_code asn1_encode_reply_key_pack_draft9(asn1buf *buf, const krb5_reply_key_pack_draft9 *val, unsigned int *retlen)
+{
+ asn1_setup();
+
+ asn1_addfield(val->nonce, 1, asn1_encode_integer);
+ asn1_addfield(&(val->replyKey), 0, asn1_encode_encryption_key);
+
+ asn1_makeseq();
+ asn1_cleanup();
+}
+
+asn1_error_code asn1_encode_pa_pk_as_rep(asn1buf *buf, const krb5_pa_pk_as_rep *val, unsigned int *retlen)
+{
+ asn1_setup();
+
+ switch (val->choice)
+ {
+ case choice_pa_pk_as_rep_dhInfo:
+ asn1_addfield(&(val->u.dh_Info), choice_pa_pk_as_rep_dhInfo, asn1_encode_dh_rep_info);
+ break;
+ case choice_pa_pk_as_rep_encKeyPack:
+ asn1_insert_implicit_octetstring(val->u.encKeyPack.length,val->u.encKeyPack.data,1);
+ break;
+ default:
+ return ASN1_MISSING_FIELD;
+ }
+
+ asn1_cleanup();
+}
+
+asn1_error_code asn1_encode_pa_pk_as_rep_draft9(asn1buf *buf, const krb5_pa_pk_as_rep_draft9 *val, unsigned int *retlen)
+{
+ asn1_setup();
+
+ switch (val->choice)
+ {
+ case choice_pa_pk_as_rep_draft9_dhSignedData:
+ asn1_insert_implicit_octetstring(val->u.dhSignedData.length,val->u.dhSignedData.data,0);
+ break;
+ case choice_pa_pk_as_rep_encKeyPack:
+ asn1_insert_implicit_octetstring(val->u.encKeyPack.length,val->u.encKeyPack.data,1);
+ break;
+ default:
+ return ASN1_MISSING_FIELD;
+ }
+
+ asn1_cleanup();
+}
+
+asn1_error_code asn1_encode_td_trusted_certifiers(asn1buf *buf, const krb5_external_principal_identifier **val, unsigned int *retlen)
+{
+ asn1_setup();
+ /* unfinished function */
+ asn1_addfield(val,1,asn1_encode_sequence_of_external_principal_identifier);
+ asn1_cleanup();
+}
+
+
diff --git a/src/lib/krb5/asn.1/asn1_k_encode.h b/src/lib/krb5/asn.1/asn1_k_encode.h
index caa46c5..76dbdfa 100644
--- a/src/lib/krb5/asn.1/asn1_k_encode.h
+++ b/src/lib/krb5/asn.1/asn1_k_encode.h
@@ -269,4 +269,65 @@ asn1_error_code asn1_encode_predicted_sam_response
asn1_error_code asn1_encode_krb_saved_safe_body
(asn1buf *buf, const krb5_data *body, unsigned int *retlen);
+/* PKINIT */
+
+asn1_error_code asn1_encode_pk_authenticator
+ (asn1buf *buf, const krb5_pk_authenticator *val, unsigned int *retlen);
+
+asn1_error_code asn1_encode_pk_authenticator_draft9
+ (asn1buf *buf, const krb5_pk_authenticator_draft9 *val, unsigned int *retlen);
+
+asn1_error_code asn1_encode_algorithm_identifier
+ (asn1buf *buf, const krb5_algorithm_identifier *val, unsigned int *retlen);
+
+asn1_error_code asn1_encode_subject_pk_info
+ (asn1buf *buf, const krb5_subject_pk_info *val, unsigned int *retlen);
+
+asn1_error_code asn1_encode_sequence_of_algorithm_identifier
+ (asn1buf *buf, const krb5_algorithm_identifier **val, unsigned int *retlen);
+
+asn1_error_code asn1_encode_auth_pack
+ (asn1buf *buf, const krb5_auth_pack *val, unsigned int *retlen);
+
+asn1_error_code asn1_encode_auth_pack_draft9
+ (asn1buf *buf, const krb5_auth_pack_draft9 *val, unsigned int *retlen);
+
+asn1_error_code asn1_encode_external_principal_identifier
+ (asn1buf *buf, const krb5_external_principal_identifier *val, unsigned int *retlen);
+
+asn1_error_code asn1_encode_sequence_of_external_principal_identifier
+ (asn1buf *buf, const krb5_external_principal_identifier **val, unsigned int *retlen);
+
+asn1_error_code asn1_encode_pa_pk_as_req
+ (asn1buf *buf, const krb5_pa_pk_as_req *val, unsigned int *retlen);
+
+asn1_error_code asn1_encode_trusted_ca
+ (asn1buf *buf, const krb5_trusted_ca *val, unsigned int *retlen);
+
+asn1_error_code asn1_encode_sequence_of_trusted_ca
+ (asn1buf *buf, const krb5_trusted_ca **val, unsigned int *retlen);
+
+asn1_error_code asn1_encode_pa_pk_as_req_draft9
+ (asn1buf *buf, const krb5_pa_pk_as_req_draft9 *val, unsigned int *retlen);
+
+asn1_error_code asn1_encode_dh_rep_info
+ (asn1buf *buf, const krb5_dh_rep_info *val, unsigned int *retlen);
+
+asn1_error_code asn1_encode_kdc_dh_key_info
+ (asn1buf *buf, const krb5_kdc_dh_key_info *val, unsigned int *retlen);
+
+asn1_error_code asn1_encode_reply_key_pack
+ (asn1buf *buf, const krb5_reply_key_pack *val, unsigned int *retlen);
+
+asn1_error_code asn1_encode_reply_key_pack_draft9
+ (asn1buf *buf, const krb5_reply_key_pack_draft9 *val, unsigned int *retlen);
+
+asn1_error_code asn1_encode_pa_pk_as_rep
+ (asn1buf *buf, const krb5_pa_pk_as_rep *val, unsigned int *retlen);
+
+asn1_error_code asn1_encode_pa_pk_as_rep_draft9
+ (asn1buf *buf, const krb5_pa_pk_as_rep_draft9 *val, unsigned int *retlen);
+
+asn1_error_code asn1_encode_td_trusted_certifiers
+ (asn1buf *buf, const krb5_external_principal_identifier **val, unsigned int *retlen);
#endif
diff --git a/src/lib/krb5/asn.1/krb5_decode.c b/src/lib/krb5/asn.1/krb5_decode.c
index e5cd4f8..0f3de4e 100644
--- a/src/lib/krb5/asn.1/krb5_decode.c
+++ b/src/lib/krb5/asn.1/krb5_decode.c
@@ -934,3 +934,113 @@ krb5_error_code decode_krb5_predicted_sam_response(const krb5_data *code, krb5_p
cleanup(free);
}
+krb5_error_code decode_krb5_pa_pk_as_req(const krb5_data *code, krb5_pa_pk_as_req **rep)
+{
+ setup_buf_only();
+ alloc_field(*rep, krb5_pa_pk_as_req);
+
+ retval = asn1_decode_pa_pk_as_req(&buf, *rep);
+ if (retval) clean_return(retval);
+
+ cleanup(free);
+}
+
+krb5_error_code decode_krb5_pa_pk_as_req_draft9(const krb5_data *code, krb5_pa_pk_as_req_draft9 **rep)
+{
+ setup_buf_only();
+ alloc_field(*rep, krb5_pa_pk_as_req_draft9);
+
+ retval = asn1_decode_pa_pk_as_req_draft9(&buf, *rep);
+ if (retval) clean_return(retval);
+
+ cleanup(free);
+}
+
+krb5_error_code decode_krb5_pa_pk_as_rep(const krb5_data *code, krb5_pa_pk_as_rep **rep)
+{
+ setup_buf_only();
+ alloc_field(*rep, krb5_pa_pk_as_rep);
+
+ retval = asn1_decode_pa_pk_as_rep(&buf, *rep);
+ if (retval) clean_return(retval);
+
+ cleanup(free);
+}
+
+krb5_error_code decode_krb5_pa_pk_as_rep_draft9(const krb5_data *code, krb5_pa_pk_as_rep_draft9 **rep)
+{
+ setup_buf_only();
+ alloc_field(*rep, krb5_pa_pk_as_rep_draft9);
+
+ retval = asn1_decode_pa_pk_as_rep_draft9(&buf, *rep);
+ if (retval) clean_return(retval);
+
+ cleanup(free);
+}
+
+krb5_error_code decode_krb5_auth_pack(const krb5_data *code, krb5_auth_pack **rep)
+{
+ setup_buf_only();
+ alloc_field(*rep, krb5_auth_pack);
+
+ retval = asn1_decode_auth_pack(&buf, *rep);
+ if (retval) clean_return(retval);
+
+ cleanup(free);
+}
+
+krb5_error_code decode_krb5_auth_pack_draft9(const krb5_data *code, krb5_auth_pack_draft9 **rep)
+{
+ setup_buf_only();
+ alloc_field(*rep, krb5_auth_pack_draft9);
+
+ retval = asn1_decode_auth_pack_draft9(&buf, *rep);
+ if (retval) clean_return(retval);
+
+ cleanup(free);
+}
+
+krb5_error_code decode_krb5_kdc_dh_key_info(const krb5_data *code, krb5_kdc_dh_key_info **rep)
+{
+ setup_buf_only();
+ alloc_field(*rep, krb5_kdc_dh_key_info);
+
+ retval = asn1_decode_kdc_dh_key_info(&buf, *rep);
+ if (retval) clean_return(retval);
+
+ cleanup(free);
+}
+
+krb5_error_code decode_krb5_principal_name(const krb5_data *code, krb5_principal_data **rep)
+{
+ setup_buf_only();
+ alloc_field(*rep, krb5_principal_data);
+
+ retval = asn1_decode_krb5_principal_name(&buf, rep);
+ if (retval) clean_return(retval);
+
+ cleanup(free);
+}
+
+krb5_error_code decode_krb5_reply_key_pack(const krb5_data *code, krb5_reply_key_pack **rep)
+{
+ setup_buf_only();
+ alloc_field(*rep, krb5_reply_key_pack);
+
+ retval = asn1_decode_reply_key_pack(&buf, *rep);
+ if (retval) clean_return(retval);
+
+ cleanup(free);
+}
+
+krb5_error_code decode_krb5_reply_key_pack_draft9(const krb5_data *code, krb5_reply_key_pack_draft9 **rep)
+{
+ setup_buf_only();
+ alloc_field(*rep, krb5_reply_key_pack_draft9);
+
+ retval = asn1_decode_reply_key_pack_draft9(&buf, *rep);
+ if (retval) clean_return(retval);
+
+ cleanup(free);
+}
+
diff --git a/src/lib/krb5/asn.1/krb5_encode.c b/src/lib/krb5/asn.1/krb5_encode.c
index 639db43..0f5dbe0 100644
--- a/src/lib/krb5/asn.1/krb5_encode.c
+++ b/src/lib/krb5/asn.1/krb5_encode.c
@@ -887,3 +887,94 @@ krb5_error_code encode_krb5_setpw_req(const krb5_principal target,
krb5_cleanup();
}
+
+krb5_error_code encode_krb5_pa_pk_as_req(const krb5_pa_pk_as_req *rep, krb5_data **code)
+{
+ krb5_setup();
+ retval = asn1_encode_pa_pk_as_req(buf,rep,&length);
+ if(retval) return retval;
+ sum += length;
+ krb5_cleanup();
+}
+
+krb5_error_code encode_krb5_pa_pk_as_req_draft9(const krb5_pa_pk_as_req_draft9 *rep, krb5_data **code)
+{
+ krb5_setup();
+ retval = asn1_encode_pa_pk_as_req_draft9(buf,rep,&length);
+ if(retval) return retval;
+ sum += length;
+ krb5_cleanup();
+}
+
+krb5_error_code encode_krb5_pa_pk_as_rep(const krb5_pa_pk_as_rep *rep, krb5_data **code)
+{
+ krb5_setup();
+ retval = asn1_encode_pa_pk_as_rep(buf,rep,&length);
+ if(retval) return retval;
+ sum += length;
+ krb5_cleanup();
+}
+
+krb5_error_code encode_krb5_pa_pk_as_rep_draft9(const krb5_pa_pk_as_rep_draft9 *rep, krb5_data **code)
+{
+ krb5_setup();
+ retval = asn1_encode_pa_pk_as_rep_draft9(buf,rep,&length);
+ if(retval) return retval;
+ sum += length;
+ krb5_cleanup();
+}
+
+krb5_error_code encode_krb5_auth_pack(const krb5_auth_pack *rep, krb5_data **code)
+{
+ krb5_setup();
+ retval = asn1_encode_auth_pack(buf,rep,&length);
+ if(retval) return retval;
+ sum += length;
+ krb5_cleanup();
+}
+
+krb5_error_code encode_krb5_auth_pack_draft9(const krb5_auth_pack_draft9 *rep, krb5_data **code)
+{
+ krb5_setup();
+ retval = asn1_encode_auth_pack_draft9(buf,rep,&length);
+ if(retval) return retval;
+ sum += length;
+ krb5_cleanup();
+}
+
+krb5_error_code encode_krb5_kdc_dh_key_info(const krb5_kdc_dh_key_info *rep, krb5_data **code)
+{
+ krb5_setup();
+ retval = asn1_encode_kdc_dh_key_info(buf,rep,&length);
+ if(retval) return retval;
+ sum += length;
+ krb5_cleanup();
+}
+
+krb5_error_code encode_krb5_reply_key_pack(const krb5_reply_key_pack *rep, krb5_data **code)
+{
+ krb5_setup();
+ retval = asn1_encode_reply_key_pack(buf,rep,&length);
+ if(retval) return retval;
+ sum += length;
+ krb5_cleanup();
+}
+
+krb5_error_code encode_krb5_reply_key_pack_draft9(const krb5_reply_key_pack_draft9 *rep, krb5_data **code)
+{
+ krb5_setup();
+ retval = asn1_encode_reply_key_pack_draft9(buf,rep,&length);
+ if(retval) return retval;
+ sum += length;
+ krb5_cleanup();
+}
+
+krb5_error_code encode_krb5_td_trusted_certifiers(const krb5_external_principal_identifier **rep, krb5_data **code)
+{
+ krb5_setup();
+ retval = asn1_encode_td_trusted_certifiers(buf,rep,&length);
+ if(retval) return retval;
+ sum += length;
+ krb5_cleanup();
+}
+
diff --git a/src/lib/krb5/libkrb5.exports b/src/lib/krb5/libkrb5.exports
index d7998f9..5e66d35 100644
--- a/src/lib/krb5/libkrb5.exports
+++ b/src/lib/krb5/libkrb5.exports
@@ -164,6 +164,8 @@ decode_krb5_as_rep
decode_krb5_as_req
decode_krb5_authdata
decode_krb5_authenticator
+decode_krb5_auth_pack
+decode_krb5_auth_pack_draft9
decode_krb5_cred
decode_krb5_enc_cred_part
decode_krb5_enc_data
@@ -177,13 +179,20 @@ decode_krb5_encryption_key
decode_krb5_error
decode_krb5_etype_info
decode_krb5_etype_info2
+decode_krb5_kdc_dh_key_info
decode_krb5_kdc_req_body
decode_krb5_pa_enc_ts
+decode_krb5_pa_pk_as_rep
+decode_krb5_pa_pk_as_req
+decode_krb5_pa_pk_as_req_draft9
decode_krb5_padata_sequence
decode_krb5_predicted_sam_response
+decode_krb5_principal_name
decode_krb5_priv
decode_krb5_pwd_data
decode_krb5_pwd_sequence
+decode_krb5_reply_key_pack
+decode_krb5_reply_key_pack_draft9
decode_krb5_safe
decode_krb5_safe_with_body
decode_krb5_sam_challenge
@@ -202,6 +211,8 @@ encode_krb5_as_rep
encode_krb5_as_req
encode_krb5_authdata
encode_krb5_authenticator
+encode_krb5_auth_pack
+encode_krb5_auth_pack_draft9
encode_krb5_cred
encode_krb5_enc_cred_part
encode_krb5_enc_data
@@ -214,13 +225,20 @@ encode_krb5_encryption_key
encode_krb5_error
encode_krb5_etype_info
encode_krb5_etype_info2
+encode_krb5_kdc_dh_key_info
encode_krb5_kdc_req_body
encode_krb5_pa_enc_ts
+encode_krb5_pa_pk_as_rep
+encode_krb5_pa_pk_as_rep_draft9
+encode_krb5_pa_pk_as_req
+encode_krb5_pa_pk_as_req_draft9
encode_krb5_padata_sequence
encode_krb5_predicted_sam_response
encode_krb5_priv
encode_krb5_pwd_data
encode_krb5_pwd_sequence
+encode_krb5_reply_key_pack
+encode_krb5_reply_key_pack_draft9
encode_krb5_safe
encode_krb5_safe_with_body
encode_krb5_sam_challenge
@@ -230,6 +248,7 @@ encode_krb5_sam_key
encode_krb5_sam_response
encode_krb5_sam_response_2
encode_krb5_setpw_req
+encode_krb5_td_trusted_certifiers
encode_krb5_tgs_rep
encode_krb5_tgs_req
encode_krb5_ticket
diff --git a/src/plugins/preauth/pkinit/Makefile.in b/src/plugins/preauth/pkinit/Makefile.in
new file mode 100644
index 0000000..39f96cc
--- /dev/null
+++ b/src/plugins/preauth/pkinit/Makefile.in
@@ -0,0 +1,63 @@
+thisconfigdir=.
+myfulldir=plugins/preauth/pkinit
+mydir=.
+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
+
+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)
+SHLIB_EXPLIBS= -lkrb5 -lcom_err -lk5crypto -ldl $(SUPPORT_LIB) $(LIBS)
+
+SHLIB_DIRS=-L$(TOPLIBD)
+SHLIB_RDIRS=$(KRB5_LIBDIR)
+
+SRCS= \
+ $(srcdir)/pkinit_srv.c \
+ $(srcdir)/pkinit_lib.c \
+ $(srcdir)/pkinit_clnt.c
+
+STOBJLISTS=OBJS.ST
+STLIBOBJS= \
+ pkinit_srv.o \
+ pkinit_lib.o \
+ pkinit_clnt.o
+
+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_srv.so pkinit_srv.po $(OUTPRE)pkinit_srv.$(OBJEXT): \
+ $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(SRCTOP)/include/k5-int-pkinit.h \
+ $(SRCTOP)/include/krb5/preauth_plugin.h pkinit.h pkinit_srv.c
+pkinit_lib.so pkinit_lib.po $(OUTPRE)pkinit_lib.$(OBJEXT): \
+ $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(SRCTOP)/include/k5-int-pkinit.h \
+ $(SRCTOP)/include/krb5/preauth_plugin.h pkinit.h pkinit_lib.c
+pkinit_clnt.so pkinit_clnt.po $(OUTPRE)pkinit_clnt.$(OBJEXT): \
+ $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(SRCTOP)/include/k5-int-pkinit.h \
+ $(SRCTOP)/include/krb5/preauth_plugin.h pkinit.h pkinit_clnt.c
diff --git a/src/plugins/preauth/pkinit/README b/src/plugins/preauth/pkinit/README
new file mode 100644
index 0000000..ebdaf97
--- /dev/null
+++ b/src/plugins/preauth/pkinit/README
@@ -0,0 +1,52 @@
+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.
+
+Running with pkinit enabled:
+=============================
+The current code depends on environment variables to locate
+the Certificate, Private Key, and Trusted CAs. Future code
+will have similar features to those in the Heimdal pkinit,
+being able to switch between FILE: and PKCS11: storage
+locations.
+
+In the following, X509_CA_DIR points to an OpenSSL-style
+hashed CA directory where each CA cert is stored in a file
+"<hash of CA cert>.0".
+We assume that X509_CA_DIR will also contain CRL files
+"<hash of CA cert>.r0".
+
+For a pkinit-enabled KDC:
+-------------------------
+1. setenv KDC_CERT <full path to your kdc cert>
+2. setenv KDC_KEY <full path to your kdc key>
+3. setenv X509_CA_DIR <path to your trusted cas directory>
+4. start your kdc
+
+For a pkinit-enabled client using smartcards:
+---------------------------------------------
+1. Follow the directions at http://www.citi.umich.edu/projects/pkinit/smartcard_setup.html
+2. setenv X509_CA_DIR <path to your trusted cas directory>
+
+For a pkinit-enabled client using the filesystem for Cert and Key:
+------------------------------------------------------------------
+1. setenv X509_USER_CERT <full path to your client cert>
+2. setenv X509_USER_KEY <full path to your client key>
+3. setenv X509_CA_DIR <path to your trusted cas directory>
+
+
+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.
+
+Known Issues
+============
+- The client principal must currently have the REQUIRES_PREAUTH attribute
+ set to cause the use of pkinit
+- Some error reporting is incomplete
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..59ece38
--- /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_WITH_DEPS
+V5_AC_OUTPUT_MAKEFILE
diff --git a/src/plugins/preauth/pkinit/pkinit.exports b/src/plugins/preauth/pkinit/pkinit.exports
new file mode 100644
index 0000000..32e067b
--- /dev/null
+++ b/src/plugins/preauth/pkinit/pkinit.exports
@@ -0,0 +1,2 @@
+preauthentication_client_0
+preauthentication_server_0
diff --git a/src/plugins/preauth/pkinit/pkinit.h b/src/plugins/preauth/pkinit/pkinit.h
new file mode 100644
index 0000000..c8e90ea
--- /dev/null
+++ b/src/plugins/preauth/pkinit/pkinit.h
@@ -0,0 +1,142 @@
+/*
+ * COPYRIGHT (C) 2006
+ * 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
+
+extern const krb5_octet_data dh_oid;
+extern unsigned char pkinit_1024_dhprime[1024/8];
+extern unsigned char pkinit_2048_dhprime[2048/8];
+extern unsigned char pkinit_4096_dhprime[4096/8];
+
+typedef struct _pkinit_context {
+ int magic;
+ int version;
+ int require_eku;
+ int require_san;
+ int allow_upn;
+ int require_crl_checking;
+ 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_pkinit_san9;
+ ASN1_OBJECT *id_pkinit_KPClientAuth;
+ ASN1_OBJECT *id_pkinit_KPKdc;
+ ASN1_OBJECT *id_ms_kp_sc_logon;
+ ASN1_OBJECT *id_kp_serverAuth;
+} pkinit_context;
+
+typedef struct _pkinit_req_context {
+ int magic;
+ int version;
+ DH *dh;
+} pkinit_req_context;
+
+/* Function prototypes */
+
+void openssl_init(void);
+
+krb5_error_code pkinit_sign_data
+ (unsigned char *data, int data_len, unsigned char **sig,
+ int *sig_len, char *filename);
+
+krb5_error_code create_signature
+ (unsigned char **, int *, unsigned char *, int, char *);
+
+krb5_error_code pkcs7_signeddata_create
+ (unsigned char *, int, unsigned char **, int *, X509 *,
+ char *, ASN1_OBJECT *, krb5_context);
+
+krb5_error_code pkcs7_signeddata_verify
+ (unsigned char *, int, char **, int *, X509 **,
+ ASN1_OBJECT *, krb5_context, pkinit_context *);
+
+krb5_error_code pkinit_octetstring2key
+ (krb5_context context, krb5_enctype etype, unsigned char *key,
+ int key_len, krb5_keyblock * krb5key);
+
+krb5_error_code pkcs7_envelopeddata_create
+ (unsigned char *key_pack, int key_pack_len, unsigned char **out,
+ int *out_len, X509 *client_cert, X509 *kdc_cert,
+ krb5_preauthtype pa_type, char *filename,
+ ASN1_OBJECT *, krb5_context context);
+
+krb5_error_code pkcs7_envelopeddata_verify
+ (unsigned char *, int, char **, int *, X509 *, char *,
+ krb5_preauthtype , pkinit_context *, X509 **, krb5_context);
+
+int verify_id_pkinit_san
+ (X509 * x, krb5_principal *out, krb5_context context,
+ krb5_preauthtype pa_type, pkinit_context *plgctx);
+
+int verify_id_pkinit_eku
+ (X509 * x, krb5_preauthtype pa_type, pkinit_context *plgctx);
+
+krb5_error_code load_trusted_certifiers
+ (STACK_OF(X509) **, char *);
+
+krb5_error_code create_krb5_trustedCertifiers
+ (STACK_OF(X509) *, krb5_external_principal_identifier ***);
+
+krb5_error_code pkinit_lib_init
+ (krb5_context context, void **blob);
+
+void pkinit_lib_fini
+ (krb5_context context, void *blob);
+
+krb5_error_code get_filename(char **, char *, int);
+X509 *get_cert(char *filename);
+void hexdump(const u_char * buf, int len, int offset);
+void print_buffer(unsigned char *, int);
+void print_buffer_bin(unsigned char *, int, char *);
+void print_dh(DH *, unsigned char *);
+void print_pubkey(BIGNUM *, unsigned char *);
+
+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 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);
+#endif /* _PKINIT_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..6eb4485
--- /dev/null
+++ b/src/plugins/preauth/pkinit/pkinit_clnt.c
@@ -0,0 +1,1684 @@
+/*
+ * COPYRIGHT (C) 2006
+ * 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 <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <dlfcn.h>
+
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+#include <openssl/pkcs7.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/dh.h>
+#include <openssl/asn1_mac.h>
+#include <openssl/sha.h>
+#include <openssl/asn1.h>
+
+#ifndef WITHOUT_PKCS11
+#include <opensc/pkcs11.h>
+#endif
+
+#include <krb5/preauth_plugin.h>
+#include <k5-int-pkinit.h>
+#include "pkinit.h"
+
+/* #define DEBUG */
+/* #define DEBUG_DH */
+#ifdef DEBUG
+#define pkiDebug(args...) printf(args)
+#else
+#define pkiDebug(args...)
+#endif
+
+#define DH_PROTOCOL 0
+#define RSA_PROTOCOL 1
+
+krb5_error_code pkinit_client_process
+ (krb5_context context, void *plugin_context, void *request_context,
+ 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,
+ 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 pkinit_as_req_create
+ (krb5_context context, pkinit_context *plgctx,
+ pkinit_req_context *reqctx, krb5_preauthtype pa_type,
+ krb5_timestamp ctsec, krb5_int32 cusec, krb5_ui_4 nonce,
+ const krb5_checksum * cksum, krb5_principal server,
+ X509 * client_cert, STACK_OF(X509) * trusted_CAs,
+ X509 *kdc_cert, 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,
+ X509 * client_cert, krb5_keyblock * key_block,
+ krb5_enctype etype, krb5_principal server);
+
+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_pa_data ** out_padata,
+ krb5_enctype etype, krb5_keyblock * as_key);
+
+static int using_pkcs11(void);
+
+krb5_error_code pkinit_get_client_cert
+ (const char *principal, char *filename, X509 ** client_cert);
+
+krb5_error_code create_issuerAndSerial
+ (X509 *cert, unsigned char **out, int *out_len);
+
+static krb5_error_code client_create_dh
+ (DH **, unsigned char **, int *, unsigned char **, int *);
+
+static krb5_error_code client_process_dh
+ (DH *, unsigned char *, long, unsigned char **, int *);
+
+static krb5_error_code der_encode_data
+ (unsigned char *, int, unsigned char **, long *);
+
+static krb5_error_code der_decode_data
+ (unsigned char *, long, unsigned char **, long *);
+
+static krb5_error_code create_krb5_trustedCas
+ (STACK_OF(X509) *, int flag, krb5_trusted_ca ***);
+
+#ifndef WITHOUT_PKCS11
+static krb5_error_code pkinit_open_session
+ (krb5_context, krb5_prompter_fct, void *);
+
+static void pkinit_close_session();
+#endif
+
+static void init_krb5_subject_pk_info(krb5_subject_pk_info **in);
+
+static void free_krb5_kdc_dh_key_info(krb5_kdc_dh_key_info **in);
+static void free_krb5_subject_pk_info(krb5_subject_pk_info **in);
+
+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_enctype * etype,
+ krb5_keyblock * as_key)
+{
+
+ 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;
+ X509 *client_cert = NULL;
+ krb5_data *der_req = NULL;
+ char *client_principal = NULL;
+ char *server_principal = NULL;
+ char *filename = NULL;
+ STACK_OF(X509) *trusted_CAs = NULL;
+ X509 *kdc_cert = NULL;
+#if 0
+ krb5_timestamp time_now;
+#endif
+
+ pkiDebug("pa_pkinit_gen_req: enctype = %d\n", *etype);
+ cksum.contents = NULL;
+
+ /* If we don't have a client cert, we're done */
+ if (request->client == NULL) {
+ pkiDebug("No request->client; aborting PKINIT\n");
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+ retval = krb5_unparse_name(context, request->client, &client_principal);
+ if (retval)
+ goto cleanup;
+
+ if (using_pkcs11()) {
+#ifndef WITHOUT_PKCS11
+ if (pkinit_open_session(context, prompter, prompter_data)) {
+ pkiDebug("can't open pkcs11 session\n");
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+#endif
+ } else {
+ if (get_filename(&filename, "X509_USER_CERT", 0) != 0) {
+ pkiDebug("failed to get user's cert\n");
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+ }
+
+ retval = pkinit_get_client_cert(client_principal, filename, &client_cert);
+ if (filename != NULL)
+ free(filename);
+ free(client_principal);
+ if (retval) {
+ pkiDebug("No client cert; aborting PKINIT\n");
+ return retval;
+ }
+
+ /* look for optional CA list */
+ if (get_filename(&filename, "X509_CA_BUNDLE", 1) != 0) {
+ pkiDebug("didn't find trusted certifiers ca bundle file."
+ "optional trustedCertifiers are not included in AS_REQ."
+ "set X509_CA_BUNDLE environment variable.\n");
+ } else {
+ retval = load_trusted_certifiers(&trusted_CAs, filename);
+ if (filename != NULL)
+ free(filename);
+ }
+
+ /* checksum of the encoded KDC-REQ-BODY */
+ retval = 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;
+
+ retval = krb5_us_timeofday(context, &ctsec, &cusec);
+ if (retval)
+ goto cleanup;
+
+#if 0
+ krb5_timeofday(context, &time_now);
+ nonce = (krb5_int32) time_now;
+#else
+ /* 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;
+#endif
+ retval = pkinit_as_req_create(context, plgctx, reqctx, in_padata->pa_type,
+ ctsec, cusec, nonce, &cksum, request->server,
+ client_cert, trusted_CAs, kdc_cert, &out_data);
+ if (retval || !out_data->length) {
+ pkiDebug("error %d on pkinit_as_req_create; aborting PKINIT\n",
+ (int) retval);
+ goto cleanup;
+ }
+ *out_padata = (krb5_pa_data *) malloc(sizeof(krb5_pa_data));
+ if (*out_padata == NULL) {
+ retval = ENOMEM;
+ free(out_data->data);
+ free(out_data);
+ goto cleanup;
+ }
+ (*out_padata)->magic = KV5M_PA_DATA;
+#if 1
+ if (in_padata->pa_type == KRB5_PADATA_PK_AS_REQ_OLD)
+ (*out_padata)->pa_type = KRB5_PADATA_PK_AS_REP_OLD;
+ else (*out_padata)->pa_type = in_padata->pa_type;
+#else
+ (*out_padata)->pa_type = in_padata->pa_type;
+#endif
+ (*out_padata)->length = out_data->length;
+ (*out_padata)->contents = (krb5_octet *) out_data->data;
+ retval = 0;
+
+ cleanup:
+ if (client_cert != NULL)
+ X509_free(client_cert);
+
+ if (der_req != NULL)
+ krb5_free_data(context, der_req);
+
+ if (server_principal != NULL)
+ free(server_principal);
+
+ if (trusted_CAs != NULL)
+ sk_X509_pop_free(trusted_CAs, X509_free);
+
+ if (kdc_cert != NULL)
+ X509_free(kdc_cert);
+
+ if (out_data != NULL)
+ free(out_data);
+
+ return retval;
+}
+
+krb5_error_code
+pkinit_as_req_create(krb5_context context,
+ pkinit_context *plgctx,
+ pkinit_req_context *reqctx,
+ krb5_preauthtype pa_type,
+ krb5_timestamp ctsec,
+ krb5_int32 cusec,
+ krb5_ui_4 nonce,
+ const krb5_checksum * cksum,
+ krb5_principal server,
+ X509 * cert,
+ STACK_OF(X509) * trusted_CAs,
+ X509 * kdc_cert,
+ 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 = DH_PROTOCOL;
+ char *filename = NULL;
+#ifdef KDC_CERT
+ X509 *kdc_cert = NULL;
+ char *kdc_filename = NULL;
+#endif
+
+ pkiDebug("pkinit_as_req_create pa_type = %d\n", pa_type);
+
+ /* Create the authpack */
+ switch((int)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 = 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->supportedCMSTypes = NULL;
+ auth_pack->clientDHNonce.length = 0;
+ auth_pack->clientPublicValue = info;
+ break;
+ default:
+ pkiDebug("as_req: unrecognized pa_type = %d\n",
+ (int)pa_type);
+ retval = -1;
+ goto cleanup;
+ }
+
+ switch(protocol) {
+ case DH_PROTOCOL:
+ pkiDebug("as_req: DH key transport algorithm\n");
+ info->algorithm.algorithm = dh_oid;
+
+ /* create client-side DH keys */
+ if ((retval = client_create_dh(&reqctx->dh,
+ &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)pa_type) {
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ auth_pack9->clientPublicValue = NULL;
+ break;
+ case KRB5_PADATA_PK_AS_REQ:
+ 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)pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ retval = encode_krb5_auth_pack(auth_pack, &coded_auth_pack);
+ break;
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ retval = 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(coded_auth_pack->data, coded_auth_pack->length, "/tmp/client_auth_pack");
+#endif
+
+ if (!using_pkcs11() && get_filename(&filename, "X509_USER_KEY", 0) != 0) {
+ pkiDebug("failed to get user key filename\n");
+ retval = -1;
+ goto cleanup;
+ }
+
+ /* create PKCS7 object from authpack */
+ switch((int)pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ init_krb5_pa_pk_as_req(&req);
+ if (req == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ retval = pkcs7_signeddata_create(coded_auth_pack->data,
+ coded_auth_pack->length, &req->signedAuthPack.data,
+ &req->signedAuthPack.length, cert, filename,
+ plgctx->id_pkinit_authData, context);
+ break;
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ init_krb5_pa_pk_as_req_draft9(&req9);
+ if (req9 == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ retval = pkcs7_signeddata_create(coded_auth_pack->data,
+ coded_auth_pack->length, &req9->signedAuthPack.data,
+ &req9->signedAuthPack.length, cert, filename,
+ plgctx->id_pkinit_authData9, context);
+ break;
+ }
+ krb5_free_data(context, coded_auth_pack);
+ if (filename != NULL)
+ free(filename);
+ if (retval) {
+ pkiDebug("failed to create pkcs7 signed data\n");
+ goto cleanup;
+ }
+
+ /* create a list of trusted CAs */
+#ifdef KDC_CERT
+ get_filename(&kdc_filename, "KDC_CERT", 1);
+ if (kdc_filename == NULL)
+ goto cleanup;
+ kdc_cert = get_cert(kdc_filename);
+#endif
+
+ switch((int)pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+#if 1
+ if (trusted_CAs) {
+ create_krb5_trustedCertifiers(trusted_CAs,
+ &req->trustedCertifiers);
+ }
+#endif
+#ifdef KDC_CERT
+ retval = create_issuerAndSerial(kdc_cert, &req->kdcPkId.data,
+ &req->kdcPkId.length);
+ if (retval)
+ goto cleanup;
+#endif
+ /* Encode the as-req */
+ retval = encode_krb5_pa_pk_as_req(req, as_req);
+ break;
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ if (trusted_CAs) {
+#if 0
+ create_krb5_trustedCas(trusted_CAs, 1, &req9->trustedCertifiers);
+#endif
+ }
+#ifdef KDC_CERT
+ retval = create_issuerAndSerial(kdc_cert, &req9->kdcCert.data,
+ &req9->kdcCert.length);
+ if (retval)
+ goto cleanup;
+#endif
+ /* Encode the as-req */
+ retval = encode_krb5_pa_pk_as_req_draft9(req9, as_req);
+ break;
+ }
+#ifdef DEBUG_ASN1
+ print_buffer_bin((*as_req)->data, (*as_req)->length, "/tmp/client_as_req");
+#endif
+
+cleanup:
+ switch((int)pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ free_krb5_pa_pk_as_req(&req);
+ free_krb5_auth_pack(&auth_pack);
+ 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;
+}
+
+static krb5_error_code
+create_krb5_trustedCas(STACK_OF(X509) * sk,
+ int flag,
+ krb5_trusted_ca *** ids)
+{
+ krb5_error_code retval = ENOMEM;
+ int i = 0, len = 0, sk_size = sk_X509_num(sk);
+ krb5_trusted_ca **krb5_cas = NULL;
+ X509 *x = NULL;
+ char buf[256];
+ X509_NAME *xn = NULL;
+ unsigned char *p = NULL;
+ PKCS7_ISSUER_AND_SERIAL *is = NULL;
+
+ *ids = NULL;
+ 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] = 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, 256);
+ 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 =
+ 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 =
+ 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
+pa_pkinit_parse_rep(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_enctype etype,
+ krb5_keyblock * as_key)
+{
+ krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ krb5_data asRep;
+ X509 *client_cert = NULL;
+ char *princ_name = NULL;
+ char *filename = 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.
+ */
+ *out_padata = NULL;
+ if ((in_padata == NULL) || (in_padata->length == 0)) {
+ pkiDebug("pa_pkinit_parse_rep: no in_padata\n");
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+
+ /* If we don't have a client cert, we're done */
+ if (request->client == NULL) {
+ pkiDebug("No request->client; aborting PKINIT\n");
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+ pkiDebug("pa_pkinit_parse_rep: enctype = %d\n", etype);
+ retval = krb5_unparse_name(context, request->client, &princ_name);
+ if (retval)
+ return retval;
+
+ if (!using_pkcs11() && get_filename(&filename, "X509_USER_CERT", 0) != 0) {
+ pkiDebug("failed to get user's cert\n");
+ retval = -1;
+ goto cleanup;
+ }
+ retval = pkinit_get_client_cert(princ_name, filename, &client_cert);
+ if (filename != NULL)
+ free(filename);
+ if (retval) {
+ pkiDebug("No client cert; aborting PKINIT\n");
+ goto cleanup;
+ }
+
+ 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, client_cert, as_key,
+ etype, request->server);
+ if (retval) {
+ pkiDebug("pkinit_as_rep_parse returned %d\n", (int) retval);
+ goto cleanup;
+ }
+
+ retval = 0;
+
+cleanup:
+ if (princ_name)
+ free(princ_name);
+ if (client_cert != NULL)
+ X509_free(client_cert);
+
+ 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,
+ X509 * client_cert,
+ krb5_keyblock * key_block,
+ krb5_enctype etype,
+ krb5_principal server)
+{
+ 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_data dh_data = { 0, 0, NULL };
+ X509 *kdc_cert = NULL;
+ unsigned char *client_key = NULL, *data_der = NULL;
+ int client_key_len = 0;
+ long der_len = 0;
+ char *filename = NULL;
+ krb5_data *der_req = NULL;
+ krb5_checksum cksum = {0, 0, 0, NULL};
+ int num_types = 0;
+ krb5_cksumtype *cksum_types = NULL;
+ int i = 0;
+ krb5_principal tmp_server;
+
+ assert((as_rep != NULL) && (key_block != NULL));
+
+#ifdef DEBUG_ASN1
+ print_buffer_bin(as_rep->data, as_rep->length, "/tmp/client_as_rep");
+#endif
+
+ if ((retval = 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");
+ if ((retval = pkcs7_signeddata_verify(
+ kdc_reply->u.dh_Info.dhSignedData.data,
+ kdc_reply->u.dh_Info.dhSignedData.length,
+ &dh_data.data, &dh_data.length, &kdc_cert,
+ plgctx->id_pkinit_DHKeyData, context, plgctx)) != 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 (!using_pkcs11() && get_filename(&filename, "X509_USER_KEY", 0) != 0) {
+ pkiDebug("failed to get client's key filename\n");
+ goto cleanup;
+ }
+ if ((retval = pkcs7_envelopeddata_verify(
+ kdc_reply->u.encKeyPack.data,
+ kdc_reply->u.encKeyPack.length,
+ &dh_data.data, &dh_data.length, client_cert, filename,
+ pa_type, plgctx, &kdc_cert, context)) != 0) {
+ pkiDebug("failed to verify pkcs7 enveloped data\n");
+ goto cleanup;
+ }
+ if (filename != NULL)
+ free(filename);
+ break;
+ default:
+ pkiDebug("unknown as_rep type %d\n", kdc_reply->choice);
+ retval = -1;
+ goto cleanup;
+ }
+
+ if (plgctx->require_san) {
+ if (!verify_id_pkinit_san(kdc_cert, &tmp_server, context, pa_type,
+ plgctx)) {
+ pkiDebug("failed to verify id-pkinit-san\n");
+ retval = KRB5KDC_ERR_KDC_NOT_TRUSTED;
+ goto cleanup;
+ } else {
+ if (pa_type == KRB5_PADATA_PK_AS_REP && tmp_server != NULL) {
+ retval = krb5_principal_compare(context, server, tmp_server);
+ krb5_free_principal(context, tmp_server);
+ if (!retval) {
+ pkiDebug("identity in the certificate does not match "
+ "the requested principal\n");
+ retval = KRB5KDC_ERR_KDC_NAME_MISMATCH;
+ goto cleanup;
+ }
+ } else if (pa_type == KRB5_PADATA_PK_AS_REP_OLD) {
+ pkiDebug("need to check dnsName against KDC's hostname\n");
+ }
+ }
+ }
+
+ if (!verify_id_pkinit_eku(kdc_cert, pa_type, plgctx)) {
+ pkiDebug("failed to verify id-pkinit-KPKdc\n");
+ retval = KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE;
+ goto cleanup;
+ }
+
+ 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 = decode_krb5_kdc_dh_key_info(&dh_data,
+ &kdc_dh)) != 0) {
+ pkiDebug("failed to decode kdc_dh_key_info\n");
+ goto cleanup;
+ }
+
+ if (der_decode_data(kdc_dh->subjectPublicKey.data,
+ kdc_dh->subjectPublicKey.length,
+ &data_der, &der_len) != 0) {
+ pkiDebug("failed to decode subjectPublicKey\n");
+ retval = -1;
+ goto cleanup;
+ }
+
+ /* client after KDC reply */
+ if ((retval = client_process_dh(reqctx->dh, data_der,
+ der_len, &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
+ switch ((int) pa_type) {
+ case KRB5_PADATA_PK_AS_REP:
+ if ((retval =
+ decode_krb5_reply_key_pack(&dh_data, &key_pack)) != 0) {
+ pkiDebug("failed to decode reply_key_pack\n");
+ goto cleanup;
+ }
+
+ retval = encode_krb5_as_req(request, &der_req);
+ if (retval) {
+ pkiDebug("encode_krb5_kdc_req_body returned %d\n", retval);
+ goto cleanup;
+ }
+
+ retval = krb5_c_keyed_checksum_types(context,
+ key_pack->replyKey.enctype, &num_types, &cksum_types);
+ for (i = 0; i < num_types; i++) {
+ retval = krb5_c_make_checksum(context, cksum_types[i],
+ &key_pack->replyKey,
+ KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM, der_req, &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("checksums match\n");
+ break;
+ }
+ else
+ if (cksum.contents != NULL)
+ free(cksum.contents);
+ }
+
+ if (i == num_types) {
+ pkiDebug("failed to match the checksums\n");
+ goto cleanup;
+ }
+ krb5_copy_keyblock_contents(context, &key_pack->replyKey,
+ key_block);
+ break;
+ case KRB5_PADATA_PK_AS_REP_OLD:
+ if ((retval =
+ decode_krb5_reply_key_pack_draft9(&dh_data,
+ &key_pack9)) != 0) {
+ pkiDebug("failed to decode reply_key_pack_draft9\n");
+ goto cleanup;
+ }
+ 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;
+ default:
+ pkiDebug("unrecognized pa_type = %d\n", pa_type);
+ retval = -1;
+ goto cleanup;
+ }
+
+ 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 (data_der != NULL)
+ free(data_der);
+ if (client_key != NULL)
+ free(client_key);
+ if (kdc_cert != NULL)
+ X509_free(kdc_cert);
+ free_krb5_kdc_dh_key_info(&kdc_dh);
+ free_krb5_pa_pk_as_rep(&kdc_reply);
+ switch((int)pa_type) {
+ case KRB5_PADATA_PK_AS_REP:
+ free_krb5_reply_key_pack(&key_pack);
+ if (der_req != NULL)
+ krb5_free_data(context, der_req);
+ if (cksum_types != NULL)
+ free(cksum_types);
+ if (cksum.contents != NULL)
+ free(cksum.contents);
+ break;
+ case KRB5_PADATA_PK_AS_REP_OLD:
+ free_krb5_reply_key_pack_draft9(&key_pack9);
+ break;
+ }
+
+ pkiDebug("pkinit_as_rep_parse retval=%d\n", (int) retval);
+ return retval;
+}
+
+krb5_error_code
+pkinit_client_process(krb5_context context,
+ void *plugin_context,
+ void *request_context,
+ 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 r = -1;
+ krb5_enctype enctype = -1;
+ krb5_data *cdata;
+ pkinit_context *plgctx = (pkinit_context *)plugin_context;
+ pkinit_req_context *reqctx = (pkinit_req_context *)request_context;
+
+ /*
+ * Get enctype of reply, if available. It won't be available
+ * if we were called to handle a request, so just ignore the
+ * error in that case. We check below that we have it if we
+ * really need it.
+ */
+ r = (*get_data_proc)(context, rock, krb5plugin_preauth_client_get_etype,
+ &cdata);
+ if (r != 0 && r != ENOENT)
+ return r;
+ if (r == 0) {
+ enctype = *((krb5_enctype *)cdata->data);
+ (*get_data_proc)(context, rock, krb5plugin_preauth_client_free_etype,
+ &cdata);
+ }
+ switch ((int) in_padata->pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ pkiDebug("processing KRB5_PADATA_PK_AS_REQ\n");
+ r = pa_pkinit_gen_req(context, plgctx, reqctx, request, in_padata,
+ out_padata, prompter, prompter_data, &enctype, as_key);
+ break;
+
+ case KRB5_PADATA_PK_AS_REP:
+ pkiDebug("processing KRB5_PADATA_PK_AS_REP\n");
+ if (enctype == -1)
+ return EINVAL; /* XXX */
+ r = pa_pkinit_parse_rep(context, plgctx, reqctx,
+ request, in_padata, out_padata, enctype,
+ as_key);
+ 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;
+ r = pa_pkinit_gen_req(context, plgctx, reqctx, request,
+ in_padata, out_padata, prompter, prompter_data,
+ &enctype, as_key);
+ } else {
+ pkiDebug("processing KRB5_PADATA_PK_AS_REP_OLD\n");
+ if (enctype == -1)
+ return EINVAL; /* XXX */
+ in_padata->pa_type = KRB5_PADATA_PK_AS_REP_OLD;
+ r = pa_pkinit_parse_rep(context, plgctx, reqctx,
+ request, in_padata, out_padata,
+ enctype, as_key);
+ }
+ break;
+ default:
+ pkiDebug("unrecognized patype = %d for PKINIT\n",
+ in_padata->pa_type);
+ }
+ return r;
+}
+
+krb5_error_code
+pkinit_client_tryagain(krb5_context context,
+ void *plugin_context,
+ void *request_context,
+ 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)
+{
+ pkiDebug("%s: DUMMY version called!\n", __FUNCTION__);
+#ifdef DEBUG_ASN1
+ print_buffer_bin(err_reply->e_data.data, err_reply->e_data.length, "/tmp/client_edata");
+#endif
+ return 0;
+}
+
+#define PKINIT_REQ_CTX_MAGIC 0xdeadbeef
+void
+pkinit_client_req_init(krb5_context contex,
+ void *plugin_context,
+ void **request_context)
+{
+
+ pkinit_req_context *reqctx;
+
+ *request_context = NULL;
+
+ reqctx = (pkinit_req_context *) malloc(sizeof(*reqctx));
+ if (reqctx == NULL)
+ return;
+
+ reqctx->magic = PKINIT_REQ_CTX_MAGIC;
+ reqctx->version = 0;
+ reqctx->dh = NULL;
+
+ *request_context = (void *) reqctx;
+ pkiDebug("%s: returning reqctx at %p\n", __FUNCTION__, reqctx);
+ return;
+}
+
+void
+pkinit_client_req_fini(krb5_context context,
+ void *plugin_context,
+ void *request_context)
+{
+ pkinit_req_context *reqctx = (pkinit_req_context *)request_context;
+
+ pkiDebug("%s: received reqctx at %p\n", __FUNCTION__, reqctx);
+#ifndef WITHOUT_PKCS11
+ pkinit_close_session();
+#endif
+ if (reqctx != NULL) {
+ if (reqctx->magic != PKINIT_REQ_CTX_MAGIC) {
+ pkiDebug("%s: Bad magic value (%x) in req ctx\n",
+ __FUNCTION__, reqctx->magic);
+ return;
+ }
+ if (reqctx->dh != NULL) {
+ DH_free(reqctx->dh);
+ }
+ free(reqctx);
+ }
+ return;
+}
+
+static int
+using_pkcs11(void)
+{
+#ifdef WITHOUT_PKCS11
+ return 0;
+#else
+ return (getenv("PKCS11") != NULL);
+#endif
+}
+
+#ifndef WITHOUT_PKCS11
+
+static char *module_name = "opensc-pkcs11.so";
+static void *module = NULL;
+static int slotid = 0;
+static CK_SESSION_HANDLE session = CK_INVALID_HANDLE;
+static CK_FUNCTION_LIST_PTR p11 = NULL;
+
+void *
+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);
+ 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;
+}
+
+CK_RV
+C_UnloadModule(void *handle)
+{
+ dlclose(handle);
+ return CKR_OK;
+}
+
+static krb5_error_code
+pkinit_open_session(krb5_context context,
+ krb5_prompter_fct prompter,
+ void *prompter_data)
+{
+ int r;
+ char *s;
+ char pin[32];
+ char custom_module_name[100];
+ krb5_data response_data;
+ krb5_prompt kprompt;
+ krb5_prompt_type prompt_type;
+
+ if (module != NULL)
+ return 0;
+
+ /* Temporary pending use of krb5.conf */
+ if ((s = getenv("PKCS11")) != NULL && strlen(s) > 0) {
+ if (sscanf(s, "%99[^:]:%d", custom_module_name, &slotid) < 2)
+ slotid = atoi(s);
+ else
+ module_name = custom_module_name;
+ }
+
+ /* Load module, init, and open session */
+ module = C_LoadModule(module_name, &p11);
+ if (module == NULL) {
+ pkiDebug("fail C_LoadModule\n");
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+ if ((r = p11->C_Initialize(NULL)) != CKR_OK) {
+ pkiDebug("fail C_Initialize %x\n", r);
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+ pkiDebug("opening slot %d\n", slotid);
+ if ((r = p11->C_OpenSession(slotid, CKF_SERIAL_SESSION, NULL, NULL, &session)) !=
+ CKR_OK) {
+ pkiDebug("fail C_OpenSession %x\n", r);
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+
+ response_data.data = pin;
+ response_data.length = sizeof(pin);
+
+ kprompt.prompt = "PIN";
+ kprompt.hidden = 1;
+ kprompt.reply = &response_data;
+ prompt_type = KRB5_PROMPT_TYPE_PREAUTH;
+
+ /* PROMPTER_INVOCATION */
+ krb5int_set_prompt_types(context, &prompt_type);
+ if ((r = ((*prompter)(context, prompter_data, NULL, NULL, 1, &kprompt)))) {
+ krb5int_set_prompt_types(context, 0);
+ return r;
+ }
+ krb5int_set_prompt_types(context, 0);
+
+ if ((r = p11->C_Login(session, CKU_USER, (u_char *) pin, response_data.length)) != CKR_OK) {
+ pkiDebug("fail C_Login %x\n", r);
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+
+ return 0;
+}
+
+static void
+pkinit_close_session(void)
+{
+ if (p11) {
+ if (session) {
+ p11->C_CloseSession(session);
+ session = CK_INVALID_HANDLE;
+ }
+ p11->C_Finalize(NULL_PTR);
+ p11 = NULL;
+ }
+ if (module) {
+ C_UnloadModule(module);
+ module = NULL;
+ }
+}
+#endif
+
+krb5_error_code
+pkinit_get_client_cert(const char *principal,
+ char *filename,
+ X509 ** client_cert)
+{
+#ifndef WITHOUT_PKCS11
+ CK_OBJECT_CLASS cls;
+ CK_OBJECT_HANDLE object;
+ CK_ATTRIBUTE attrs[2];
+ CK_ULONG count, r;
+ CK_CERTIFICATE_TYPE certtype;
+ krb5_octet cert[4096], *cp;
+ const unsigned char *p = cp;
+#endif
+
+ if (!using_pkcs11()) {
+ if ((*client_cert = get_cert(filename)) == NULL)
+ return -1;
+ else
+ return 0;
+ }
+
+#ifndef WITHOUT_PKCS11
+ if (principal == NULL) {
+ return KRB5_PRINC_NOMATCH;
+ }
+
+ pkiDebug("Reading cert for '%s' from card\n", principal);
+
+ 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;
+
+ if (p11->C_FindObjectsInit(session, attrs, 2) != CKR_OK) {
+ pkiDebug("fail C_FindObjectsInit\n");
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+
+ /* Look for x.509 cert */
+ if ((r = p11->C_FindObjects(session, &object, 1, &count)) != CKR_OK
+ || count != 1) {
+ pkiDebug("can't find any certs %x\n", r);
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+ p11->C_FindObjectsFinal(session);
+
+ /*
+ * Read the cert off the card.
+ * I can't find any way to realiably get the size before doing the read.
+ */
+ attrs[0].type = CKA_VALUE;
+ attrs[0].pValue = cert;
+ attrs[0].ulValueLen = sizeof cert;
+ if ((r = p11->C_GetAttributeValue(session, object, attrs, 1)) != CKR_OK) {
+ pkiDebug("fail C_GetAttributeValue %x\n", r);
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+
+ pkiDebug("cert size %d\n", (int) attrs[0].ulValueLen);
+ p = cp = malloc(attrs[0].ulValueLen);
+ if (cp == NULL)
+ return ENOMEM;
+ memcpy(cp, cert, attrs[0].ulValueLen);
+ *client_cert = d2i_X509(NULL, &p, attrs[0].ulValueLen);
+
+#endif
+ return 0;
+}
+
+krb5_error_code
+pkinit_sign_data(unsigned char *data,
+ int data_len,
+ unsigned char **sig,
+ int *sig_len,
+ char *filename)
+{
+#ifndef WITHOUT_PKCS11
+ CK_OBJECT_CLASS cls;
+ CK_OBJECT_HANDLE object;
+ CK_ATTRIBUTE attrs[1];
+ CK_ULONG count, len, r;
+ CK_MECHANISM mech;
+ unsigned char sigbuf[1024], *cp;
+#endif
+
+ if (!using_pkcs11()) {
+ if (create_signature(sig, sig_len, data, data_len, filename) != 0) {
+ pkiDebug("failed to create the signature\n");
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+ return 0;
+ }
+
+#ifndef WITHOUT_PKCS11
+ /* Find the private key - we just take the first one for now */
+ cls = CKO_PRIVATE_KEY;
+ attrs[0].type = CKA_CLASS;
+ attrs[0].pValue = &cls;
+ attrs[0].ulValueLen = sizeof cls;
+
+ if (p11->C_FindObjectsInit(session, attrs, 1) != CKR_OK) {
+ pkiDebug("krb5_pkinit_sign_data: fail C_FindObjectsInit\n");
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+
+ if ((r = p11->C_FindObjects(session, &object, 1, &count)) != CKR_OK || count < 1) {
+ pkiDebug("can't find any keys %x\n", (int) r);
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+ pkiDebug("Got a private key!\n");
+ p11->C_FindObjectsFinal(session);
+
+ mech.mechanism = CKM_SHA1_RSA_PKCS;
+ mech.pParameter = NULL;
+ mech.ulParameterLen = 0;
+
+ if ((r = p11->C_SignInit(session, &mech, object)) != CKR_OK) {
+ pkiDebug("fail C_SignInit %x\n", (int) r);
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+
+ len = sizeof sigbuf;
+ pkiDebug("signing %d -> %d\n", (int) data_len, (int) len);
+ if ((r = p11->C_Sign(session, (krb5_octet *) data, (u_int) data_len, sigbuf, &len)) !=
+ CKR_OK) {
+ pkiDebug("fail C_Sign %x\n", (int) r);
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+ pkiDebug("sig size %d\n", (int) len);
+ cp = malloc(len);
+ if (cp == NULL)
+ return ENOMEM;
+ memcpy(cp, sigbuf, len);
+ *sig_len = len;
+ *sig = cp;
+ return 0;
+#endif
+}
+
+static krb5_error_code
+client_create_dh(DH ** dh_client,
+ unsigned char **dh_params,
+ int *dh_params_len,
+ unsigned char **dh_pubkey, int *dh_pubkey_len)
+{
+ krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ int bufsize = 0;
+ unsigned char *buf = NULL;
+ int dh_err = 0;
+ ASN1_INTEGER *p = NULL, *g = NULL, *q = NULL, *pub_key;
+ int r = 0;
+
+ if ((*dh_client = DH_new()) == NULL)
+ goto cleanup;
+ if (((*dh_client)->g = BN_new()) == NULL ||
+ ((*dh_client)->q = BN_new()) == NULL)
+ goto cleanup;
+#if 1
+ (*dh_client)->p = get_rfc2409_prime_1024(NULL);
+#else
+ (*dh_client)->p = BN_bin2bn(pkinit_1024_dhprime,
+ sizeof(pkinit_1024_dhprime), NULL);
+#endif
+
+ BN_set_word(((*dh_client)->g), DH_GENERATOR_2);
+ BN_rshift1((*dh_client)->q, (*dh_client)->p);
+ DH_generate_key(*dh_client);
+ DH_check(*dh_client, &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(*dh_client, "client's DH params\n");
+ print_pubkey((*dh_client)->pub_key, "client's pub_key=");
+#endif
+
+ DH_check_pub_key(*dh_client, (*dh_client)->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.
+ */
+ if ((p = BN_to_ASN1_INTEGER((*dh_client)->p, NULL)) == NULL)
+ goto cleanup;
+ if ((g = BN_to_ASN1_INTEGER((*dh_client)->g, NULL)) == NULL)
+ goto cleanup;
+ if ((q = BN_to_ASN1_INTEGER((*dh_client)->q, NULL)) == NULL)
+ goto cleanup;
+
+ bufsize = i2d_ASN1_INTEGER(p, NULL);
+ bufsize += i2d_ASN1_INTEGER(g, NULL);
+ bufsize += i2d_ASN1_INTEGER(q, NULL);
+
+ r = ASN1_object_size(1, bufsize, V_ASN1_SEQUENCE);
+ buf = *dh_params = malloc((size_t) r);
+ if (buf == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ ASN1_put_object(&buf, 1, bufsize, V_ASN1_SEQUENCE, V_ASN1_UNIVERSAL);
+
+ i2d_ASN1_INTEGER(p, &buf);
+ i2d_ASN1_INTEGER(g, &buf);
+ i2d_ASN1_INTEGER(q, &buf);
+
+ *dh_params_len = r;
+
+ if (p != NULL)
+ ASN1_INTEGER_free(p);
+ if (g != NULL)
+ ASN1_INTEGER_free(g);
+ if (q != NULL)
+ ASN1_INTEGER_free(q);
+
+ /* 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_client)->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 (*dh_client != NULL)
+ DH_free(*dh_client);
+ if (*dh_params != NULL)
+ free(*dh_params);
+ if (*dh_pubkey != NULL)
+ free(*dh_pubkey);
+ if (p != NULL)
+ ASN1_INTEGER_free(p);
+ if (g != NULL)
+ ASN1_INTEGER_free(g);
+ if (q != NULL)
+ ASN1_INTEGER_free(q);
+ if (pub_key != NULL)
+ ASN1_INTEGER_free(pub_key);
+
+ return retval;
+}
+
+static krb5_error_code
+client_process_dh(DH * dh_client,
+ unsigned char *data,
+ long data_len,
+ unsigned char **client_key, 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 = data;
+
+ *client_key_len = DH_size(dh_client);
+ if ((*client_key = (unsigned char *)
+ malloc((size_t) *client_key_len)) == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ 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, dh_client);
+#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);
+
+ return retval;
+
+ cleanup:
+ if (*client_key != NULL)
+ free(*client_key);
+ if (pub_key != NULL)
+ ASN1_INTEGER_free(pub_key);
+ return retval;
+}
+
+static krb5_error_code
+der_encode_data(unsigned char *data,
+ int data_len, unsigned char **out, long *out_len)
+{
+ krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ ASN1_OCTET_STRING *s = NULL;
+ unsigned char *p = NULL;
+
+ if ((s = ASN1_OCTET_STRING_new()) == NULL)
+ goto cleanup;
+ if (!ASN1_STRING_set(s, data, data_len))
+ goto cleanup;
+ *out_len = i2d_ASN1_OCTET_STRING(s, NULL);
+ if ((p = *out = (unsigned char *) malloc((size_t) *out_len)) == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ i2d_ASN1_OCTET_STRING(s, &p);
+
+ retval = 0;
+ cleanup:
+ if (s != NULL)
+ ASN1_OCTET_STRING_free(s);
+
+ return retval;
+}
+
+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;
+}
+
+
+
+krb5_error_code
+create_issuerAndSerial(X509 *cert,
+ unsigned char **out,
+ int *out_len)
+{
+ unsigned char *p = NULL;
+ PKCS7_ISSUER_AND_SERIAL *is = NULL;
+ int len = 0;
+ krb5_error_code retval = ENOMEM;
+
+ 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 = 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 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;
+}
+
+static 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);
+}
+
+static 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);
+}
+
+static int
+pkinit_client_get_flags(krb5_context kcontext, krb5_preauthtype patype)
+{
+#if 0
+ switch (patype) {
+ case KRB5_PADATA_PK_AS_REP:
+ case KRB5_PADATA_PK_AS_REP_OLD:
+ return PA_INFO;
+ break;
+ case KRB5_PADATA_PK_AS_REQ:
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ return PA_REAL;
+ break;
+ }
+ return 0;
+#else
+ return PA_REAL;
+#endif
+}
+
+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
+};
+
+struct krb5plugin_preauth_client_ftable_v0 preauthentication_client_0 = {
+ "pkinit", /* name */
+ supported_client_pa_types, /* pa_type_list */
+ NULL, /* enctype_list */
+ pkinit_lib_init, /* (*init) */
+ pkinit_lib_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) */
+};
diff --git a/src/plugins/preauth/pkinit/pkinit_lib.c b/src/plugins/preauth/pkinit/pkinit_lib.c
new file mode 100644
index 0000000..5b6cc1f
--- /dev/null
+++ b/src/plugins/preauth/pkinit/pkinit_lib.c
@@ -0,0 +1,1776 @@
+/*
+ * COPYRIGHT (C) 2006
+ * 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 <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+#include <openssl/pkcs7.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/dh.h>
+#include <openssl/asn1_mac.h>
+#include <openssl/sha.h>
+#include <openssl/asn1.h>
+#include <openssl/pem.h>
+
+#ifndef WITHOUT_PKCS11
+#include <opensc/pkcs11.h>
+#endif
+
+#include <krb5/preauth_plugin.h>
+#include <k5-int-pkinit.h>
+#include "pkinit.h"
+
+#define FAKECERT
+#ifdef DEBUG
+#define pkiDebug(args...) printf(args)
+#else
+#define pkiDebug(args...)
+#endif
+
+#define PKINIT_CTX_MAGIC 0x05551212
+
+/*
+ * Custom OIDS to specify as eContentType
+ */
+unsigned dh_oid_num[6] = { 1, 2, 840, 10046, 2, 1 };
+const krb5_octet_data dh_oid = { 0, 7, "\x2A\x86\x48\xce\x3e\x02\x01" };
+
+/* 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 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 *);
+
+/* This handy macro borrowed from crypto/x509v3/v3_purp.c */
+#define ku_reject(x, usage) \
+ (((x)->ex_flags & EXFLAG_KUSAGE) && !((x)->ex_kusage & (usage)))
+
+krb5_error_code
+pkinit_lib_init(krb5_context context, void **blob)
+{
+ pkinit_context *plgctx;
+ krb5_error_code retval = ENOMEM;
+ int tmp = 0;
+
+ plgctx = (pkinit_context *) malloc(sizeof(*plgctx));
+ if (plgctx == NULL)
+ goto out;
+
+ plgctx->magic = PKINIT_CTX_MAGIC;
+ plgctx->version = 0;
+ plgctx->require_eku = 0;
+ plgctx->require_san = 0;
+ plgctx->allow_upn = 0;
+ plgctx->require_crl_checking = 0;
+
+ tmp = OBJ_create("1.3.6.1.5.2.2", "id-pkinit-san", "KRB5PrincipalName");
+ if (tmp == NID_undef)
+ goto out;
+ plgctx->id_pkinit_san = OBJ_nid2obj(tmp);
+
+ tmp = OBJ_create("1.3.6.1.5.2.3.1", "id-pkinit-authdata",
+ "PKINIT signedAuthPack");
+ if (tmp == NID_undef)
+ goto out;
+ plgctx->id_pkinit_authData = OBJ_nid2obj(tmp);
+
+ tmp = OBJ_create("1.3.6.1.5.2.3.2", "id-pkinit-DHKeyData",
+ "PKINIT dhSignedData");
+ if (tmp == NID_undef)
+ goto out;
+ plgctx->id_pkinit_DHKeyData = OBJ_nid2obj(tmp);
+
+ tmp = OBJ_create("1.3.6.1.5.2.3.3", "id-pkinit-rkeyData",
+ "PKINIT encKeyPack");
+ if (tmp == NID_undef)
+ goto out;
+ plgctx->id_pkinit_rkeyData = OBJ_nid2obj(tmp);
+
+ tmp = OBJ_create("1.3.6.1.5.2.3.4", "id-pkinit-KPClientAuth",
+ "PKINIT Client EKU");
+ if (tmp == NID_undef)
+ goto out;
+ plgctx->id_pkinit_KPClientAuth = OBJ_nid2obj(tmp);
+
+ tmp = OBJ_create("1.3.6.1.5.2.3.5", "id-pkinit-KPKdc", "KDC EKU");
+ if (tmp == NID_undef)
+ goto out;
+ plgctx->id_pkinit_KPKdc = OBJ_nid2obj(tmp);
+ tmp = OBJ_create("1.2.840.113549.1.7.1", "id-data",
+ "CMS id-data");
+ if (tmp == NID_undef)
+ goto out;
+ plgctx->id_pkinit_authData9 = OBJ_nid2obj(tmp);
+ tmp = OBJ_create("1.3.6.1.4.1.311.20.2.3", "id-pkinit-san draft9",
+ "KRB5PrincipalName draft9");
+ if (tmp == NID_undef)
+ goto out;
+ plgctx->id_pkinit_san9 = OBJ_nid2obj(tmp);
+ tmp = OBJ_create("1.3.6.1.4.1.311.20.2.2", "id-ms-kp-sc-logon EKU",
+ "KDC/Client EKU draft9");
+ if (tmp == NID_undef)
+ goto out;
+ plgctx->id_ms_kp_sc_logon = OBJ_nid2obj(tmp);
+ tmp = OBJ_create("1.3.6.1.5.5.7.3.1", "id-kp-serverAuth EKU",
+ "KDC EKU draft9");
+ if (tmp == NID_undef)
+ goto out;
+ plgctx->id_kp_serverAuth = OBJ_nid2obj(tmp);
+
+ *blob = (void *) plgctx;
+ pkiDebug("%s: returning plgctx at %p\n", __FUNCTION__, plgctx);
+
+ /* initialize openssl routines */
+ openssl_init();
+ retval = 0;
+
+ out:
+ return retval;
+}
+
+void
+pkinit_lib_fini(krb5_context context, void *blob)
+{
+ pkinit_context *plgctx;
+
+ plgctx = (pkinit_context *) blob;
+
+ OBJ_cleanup();
+ pkiDebug("%s: got plgctx at %p\n", __FUNCTION__, plgctx);
+ if (plgctx == NULL || plgctx->magic != PKINIT_CTX_MAGIC) {
+ pkiDebug("pkinit_lib_fini: got bad plgctx (%p)!\n", plgctx);
+ return;
+ }
+ free(plgctx);
+}
+
+void
+openssl_init()
+{
+ static int did_init;
+
+ if (!did_init) {
+ /* initialize openssl routines */
+ CRYPTO_malloc_init();
+ ERR_load_crypto_strings();
+ OpenSSL_add_all_algorithms();
+ did_init++;
+ }
+}
+
+static int
+purpose_print(BIO * bio, X509 * cert, X509_PURPOSE * pt)
+{
+ int id, i, idret;
+ char *pname;
+ id = X509_PURPOSE_get_id(pt);
+ pname = X509_PURPOSE_get0_name(pt);
+ for (i = 0; i < 2; i++) {
+ idret = X509_check_purpose(cert, id, i);
+ BIO_printf(bio, "%s%s : ", pname, i ? " CA" : "");
+ if (idret == 1)
+ BIO_printf(bio, "Yes\n");
+ else if (idret == 0)
+ BIO_printf(bio, "No\n");
+ else
+ BIO_printf(bio, "Yes (WARNING code=%d)\n", idret);
+ }
+ return 1;
+}
+
+static int
+openssl_callback(int ok, X509_STORE_CTX * ctx)
+{
+#ifdef DEBUG
+ if (!ok) {
+ X509 *cert = ctx->current_cert;
+ char buf[256];
+
+ X509_NAME_oneline(X509_get_subject_name(ctx->current_cert), buf, 256);
+ 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;
+}
+
+krb5_error_code
+pkcs7_signeddata_create(unsigned char *data,
+ int data_len,
+ unsigned char **signed_data,
+ int *signed_data_len,
+ X509 * cert,
+ char *filename,
+ ASN1_OBJECT *oid,
+ krb5_context context)
+{
+ 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;
+ const EVP_MD *md_tmp;
+ unsigned char md_data[EVP_MAX_MD_SIZE], *abuf = NULL;
+ unsigned int md_len, alen;
+ STACK_OF(X509_ATTRIBUTE) * sk;
+ unsigned char *sig = NULL;
+ int sig_len = 0;
+
+ /* 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, 1))
+ goto cleanup;
+
+ /* create a cert chain */
+ if ((cert_stack = sk_X509_new_null()) == NULL)
+ goto cleanup;
+ p7s->cert = cert_stack;
+ sk_X509_push(cert_stack, X509_dup(cert));
+
+ /* 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_rsaEncryption);
+ if (!(p7si->digest_enc_alg->parameter = ASN1_TYPE_new()))
+ goto cleanup;
+ p7si->digest_enc_alg->parameter->type = V_ASN1_NULL;
+
+ /* 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, 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 cleanup;
+
+ retval = pkinit_sign_data(abuf, alen, &sig, &sig_len, filename);
+ free(abuf);
+ if (retval)
+ goto cleanup;
+
+ /* Add signature */
+ if (!ASN1_STRING_set(p7si->enc_digest, (unsigned char *) sig, 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 cleanup;
+ }
+ /* adder signer_info to pkcs7 signed */
+ if (!PKCS7_add_signer(p7, p7si))
+ goto cleanup;
+
+ /* start on adding data to the pkcs7 signed */
+ if ((inner_p7 = PKCS7_new()) == NULL)
+ goto cleanup;
+ if ((pkinit_data = ASN1_TYPE_new()) == NULL)
+ goto cleanup;
+ pkinit_data->type = V_ASN1_OCTET_STRING;
+ if ((pkinit_data->value.octet_string = ASN1_OCTET_STRING_new()) == NULL)
+ goto cleanup;
+ if (!ASN1_OCTET_STRING_set(pkinit_data->value.octet_string, data,
+ 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 cleanup;
+ }
+
+ if (!PKCS7_set0_type_other(inner_p7, OBJ_obj2nid(oid), pkinit_data))
+ goto cleanup;
+ 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 cleanup;
+ }
+ if ((p = *signed_data =
+ (unsigned char *) malloc(*signed_data_len)) == NULL)
+ goto cleanup;
+
+ /* 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 cleanup;
+ }
+ retval = 0;
+
+ cleanup:
+ if (p7 != NULL)
+ PKCS7_free(p7);
+ EVP_MD_CTX_cleanup(&ctx);
+ if (sig != NULL)
+ free(sig);
+
+ return retval;
+}
+
+krb5_error_code
+pkcs7_signeddata_verify(unsigned char *signed_data,
+ int signed_data_len,
+ char **data,
+ int *data_len,
+ X509 ** cert,
+ ASN1_OBJECT *oid,
+ krb5_context context,
+ pkinit_context *plgctx)
+{
+ krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ PKCS7 *p7 = NULL;
+ BIO *out = NULL;
+ X509_STORE *store = NULL;
+ X509_LOOKUP *lookup = NULL;
+ int flags = PKCS7_NOVERIFY, i = 0, size = 0;
+ int vflags = 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_CTX cert_ctx;
+ char *filename = NULL;
+
+ if (get_filename(&filename, "X509_CA_DIR", 1) != 0) {
+ pkiDebug("failed to get the name of the directory for trusted CAs\n");
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+
+ if ((p7 = d2i_PKCS7(NULL, &p, signed_data_len)) == NULL) {
+ unsigned long err = ERR_peek_error();
+ krb5_set_error_message(context, retval, "%s\n",
+ ERR_error_string(err, NULL));
+ goto cleanup;
+ }
+
+ if (OBJ_obj2nid(p7->type) != NID_pkcs7_signed) {
+ pkiDebug("Excepted id-signedData PKCS7 mgs (received type = %d)\n",
+ OBJ_obj2nid(p7->type));
+ krb5_set_error_message(context, retval, "wrong oid\n");
+ goto cleanup;
+ }
+
+ /* setup verify */
+ if (!(store = X509_STORE_new()))
+ goto cleanup;
+
+ if (!X509_STORE_load_locations(store, NULL, filename)) {
+ unsigned long err = ERR_peek_error();
+ pkiDebug("error loading CA files\n");
+ krb5_set_error_message(context, retval, "%s\n",
+ ERR_error_string(err, NULL));
+ goto cleanup;
+ }
+ vflags = X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL;
+ if (plgctx->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 cert from the chain */
+ 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;
+ if (!X509_STORE_CTX_init(&cert_ctx, store, x, p7->d.sign->cert))
+ goto cleanup;
+ i = X509_verify_cert(&cert_ctx);
+ if (i <= 0) {
+ int j = X509_STORE_CTX_get_error(&cert_ctx);
+ switch(j) {
+ case X509_V_ERR_CERT_REVOKED:
+ retval = KRB5KDC_ERR_REVOKED_CERTIFICATE;
+ 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;
+ }
+ pkiDebug("%s\n", X509_verify_cert_error_string(j));
+ krb5_set_error_message(context, retval, "%s\n",
+ X509_verify_cert_error_string(j));
+ }
+ X509_STORE_CTX_cleanup(&cert_ctx);
+ if (i <= 0)
+ goto cleanup;
+
+ out = BIO_new(BIO_s_mem());
+ if (PKCS7_verify(p7, NULL, store, NULL, out, flags)) {
+ if (!OBJ_cmp(p7->d.sign->contents->type, oid))
+ pkiDebug("PKCS7 Verification successful\n");
+ else {
+ pkiDebug("wrong oid in eContentType\n");
+ 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;
+ }
+
+ for (;;) {
+ 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;
+
+ *cert = X509_dup(x);
+
+ retval = 0;
+
+ cleanup:
+ if (p7 != NULL)
+ PKCS7_free(p7);
+ if (out != NULL)
+ BIO_free(out);
+ if (store != NULL)
+ X509_STORE_free(store);
+
+ return retval;
+}
+
+static int encode_signeddata(unsigned char *data, int data_len,
+ unsigned char **out, int *out_len) {
+
+ int size = 0, r = 0;
+ ASN1_OBJECT *oid;
+ unsigned char *p = NULL;
+
+ r = ASN1_object_size(1, data_len, V_ASN1_SEQUENCE);
+ oid = OBJ_nid2obj(NID_pkcs7_signed);
+ size = i2d_ASN1_OBJECT(oid, NULL);
+ size += r;
+
+ r = ASN1_object_size(1, size, V_ASN1_SEQUENCE);
+ p = *out = malloc(r);
+ if (p == NULL) return -1;
+ ASN1_put_object(&p, 1, size, V_ASN1_SEQUENCE, V_ASN1_UNIVERSAL);
+
+ i2d_ASN1_OBJECT(oid, &p);
+ ASN1_put_object(&p, 1, data_len, 0, V_ASN1_CONTEXT_SPECIFIC);
+ memcpy(p, data, data_len);
+
+ *out_len = r;
+
+ return 0;
+}
+
+krb5_error_code
+pkcs7_envelopeddata_verify(unsigned char *enveloped_data,
+ int enveloped_data_len,
+ char **data,
+ int *data_len,
+ X509 *client_cert,
+ char *key_filename,
+ krb5_preauthtype pa_type,
+ pkinit_context *plgctx,
+ X509 ** cert,
+ krb5_context context)
+{
+ krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ PKCS7 *p7 = NULL;
+ BIO *out = NULL;
+ int flags = PKCS7_BINARY, i = 0, size = 0;
+ const unsigned char *p = enveloped_data;
+ EVP_PKEY *pkey = NULL;
+
+ int tmp_buf_len = 0, tmp_buf2_len = 0;
+ unsigned char *tmp_buf = NULL, *tmp_buf2 = NULL;
+
+ if ((p7 = d2i_PKCS7(NULL, &p, 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;
+ }
+
+ if (OBJ_obj2nid(p7->type) != NID_pkcs7_enveloped) {
+ pkiDebug("Excepted id-enveloped PKCS7 msg (received type = %d)\n",
+ OBJ_obj2nid(p7->type));
+ krb5_set_error_message(context, retval, "wrong oid\n");
+ goto cleanup;
+ }
+
+ if (key_filename != NULL) {
+ BIO *tmp = NULL;
+ if ((tmp = BIO_new(BIO_s_file()))
+ && (BIO_read_filename(tmp, key_filename) > 0))
+ pkey = (EVP_PKEY *) PEM_read_bio_PrivateKey(tmp, NULL, NULL, NULL);
+ if (pkey == NULL) {
+ pkiDebug("failed to read key from %s\n", key_filename);
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ if (tmp != NULL)
+ BIO_free(tmp);
+ }
+
+ out = BIO_new(BIO_s_mem());
+ if (PKCS7_decrypt(p7, pkey, client_cert, out, flags))
+ pkiDebug("PKCS7 Decryption successful\n");
+ else {
+ unsigned long err = ERR_peek_error();
+ krb5_set_error_message(context, retval, "%s\n",
+ ERR_error_string(err, NULL));
+ pkiDebug("PKCS7 Decryption failure\n");
+ goto cleanup;
+ }
+
+ 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
+ switch (pa_type) {
+ case KRB5_PADATA_PK_AS_REP:
+ retval = encode_signeddata(tmp_buf, tmp_buf_len, &tmp_buf2,
+ &tmp_buf2_len);
+ if (retval) {
+ pkiDebug("failed to encode signeddata\n");
+ goto cleanup;
+ }
+#ifdef DEBUG_ASN1
+ print_buffer_bin(tmp_buf2, tmp_buf2_len, "/tmp/client_enc_keypack2");
+#endif
+ retval = pkcs7_signeddata_verify(tmp_buf2, tmp_buf2_len, data,
+ data_len, cert, plgctx->id_pkinit_rkeyData, context, plgctx);
+ break;
+ case KRB5_PADATA_PK_AS_REP_OLD:
+ retval = pkcs7_signeddata_verify(tmp_buf, tmp_buf_len, data,
+ data_len, cert, plgctx->id_pkinit_authData9, context, plgctx);
+ break;
+ }
+ 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 (pkey != NULL)
+ EVP_PKEY_free(pkey);
+ if (tmp_buf != NULL)
+ free(tmp_buf);
+ if (tmp_buf2 != NULL)
+ free(tmp_buf2);
+
+ return retval;
+}
+
+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 Tinf,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 = malloc(Tlen);
+ if (outdata == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ memcpy(*outdata, c.p, Tlen);
+ *outdata_len = Tlen;
+
+ retval = 0;
+cleanup:
+
+ return retval;
+}
+
+krb5_error_code
+pkcs7_envelopeddata_create(unsigned char *key_pack,
+ int key_pack_len,
+ unsigned char **out,
+ int *out_len,
+ X509 *client_cert,
+ X509 *kdc_cert,
+ krb5_preauthtype pa_type,
+ char *filename,
+ ASN1_OBJECT *oid,
+ krb5_context context)
+{
+
+ int retval = -1;
+ unsigned char *signed_data = NULL, *enc_data = NULL;
+ int signed_data_len = 0, enc_data_len = 0;
+ STACK_OF(X509) *encerts = NULL;
+ const EVP_CIPHER *cipher = NULL;
+ int flags = PKCS7_BINARY;
+ PKCS7 *p7 = NULL;
+ BIO *in = NULL;
+ unsigned char *p = NULL;
+
+ retval = pkcs7_signeddata_create(key_pack, key_pack_len, &signed_data,
+ &signed_data_len, kdc_cert, filename, oid, context);
+ if (retval) {
+ pkiDebug("failed to create pkcs7 signed data\n");
+ goto cleanup;
+ }
+
+ encerts = sk_X509_new_null();
+ sk_X509_push(encerts, client_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;
+ }
+ p7->d.enveloped->enc_data->content_type = OBJ_nid2obj(NID_pkcs7_signed);
+
+ *out_len = i2d_PKCS7(p7, NULL);
+ if (!*out_len || (p = *out = 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;
+
+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;
+}
+
+int
+verify_id_pkinit_san(X509 *x,
+ krb5_principal *out,
+ krb5_context context,
+ krb5_preauthtype pa_type,
+ pkinit_context *plgctx)
+{
+ int i = 0;
+ int ok = 0;
+ char buf[256];
+
+ if (x == NULL) return -1;
+
+ /* now let's verify that KDC's certificate has id-pkinit-san */
+ X509_NAME_oneline(X509_get_subject_name(x), buf, 256);
+ pkiDebug("cert = %s\n", buf);
+
+ if ((i = X509_get_ext_by_NID(x, NID_subject_alt_name, -1)) >= 0) {
+ X509_EXTENSION *ext = NULL;
+ GENERAL_NAMES *ialt = NULL;
+ GENERAL_NAME *gen = NULL;
+ int ret = 0;
+
+ if (!(ext = X509_get_ext(x, i)) || !(ialt = X509V3_EXT_d2i(ext))) {
+ pkiDebug("unable to retrieve subject alt name ext\n");
+ goto cleanup;
+ }
+
+ pkiDebug("found %d subject alt name extension(s)\n",
+ sk_GENERAL_NAME_num(ialt));
+
+ for (i = 0; i < sk_GENERAL_NAME_num(ialt); 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 = gen->d.otherName->value->value.sequence->data;
+ if (!OBJ_cmp(plgctx->id_pkinit_san,
+ gen->d.otherName->type_id)) {
+#ifdef DEBUG_ASN1
+ print_buffer_bin(name.data, name.length, "/tmp/pkinit_san");
+#endif
+ ret = decode_krb5_principal_name(&name, out);
+ } else if (plgctx->allow_upn &&
+ !OBJ_cmp(plgctx->id_pkinit_san9,
+ gen->d.otherName->type_id)) {
+ ret = krb5_parse_name(context, name.data, out);
+ } else {
+ pkiDebug("unrecognized or pa_type incorrect oid in SAN\n");
+ continue;
+ }
+
+ if (ret) {
+ pkiDebug("failed to decode Krb5PrincipalName in SAN\n");
+ break;
+ }
+ if (out != NULL)
+ ok = 1;
+ break;
+ case GEN_DNS:
+ if (pa_type == KRB5_PADATA_PK_AS_REP_OLD) {
+ pkiDebug("Win2K KDC on host = %s\n", gen->d.dNSName->data);
+ ok = 1;
+ }
+ break;
+ default:
+ pkiDebug("SAN type = %d expecting %d\n", gen->type,
+ GEN_OTHERNAME);
+ }
+ if (ok)
+ break;
+ }
+ sk_GENERAL_NAME_free(ialt);
+ }
+
+ if (!ok) {
+ pkiDebug("didn't find id_pkinit_san\n");
+ }
+
+ cleanup:
+ return ok;
+}
+
+int
+verify_id_pkinit_eku(X509 * x, krb5_preauthtype pa_type, pkinit_context *plgctx)
+{
+ int i = 0;
+ int ok = 0;
+ char buf[256];
+ int id_pkinit_eku = 0;
+ ASN1_OBJECT * oid_client = NULL, *oid_server = NULL;
+ ASN1_OBJECT *oid_logon = NULL, *oid_kp = NULL;
+
+ if (x == NULL) return -1;
+
+ /* now let's verify that KDC's certificate has pkinit EKU */
+ X509_NAME_oneline(X509_get_subject_name(x), buf, 256);
+ pkiDebug("cert = %s\n", buf);
+
+ oid_client = plgctx->id_pkinit_KPClientAuth;
+ oid_server = plgctx->id_pkinit_KPKdc;
+ oid_logon = plgctx->id_ms_kp_sc_logon;
+ oid_kp = plgctx->id_kp_serverAuth;
+
+ if ((i = X509_get_ext_by_NID(x, NID_ext_key_usage, -1)) >= 0) {
+ EXTENDED_KEY_USAGE *extusage;
+
+ if ((extusage = X509_get_ext_d2i(x, NID_ext_key_usage, NULL, NULL))) {
+ for (i = 0; i < sk_ASN1_OBJECT_num(extusage); i++) {
+ ASN1_OBJECT *tmp_oid = NULL;
+ int flag = 0;
+
+ tmp_oid = sk_ASN1_OBJECT_value(extusage, i);
+ switch ((int)pa_type) {
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ case KRB5_PADATA_PK_AS_REQ:
+ if (!OBJ_cmp(oid_client, tmp_oid) ||
+ !OBJ_cmp(oid_logon, tmp_oid))
+ flag = 1;
+ break;
+ case KRB5_PADATA_PK_AS_REP_OLD:
+ case KRB5_PADATA_PK_AS_REP:
+ if (!OBJ_cmp(oid_server, tmp_oid) ||
+ !OBJ_cmp(oid_kp, tmp_oid))
+ flag = 1;
+ break;
+ default:
+ goto cleanup;
+ }
+ if (flag) {
+ ASN1_BIT_STRING *usage = NULL;
+ pkiDebug("found pa_type-specific EKU\n");
+
+ /* check that digitalSignature KeyUsage is present */
+ if ((usage =
+ X509_get_ext_d2i(x, NID_key_usage, NULL, NULL))) {
+
+ if (!ku_reject(x, X509v3_KU_DIGITAL_SIGNATURE)) {
+ pkiDebug("found digitalSignature KU\n");
+ ok = 1;
+ } else
+ pkiDebug("didn't find digitalSignature KU\n");
+ }
+
+ ASN1_BIT_STRING_free(usage);
+ break;
+ }
+ }
+ }
+ }
+cleanup:
+ if (!ok) {
+ pkiDebug("didn't find extended key usage (EKU) for pkinit\n");
+ if (!plgctx->require_eku)
+ ok = 1;
+ }
+
+ return ok;
+}
+
+krb5_error_code
+pkinit_octetstring2key(krb5_context context,
+ krb5_enctype etype,
+ unsigned char *key,
+ 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;
+ unsigned char offset;
+ size_t keybytes, keylength;
+ int i;
+ 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, 1);
+ if (key_block->contents == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+
+ random_data.length = keybytes;
+ random_data.data = 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;
+}
+
+/* debugging functions */
+void
+hexdump(const u_char * buf, int len, int offset)
+{
+
+ u_int i, j, jm;
+ int c;
+ char msgbuff[256];
+ char *m = msgbuff;
+ int written;
+
+ if (buf == NULL || len <= 0)
+ return;
+
+ for (i = 0; i < len; i += 0x10) {
+ written = sprintf(m, "\t%04x: ", (u_int) (i + offset));
+ m += written;
+ jm = len - i;
+ jm = jm > 16 ? 16 : jm;
+
+ for (j = 0; j < jm; j++) {
+ if ((j % 2) == 1)
+ written = sprintf(m, "%02x ", (u_int) buf[i + j]);
+ else
+ written = sprintf(m, "%02x", (u_int) buf[i + j]);
+ m += written;
+ }
+ for (; j < 16; j++) {
+ if ((j % 2) == 1)
+ written = sprintf(m, "\t ");
+ else
+ written = sprintf(m, "\t");
+ m += written;
+ }
+ sprintf(m, " ");
+ m++;
+
+ for (j = 0; j < jm; j++) {
+ c = buf[i + j];
+ c = isprint(c) ? c : '.';
+ sprintf(m, "%c", c);
+ m++;
+ }
+ sprintf(m, "\n");
+
+ pkiDebug("%s", msgbuff);
+ m = msgbuff;
+ memset(msgbuff, '\0', sizeof(msgbuff));
+ }
+}
+
+void
+print_buffer(unsigned char *buf, 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, 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);
+}
+
+void
+print_dh(DH * dh, unsigned 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, msg);
+ if (dh)
+ DHparams_print(bio_err, dh);
+
+ BN_print(bio_err, dh->q);
+ BIO_puts(bio_err, "\n");
+ BIO_free(bio_err);
+
+}
+
+void
+print_pubkey(BIGNUM * key, unsigned 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, msg);
+ if (key)
+ BN_print(bio_err, key);
+ BIO_puts(bio_err, "\n");
+
+ BIO_free(bio_err);
+
+}
+
+krb5_error_code
+get_filename(char **name, char *env_name, int type)
+{
+ char *ev;
+
+ if ((*name = (unsigned char *) malloc(1024)) == NULL)
+ return ENOMEM;
+
+ if ((ev = getenv(env_name)) == NULL) {
+ if (!type) {
+ snprintf(*name, 1024, "/tmp/x509up_u%d", geteuid());
+ pkiDebug("using %s=%s\n", env_name, *name);
+ } else {
+ free(*name);
+ *name = NULL;
+ return ENOENT;
+ }
+ } else {
+ pkiDebug("found %s=%s\n", env_name, ev);
+ snprintf(*name, 1024, "%s", ev);
+ }
+
+ return 0;
+}
+
+krb5_error_code
+create_signature(unsigned char **sig, int *sig_len, unsigned char *data,
+ int data_len, char *filename)
+{
+ BIO *tmp = NULL;
+ EVP_PKEY *pkey = NULL;
+ EVP_MD_CTX md_ctx;
+ krb5_error_code retval = ENOMEM;
+
+ if (filename == NULL)
+ return ENOENT;
+
+ if ((tmp = BIO_new(BIO_s_file()))
+ && (BIO_read_filename(tmp, filename) > 0))
+ pkey = (EVP_PKEY *) PEM_read_bio_PrivateKey(tmp, NULL, NULL, NULL);
+ if (pkey == NULL) {
+ pkiDebug("failed to get private key from %s\n", filename);
+ goto cleanup;
+ }
+
+ if (tmp != NULL)
+ BIO_free(tmp);
+
+ EVP_VerifyInit(&md_ctx, EVP_sha1());
+ EVP_SignUpdate(&md_ctx, data, data_len);
+ *sig_len = EVP_PKEY_size(pkey);
+ if ((*sig = (unsigned char *) malloc(*sig_len)) == NULL)
+ goto cleanup;
+ EVP_SignFinal(&md_ctx, *sig, sig_len, pkey);
+
+ retval = 0;
+
+ cleanup:
+ if (pkey != NULL) {
+ EVP_PKEY_free(pkey);
+ EVP_MD_CTX_cleanup(&md_ctx);
+ }
+
+ return retval;
+}
+
+X509 *
+get_cert(char *filename)
+{
+ X509 *cert = NULL;
+ BIO *tmp = NULL;
+
+ if (filename == NULL)
+ return NULL;
+
+ if ((tmp = BIO_new(BIO_s_file()))
+ && (BIO_read_filename(tmp, filename) > 0))
+ cert = (X509 *) PEM_read_bio_X509(tmp, NULL, NULL, NULL);
+ if (tmp != NULL)
+ BIO_free(tmp);
+
+ return cert;
+}
+
+krb5_error_code
+load_trusted_certifiers(STACK_OF(X509) **trusted_CAs,
+ char *filename)
+{
+ STACK_OF(X509_INFO) *sk = NULL;
+ STACK_OF(X509) *ca_certs = NULL;
+ BIO *in = NULL;
+ krb5_error_code retval = ENOMEM;
+
+ *trusted_CAs = NULL;
+
+ ca_certs = sk_X509_new_null();
+ if (!ca_certs) {
+ return ENOMEM;
+ }
+
+ if (!(in = BIO_new_file(filename, "r"))) {
+ pkiDebug("error opening the CAfile\n");
+ 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("error reading the CAfile\n");
+ goto cleanup;
+ }
+
+ /* scan over it and pull out the certs */
+ while (sk_X509_INFO_num(sk)) {
+ X509_INFO *xi = NULL;
+
+ xi = sk_X509_INFO_shift(sk);
+ if (xi->x509 != NULL) {
+ sk_X509_push(ca_certs, xi->x509);
+ xi->x509 = NULL;
+ }
+ X509_INFO_free(xi);
+ }
+
+ if (!sk_X509_num(ca_certs)) {
+ pkiDebug("no certificates in file, %s\n", filename);
+ sk_X509_free(ca_certs);
+ } else
+ *trusted_CAs = ca_certs;
+
+ retval = 0;
+
+ cleanup:
+
+ if (in != NULL)
+ BIO_free(in);
+ if (sk != NULL)
+ sk_X509_INFO_free(sk);
+
+ return retval;
+}
+
+krb5_error_code
+create_krb5_trustedCertifiers(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;
+
+ 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++) {
+ X509 *x = NULL;
+ char buf[256];
+ X509_NAME *xn = NULL;
+ unsigned char *p = NULL;
+ int len = 0;
+ PKCS7_ISSUER_AND_SERIAL *is = NULL;
+
+ krb5_cas[i] = malloc(sizeof(krb5_external_principal_identifier));
+
+ x = sk_X509_value(sk, i);
+
+ X509_NAME_oneline(X509_get_subject_name(x), buf, 256);
+ 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 = 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;
+
+ 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 =
+ malloc((size_t) len)) == NULL)
+ goto cleanup;
+ i2d_PKCS7_ISSUER_AND_SERIAL(is, &p);
+ krb5_cas[i]->issuerAndSerialNumber.length = len;
+
+ /* fill-in subjectKeyIdentifier */
+ krb5_cas[i]->subjectKeyIdentifier.length = 0;
+ krb5_cas[i]->subjectKeyIdentifier.magic = 0;
+ krb5_cas[i]->subjectKeyIdentifier.data = NULL;
+
+ 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 =
+ 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);
+ }
+ 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;
+}
+
+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) {
+ /* not freeing clientPublicValue->algorithm.algorithm.data because
+ * client has that as static memory but server allocates it therefore
+ * the server will free it outside of this function
+ */
+ 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);
+ 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;
+ }
+ free((*in)[i]);
+ i++;
+ }
+ 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;
+}
diff --git a/src/plugins/preauth/pkinit/pkinit_srv.c b/src/plugins/preauth/pkinit/pkinit_srv.c
new file mode 100644
index 0000000..565bfcd
--- /dev/null
+++ b/src/plugins/preauth/pkinit/pkinit_srv.c
@@ -0,0 +1,1019 @@
+/*
+ * COPYRIGHT (C) 2006
+ * 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 <krb5/krb5.h>
+#include <krb5/preauth_plugin.h>
+#include <k5-int-pkinit.h>
+
+#include <openssl/x509.h>
+#include <openssl/pkcs7.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/dh.h>
+#include <openssl/asn1_mac.h>
+#include <openssl/sha.h>
+
+#include "pkinit.h"
+
+#ifdef DEBUG
+#define pkiDebug(args...) printf(args)
+#else
+#define pkiDebug(args...)
+#endif
+
+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);
+
+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 DH *
+decode_dhparams(DH ** a, unsigned char **pp, long length)
+{
+ ASN1_INTEGER ai, *aip = NULL;
+
+ 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 int
+check_dh(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;
+}
+
+static krb5_error_code
+server_check_dh(unsigned char *dh_params, int dh_params_len)
+{
+ DH *dh = NULL;
+ unsigned char *tmp = NULL;
+ BIGNUM *p = NULL;
+ krb5_error_code retval = KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
+
+ tmp = dh_params;
+ dh = DH_new();
+ dh = decode_dhparams(&dh, &tmp, dh_params_len);
+ if (dh == NULL) {
+ pkiDebug("failed to decode dhparams\n");
+ goto cleanup;
+ }
+
+ /* KDC SHOULD check to see if the key parameters satisfy its policy */
+ /* check dhparams is group 2 */
+ p = BN_bin2bn(pkinit_1024_dhprime, sizeof(pkinit_1024_dhprime), NULL);
+ if (p && !check_dh(p, dh->p, dh->g, dh->q)) {
+ retval = 0;
+ goto cleanup;
+ }
+ BN_free(p);
+ p = NULL;
+
+ /* check dhparams is group 14 */
+ p = BN_bin2bn(pkinit_2048_dhprime, sizeof(pkinit_2048_dhprime), NULL);
+ if (p && !check_dh(p, dh->p, dh->g, dh->q)) {
+ retval = 0;
+ goto cleanup;
+ }
+ BN_free(p);
+ p = NULL;
+
+ /* check dhparams is group 16 */
+ p = BN_bin2bn(pkinit_4096_dhprime, sizeof(pkinit_4096_dhprime), NULL);
+ if (p && !check_dh(p, dh->p, dh->g, dh->q)) {
+ retval = 0;
+ goto cleanup;
+ }
+
+ cleanup:
+ if (p != NULL)
+ BN_free(p);
+ if (dh != NULL)
+ DH_free(dh);
+ return retval;
+}
+
+/* kdc's dh function */
+static krb5_error_code
+server_process_dh(DH ** dh_server,
+ unsigned char *data,
+ int data_len,
+ unsigned char *dh_params,
+ int dh_params_len,
+ unsigned char **dh_pubkey,
+ int *dh_pubkey_len,
+ unsigned char **server_key, int *server_key_len)
+{
+ krb5_error_code retval = ENOMEM;
+ DH *dh = NULL;
+ unsigned char *p = NULL;
+ ASN1_INTEGER *pub_key = NULL;
+
+ /* unpack client's DHparams keys */
+ dh = DH_new();
+ p = dh_params;
+ dh = decode_dhparams(&dh, &p, dh_params_len);
+ if (dh == NULL)
+ return KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
+
+ *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, 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(*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(*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 != NULL)
+ DH_free(dh);
+ return retval;
+
+ cleanup:
+ if (dh != NULL)
+ DH_free(dh);
+ 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 krb5_error_code
+pkinit_create_edata(krb5_error_code err_code,
+ krb5_data **e_data)
+{
+ krb5_error_code retval = KRB5KRB_ERR_GENERIC;
+ STACK_OF(X509) *trusted_CAs = NULL;
+ krb5_external_principal_identifier **krb5_trusted_certifiers;
+ char filename[] = "/etc/grid-security/certificates/ca-bundle.crt";
+ krb5_data *data = NULL;
+
+ retval = load_trusted_certifiers(&trusted_CAs, filename);
+ if (trusted_CAs) {
+ retval = create_krb5_trustedCertifiers(trusted_CAs,
+ &krb5_trusted_certifiers);
+ if (retval) {
+ pkiDebug("create_krb5_trustedCertifiers failed\n");
+ goto cleanup;
+ }
+ retval = encode_krb5_td_trusted_certifiers(krb5_trusted_certifiers,
+ &data);
+ }
+ retval = 0;
+cleanup:
+ if (data != NULL) {
+ if (data->data != NULL)
+ free(data->data);
+ free(data);
+ }
+ free_krb5_external_principal_identifier(&krb5_trusted_certifiers);
+ 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;
+ pkiDebug("pkinit_get_edata: entered!\n");
+ 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_error_code retval = 0;
+ krb5_data scratch;
+ 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;
+ X509 *cert = NULL;
+ pkinit_context *plgctx = (pkinit_context *)pa_plugin_context;
+ krb5_preauthtype pa_type;
+ krb5_principal tmp_client;
+
+ 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 -1;
+
+ scratch.data = data->contents;
+ scratch.length = data->length;
+
+#ifdef DEBUG_ASN1
+ print_buffer_bin(scratch.data, scratch.length, "/tmp/kdc_as_req");
+#endif
+
+ 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 = decode_krb5_pa_pk_as_req(&scratch, &reqp);
+ scratch.data = NULL;
+ scratch.length = 0;
+ if (retval) {
+ pkiDebug("decode_krb5_pa_pk_as_req failed\n");
+ goto cleanup;
+ }
+
+ retval = pkcs7_signeddata_verify(reqp->signedAuthPack.data,
+ reqp->signedAuthPack.length, &scratch.data, &scratch.length,
+ &cert, plgctx->id_pkinit_authData, context, plgctx);
+ 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 = decode_krb5_pa_pk_as_req_draft9(&scratch, &reqp9);
+ scratch.data = NULL;
+ scratch.length = 0;
+ if (retval) {
+ pkiDebug("decode_krb5_pa_pk_as_req_draft9 failed\n");
+ goto cleanup;
+ }
+
+ retval = pkcs7_signeddata_verify(reqp9->signedAuthPack.data,
+ reqp9->signedAuthPack.length, &scratch.data, &scratch.length,
+ &cert, plgctx->id_pkinit_authData9, context, plgctx);
+ break;
+ default:
+ pkiDebug("unrecognized pa_type = %d\n", data->pa_type);
+ scratch.data = NULL;
+ scratch.length = 0;
+ retval = -1;
+ goto cleanup;
+ }
+ if (retval) {
+ pkiDebug("pkcs7_signeddata_verify failed\n");
+ goto cleanup;
+ }
+
+ if (!verify_id_pkinit_san(cert, &tmp_client, context, pa_type, plgctx)) {
+ pkiDebug("failed to verify id-pkinit-san\n");
+ retval = KRB5KDC_ERR_CLIENT_NOT_TRUSTED;
+ goto cleanup;
+ } else {
+ if (tmp_client != NULL) {
+ retval = krb5_principal_compare(context, request->client, tmp_client);
+ krb5_free_principal(context, tmp_client);
+ if (!retval) {
+ pkiDebug("identity in the certificate does not match "
+ "the requested principal\n");
+ retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
+ goto cleanup;
+ }
+ } else {
+ pkiDebug("didn't find Kerberos identity in certificate\n");
+ retval = KRB5KDC_ERR_CLIENT_NOT_TRUSTED;
+ goto cleanup;
+ }
+ }
+
+ if (!verify_id_pkinit_eku(cert, pa_type, plgctx)) {
+ pkiDebug("failed to verify id-pkinit-KPClientAuth\n");
+ retval = KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE;
+ goto cleanup;
+ }
+
+#ifdef DEBUG_ASN1
+ print_buffer_bin(scratch.data, scratch.length, "/tmp/kdc_auth_pack");
+#endif
+ switch ((int)data->pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ retval = decode_krb5_auth_pack(&scratch, &auth_pack);
+ if (retval) {
+ pkiDebug("failed to decode krb5_auth_pack\n");
+ goto cleanup;
+ }
+
+ if (auth_pack->clientPublicValue != NULL) {
+ retval = server_check_dh(
+ auth_pack->clientPublicValue->algorithm.parameters.data,
+ auth_pack->clientPublicValue->algorithm.parameters.length);
+
+ if (retval) {
+ pkiDebug("bad dh parameters\n");
+ goto cleanup;
+ }
+ }
+ /* check if kdcPkId present and match KDC's subjectIdentifier */
+ if (reqp->kdcPkId.data != NULL) {
+ PKCS7_ISSUER_AND_SERIAL *is = NULL;
+ const unsigned char *p = reqp->kdcPkId.data;
+ X509 *kdc_cert = NULL;
+ char *filename = NULL;
+ int status = 0;
+
+ pkiDebug("found kdcPkId in AS REQ\n");
+ is = d2i_PKCS7_ISSUER_AND_SERIAL(NULL, &p, reqp->kdcPkId.length);
+
+ get_filename(&filename, "KDC_CERT", 1);
+ kdc_cert = get_cert(filename);
+ status = X509_NAME_cmp(X509_get_issuer_name(kdc_cert), is->issuer);
+ if (status) {
+ pkiDebug("issuer names do not match\n");
+ }
+
+ status = ASN1_INTEGER_cmp(X509_get_serialNumber(kdc_cert), is->serial);
+ if (status) {
+ pkiDebug("serial numbers do not match\n");
+ }
+
+ X509_NAME_free(is->issuer);
+ ASN1_INTEGER_free(is->serial);
+ free(is);
+ X509_free(kdc_cert);
+ free(filename);
+ }
+ break;
+ case KRB5_PADATA_PK_AS_REP_OLD:
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ retval = decode_krb5_auth_pack_draft9(&scratch, &auth_pack9);
+ if (retval) {
+ pkiDebug("failed to decode krb5_auth_pack_draft9\n");
+ goto cleanup;
+ }
+ if (auth_pack9->clientPublicValue != NULL) {
+ retval = server_check_dh(
+ auth_pack9->clientPublicValue->algorithm.parameters.data,
+ auth_pack9->clientPublicValue->algorithm.parameters.length);
+
+ if (retval) {
+ pkiDebug("bad dh parameters\n");
+ goto cleanup;
+ }
+ }
+ break;
+ }
+
+ /* remember to set the PREAUTH flag in the reply */
+ enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
+
+ cleanup:
+ if (retval) {
+ pkiDebug("pkinit_verify_padata failed: creating e-data\n");
+ if (pkinit_create_edata(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 (auth_pack != NULL && auth_pack->clientPublicValue != NULL &&
+ auth_pack->clientPublicValue->algorithm.algorithm.data != NULL)
+ free(auth_pack->clientPublicValue->algorithm.algorithm.data);
+ free_krb5_auth_pack(&auth_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_auth_pack_draft9(context, &auth_pack9);
+ }
+
+ if (scratch.data != NULL)
+ free(scratch.data);
+ if (cert != NULL)
+ X509_free(cert);
+
+ 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;
+ krb5_auth_pack *auth_pack = NULL;
+ krb5_auth_pack_draft9 *auth_pack9 = NULL;
+ X509 *client_cert = NULL, *kdc_cert = NULL;
+ DH *dh_server = NULL;
+ int protocol = -1;
+
+ unsigned char *dh_pubkey = NULL;
+ unsigned char *server_key = NULL;
+ int server_key_len = 0;
+ int dh_pubkey_len = 0;
+ int i = 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;
+ char *filename = NULL;
+
+ krb5_reply_key_pack *key_pack = NULL;
+ krb5_reply_key_pack_draft9 *key_pack9 = NULL;
+ krb5_data *encoded_key_pack = NULL;
+
+ pkinit_context *plgctx = (pkinit_context *)pa_plugin_context;
+
+ *send_pa = NULL;
+ if (padata == NULL || padata->length <= 0 || padata->contents == NULL)
+ return 0;
+
+ pkiDebug("pkinit_return_padata: entered!\n");
+
+ scratch.data = padata->contents;
+ scratch.length = padata->length;
+
+ switch ((int)padata->pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ pkiDebug("processing KRB5_PADATA_PK_AS_REQ\n");
+ retval = decode_krb5_pa_pk_as_req(&scratch, &reqp);
+ if (retval) {
+ pkiDebug("decode_krb5_pa_pk_as_req failed");
+ goto cleanup;
+ }
+ scratch.data = NULL;
+ scratch.length = 0;
+
+ retval = pkcs7_signeddata_verify(reqp->signedAuthPack.data,
+ reqp->signedAuthPack.length, &scratch.data, &scratch.length,
+ &client_cert, plgctx->id_pkinit_authData, context, plgctx);
+ if (retval) {
+ pkiDebug("pkcs7_signeddata_verify failed\n");
+ goto cleanup;
+ }
+ retval = decode_krb5_auth_pack(&scratch, &auth_pack);
+ if (retval) {
+ pkiDebug("failed to decode krb5_auth_pack\n");
+ goto cleanup;
+ }
+ init_krb5_pa_pk_as_rep(&rep);
+ if (rep == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ break;
+ case KRB5_PADATA_PK_AS_REP_OLD:
+ case KRB5_PADATA_PK_AS_REQ_OLD:
+ pkiDebug("processing KRB5_PADATA_PK_AS_REQ_OLD %d\n", padata->pa_type);
+ retval = decode_krb5_pa_pk_as_req_draft9(&scratch, &reqp9);
+ if (retval) {
+ pkiDebug("decode_krb5_pa_pk_as_req_draft9 failed");
+ goto cleanup;
+ }
+ scratch.data = NULL;
+ scratch.length = 0;
+
+ retval = pkcs7_signeddata_verify(reqp9->signedAuthPack.data,
+ reqp9->signedAuthPack.length, &scratch.data, &scratch.length,
+ &client_cert, plgctx->id_pkinit_authData9, context, plgctx);
+ if (retval) {
+ pkiDebug("pkcs7_signeddata_verify failed");
+ goto cleanup;
+ }
+ retval = decode_krb5_auth_pack_draft9(&scratch, &auth_pack9);
+ if (retval) {
+ pkiDebug("failed to decode krb5_auth_pack_draft9\n");
+ goto cleanup;
+ }
+ init_krb5_pa_pk_as_rep_draft9(&rep9);
+ if (rep9 == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ break;
+ default:
+ pkiDebug("unrecognized pa_type = %d\n", padata->pa_type);
+ scratch.data = NULL;
+ scratch.length = 0;
+ retval = -1;
+ goto cleanup;
+ }
+
+ if (get_filename(&filename, "KDC_CERT", 1) != 0) {
+ pkiDebug("failed to get kdc cert filename\n");
+ retval = -1;
+ goto cleanup;
+ }
+
+ kdc_cert = get_cert(filename);
+ if (filename != NULL)
+ free(filename);
+ filename = NULL;
+
+ if (kdc_cert == NULL) {
+ pkiDebug("unable to get kdc's certificate\n");
+ retval = -1;
+ goto cleanup;
+ }
+
+ if (get_filename(&filename, "KDC_KEY", 1) != 0) {
+ pkiDebug("failed to get kdc key filename\n");
+ retval = -1;
+ goto cleanup;
+ }
+
+ 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[0];
+ 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;
+ }
+
+ if (auth_pack != NULL && auth_pack->clientPublicValue != NULL) {
+
+ pkiDebug("received DH key delivery AS REQ\n");
+
+ retval = server_process_dh(&dh_server,
+ auth_pack->clientPublicValue->subjectPublicKey.data,
+ auth_pack->clientPublicValue->subjectPublicKey.length,
+ auth_pack->clientPublicValue->algorithm.parameters.data,
+ auth_pack->clientPublicValue->algorithm.parameters.length,
+ &dh_pubkey, &dh_pubkey_len, &server_key, &server_key_len);
+ if (retval) {
+ pkiDebug("failed to process/create dh paramters\n");
+ goto cleanup;
+ }
+ rep->choice = choice_pa_pk_as_rep_dhInfo;
+ } else if (auth_pack9 != NULL && auth_pack9->clientPublicValue != NULL) {
+ retval = server_process_dh(&dh_server,
+ auth_pack9->clientPublicValue->subjectPublicKey.data,
+ auth_pack9->clientPublicValue->subjectPublicKey.length,
+ auth_pack9->clientPublicValue->algorithm.parameters.data,
+ auth_pack9->clientPublicValue->algorithm.parameters.length,
+ &dh_pubkey, &dh_pubkey_len, &server_key, &server_key_len);
+ if (retval) {
+ pkiDebug("failed to process/create dh paramters\n");
+ goto cleanup;
+ }
+ rep9->choice = choice_pa_pk_as_rep_draft9_dhSignedData;
+ } else if (auth_pack != NULL)
+ rep->choice = choice_pa_pk_as_rep_encKeyPack;
+ else if (auth_pack9 != NULL)
+ rep9->choice = choice_pa_pk_as_rep_draft9_encKeyPack;
+
+ 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 = encode_krb5_kdc_dh_key_info(&dhkey_info, &encoded_dhkey_info);
+ if (retval) {
+ pkiDebug("encode_krb5_kdc_dh_key_info failed\n");
+ goto cleanup;
+ }
+
+ switch ((int)padata->pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ retval = pkcs7_signeddata_create(encoded_dhkey_info->data,
+ encoded_dhkey_info->length,
+ &rep->u.dh_Info.dhSignedData.data,
+ &rep->u.dh_Info.dhSignedData.length,
+ kdc_cert, filename,
+ plgctx->id_pkinit_DHKeyData, context);
+ 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 = pkcs7_signeddata_create(encoded_dhkey_info->data,
+ encoded_dhkey_info->length, &rep9->u.dhSignedData.data,
+ &rep9->u.dhSignedData.length, kdc_cert, filename,
+ plgctx->id_pkinit_authData9, context);
+ if (retval) {
+ pkiDebug("failed to create pkcs7 signed data\n");
+ goto cleanup;
+ }
+ break;
+ }
+ } else {
+ pkiDebug("received public key encryption 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;
+ }
+
+ 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;
+ }
+ retval = krb5_c_make_checksum(context,
+ CKSUMTYPE_HMAC_SHA1_96_AES256, encrypting_key, 6, 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);
+#endif
+
+ krb5_copy_keyblock_contents(context, encrypting_key,
+ &key_pack->replyKey);
+
+ retval = encode_krb5_reply_key_pack(key_pack,
+ &encoded_key_pack);
+ if (retval) {
+ pkiDebug("failed to encode reply_key_pack\n");
+ goto cleanup;
+ }
+
+ init_krb5_pa_pk_as_rep(&rep);
+ if (rep == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ rep->choice = choice_pa_pk_as_rep_encKeyPack;
+ retval = pkcs7_envelopeddata_create(encoded_key_pack->data,
+ encoded_key_pack->length, &rep->u.encKeyPack.data,
+ &rep->u.encKeyPack.length, client_cert, kdc_cert,
+ padata->pa_type, filename,
+ plgctx->id_pkinit_rkeyData, 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 = auth_pack9->pkAuthenticator.nonce;
+ krb5_copy_keyblock_contents(context, encrypting_key,
+ &key_pack9->replyKey);
+
+ retval = encode_krb5_reply_key_pack_draft9(key_pack9,
+ &encoded_key_pack);
+ if (retval) {
+ pkiDebug("failed to encode reply_key_pack\n");
+ goto cleanup;
+ }
+ init_krb5_pa_pk_as_rep_draft9(&rep9);
+ if (rep9 == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ rep9->choice = choice_pa_pk_as_rep_draft9_encKeyPack;
+ retval = pkcs7_envelopeddata_create(encoded_key_pack->data,
+ encoded_key_pack->length, &rep9->u.encKeyPack.data,
+ &rep9->u.encKeyPack.length, client_cert, kdc_cert,
+ padata->pa_type, filename,
+ plgctx->id_pkinit_authData9, context);
+ break;
+ }
+ if (retval) {
+ pkiDebug("failed to create pkcs7 enveloped data\n");
+ goto cleanup;
+ }
+#ifdef DEBUG_ASN1
+ print_buffer_bin(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 = 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 = 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
+ print_buffer_bin(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:
+ if (client_cert != NULL)
+ X509_free(client_cert);
+ if (kdc_cert != NULL)
+ X509_free(kdc_cert);
+ 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_server != NULL)
+ DH_free(dh_server);
+ if (filename != NULL)
+ free(filename);
+
+ switch ((int)padata->pa_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ free_krb5_pa_pk_as_req(&reqp);
+ free_krb5_auth_pack(&auth_pack);
+ 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_auth_pack_draft9(context, &auth_pack9);
+ free_krb5_pa_pk_as_rep_draft9(&rep9);
+ free_krb5_reply_key_pack_draft9(&key_pack9);
+ 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;
+}
+
+#if 0
+static krb5_preauthtype supported_server_pa_types[] = {
+ KRB5_PADATA_PK_AS_REP_OLD,
+ 0
+};
+#else
+static krb5_preauthtype supported_server_pa_types[] = {
+ KRB5_PADATA_PK_AS_REQ,
+ KRB5_PADATA_PK_AS_REQ_OLD,
+ 0
+};
+#endif
+
+struct krb5plugin_preauth_server_ftable_v0 preauthentication_server_0 = {
+ "pkinit", /* name */
+ supported_server_pa_types, /* pa_type_list */
+ pkinit_lib_init, /* (*init_proc) */
+ pkinit_lib_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) */
+};