diff options
author | Kevin Coffman <kwc@citi.umich.edu> | 2006-11-22 16:58:01 +0000 |
---|---|---|
committer | Kevin Coffman <kwc@citi.umich.edu> | 2006-11-22 16:58:01 +0000 |
commit | b9075d633057c81eaf50e090ca502bedf268ce16 (patch) | |
tree | 28e5d2940a52bd24343ea6761c7a8478d039d432 | |
parent | 652a4ec2ce33105fc2c7591594818ff8b376a89c (diff) | |
download | krb5-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.in | 4 | ||||
-rw-r--r-- | src/configure.in | 2 | ||||
-rw-r--r-- | src/include/k5-int-pkinit.h | 248 | ||||
-rw-r--r-- | src/include/k5-int.h | 24 | ||||
-rw-r--r-- | src/include/krb5/krb5.hin | 6 | ||||
-rw-r--r-- | src/lib/krb5/asn.1/asn1_k_decode.c | 646 | ||||
-rw-r--r-- | src/lib/krb5/asn.1/asn1_k_decode.h | 40 | ||||
-rw-r--r-- | src/lib/krb5/asn.1/asn1_k_encode.c | 390 | ||||
-rw-r--r-- | src/lib/krb5/asn.1/asn1_k_encode.h | 61 | ||||
-rw-r--r-- | src/lib/krb5/asn.1/krb5_decode.c | 110 | ||||
-rw-r--r-- | src/lib/krb5/asn.1/krb5_encode.c | 91 | ||||
-rw-r--r-- | src/lib/krb5/libkrb5.exports | 19 | ||||
-rw-r--r-- | src/plugins/preauth/pkinit/Makefile.in | 63 | ||||
-rw-r--r-- | src/plugins/preauth/pkinit/README | 52 | ||||
-rw-r--r-- | src/plugins/preauth/pkinit/README.developers | 18 | ||||
-rw-r--r-- | src/plugins/preauth/pkinit/configure.in | 19 | ||||
-rw-r--r-- | src/plugins/preauth/pkinit/pkinit.exports | 2 | ||||
-rw-r--r-- | src/plugins/preauth/pkinit/pkinit.h | 142 | ||||
-rw-r--r-- | src/plugins/preauth/pkinit/pkinit_clnt.c | 1684 | ||||
-rw-r--r-- | src/plugins/preauth/pkinit/pkinit_lib.c | 1776 | ||||
-rw-r--r-- | src/plugins/preauth/pkinit/pkinit_srv.c | 1019 |
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) */ +}; |