aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Coffman <kwc@citi.umich.edu>2006-12-03 18:06:39 +0000
committerKevin Coffman <kwc@citi.umich.edu>2006-12-03 18:06:39 +0000
commitcd5746c0ada286da49410ee803f08a55144082e0 (patch)
treed68519e9f460a9100db688cdab16b9c26b2feea2
parent21dff76131f8f3f0e71df175972f807c8417d3e1 (diff)
downloadkrb5-cd5746c0ada286da49410ee803f08a55144082e0.zip
krb5-cd5746c0ada286da49410ee803f08a55144082e0.tar.gz
krb5-cd5746c0ada286da49410ee803f08a55144082e0.tar.bz2
Update to CITI code as of 20061203
Implements e-data and client_tryagain. Implements reading of config/profile parameters. Some smartcard improvements. git-svn-id: svn://anonsvn.mit.edu/krb5/users/coffman/pkinit@18917 dc483132-0cff-0310-8789-dd5450dbe970
-rw-r--r--src/include/k5-int-pkinit.h22
-rw-r--r--src/lib/krb5/asn.1/asn1_k_decode.c48
-rw-r--r--src/lib/krb5/asn.1/asn1_k_decode.h4
-rw-r--r--src/lib/krb5/asn.1/asn1_k_encode.c64
-rw-r--r--src/lib/krb5/asn.1/asn1_k_encode.h6
-rw-r--r--src/lib/krb5/asn.1/krb5_decode.c26
-rw-r--r--src/lib/krb5/asn.1/krb5_encode.c17
-rw-r--r--src/lib/krb5/krb/get_in_tkt.c8
-rw-r--r--src/lib/krb5/krb/preauth2.c14
-rw-r--r--src/lib/krb5/libkrb5.exports5
-rw-r--r--src/plugins/preauth/pkinit/Makefile.in10
-rw-r--r--src/plugins/preauth/pkinit/pkinit.h43
-rw-r--r--src/plugins/preauth/pkinit/pkinit_clnt.c705
-rw-r--r--src/plugins/preauth/pkinit/pkinit_lib.c316
-rw-r--r--src/plugins/preauth/pkinit/pkinit_srv.c612
15 files changed, 1547 insertions, 353 deletions
diff --git a/src/include/k5-int-pkinit.h b/src/include/k5-int-pkinit.h
index d65712c..60c02cb 100644
--- a/src/include/k5-int-pkinit.h
+++ b/src/include/k5-int-pkinit.h
@@ -100,6 +100,14 @@ typedef struct _krb5_trusted_ca {
} u;
} krb5_trusted_ca;
+/* typed data */
+typedef struct _krb5_typed_data {
+ krb5_magic magic;
+ krb5_int32 type;
+ unsigned int length;
+ krb5_octet *data;
+} krb5_typed_data;
+
/* PA-PK-AS-REQ (Draft 9 -- PA TYPE 14) */
typedef struct _krb5_pa_pk_as_req_draft9 {
krb5_octet_data signedAuthPack;
@@ -210,6 +218,12 @@ krb5_error_code encode_krb5_reply_key_pack_draft9
krb5_error_code encode_krb5_td_trusted_certifiers
(const krb5_external_principal_identifier **, krb5_data **code);
+krb5_error_code encode_krb5_typed_data
+ (const krb5_typed_data **, krb5_data **code);
+
+krb5_error_code encode_krb5_td_dh_parameters
+ (const krb5_algorithm_identifier **, krb5_data **code);
+
/*************************************************************************
* Prototypes for pkinit asn.1 decode routines
*************************************************************************/
@@ -244,5 +258,13 @@ krb5_error_code decode_krb5_reply_key_pack
krb5_error_code decode_krb5_reply_key_pack_draft9
(const krb5_data *, krb5_reply_key_pack_draft9 **);
+krb5_error_code decode_krb5_typed_data
+ (const krb5_data *, krb5_typed_data ***);
+
+krb5_error_code decode_krb5_td_trusted_certifiers
+ (const krb5_data *, krb5_external_principal_identifier ***);
+
+krb5_error_code decode_krb5_td_dh_parameters
+ (const krb5_data *, krb5_algorithm_identifier ***);
#endif /* _KRB5_INT_PKINIT_H */
diff --git a/src/lib/krb5/asn.1/asn1_k_decode.c b/src/lib/krb5/asn.1/asn1_k_decode.c
index 507851b..9ccbffd 100644
--- a/src/lib/krb5/asn.1/asn1_k_decode.c
+++ b/src/lib/krb5/asn.1/asn1_k_decode.c
@@ -1544,7 +1544,7 @@ asn1_error_code asn1_decode_subject_pk_info(asn1buf *buf, krb5_subject_pk_info *
asn1_error_code asn1_decode_algorithm_identifier(asn1buf *buf, krb5_algorithm_identifier *val)
{
setup();
- { begin_structure();
+ { begin_structure_no_tag();
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();
@@ -1552,9 +1552,33 @@ asn1_error_code asn1_decode_algorithm_identifier(asn1buf *buf, krb5_algorithm_id
cleanup();
}
+asn1_error_code asn1_decode_AlgorithmIdentifier(asn1buf *buf, krb5_algorithm_identifier *val) {
+
+ setup();
+ { begin_structure();
+ 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_sequence_of_AlgorithmIdentifier(asn1buf *buf, krb5_algorithm_identifier ***val)
{
- decode_array_body(krb5_algorithm_identifier, asn1_decode_algorithm_identifier);
+ decode_array_body(krb5_algorithm_identifier, asn1_decode_AlgorithmIdentifier);
}
asn1_error_code asn1_decode_kdc_dh_key_info (asn1buf *buf, krb5_kdc_dh_key_info *val)
@@ -1738,3 +1762,23 @@ asn1_error_code asn1_decode_pa_pk_as_rep_draft9(asn1buf *buf, krb5_pa_pk_as_rep_
}
cleanup();
}
+
+asn1_error_code asn1_decode_sequence_of_typed_data(asn1buf *buf, krb5_typed_data ***val)
+{
+ setup();
+ {
+ decode_array_body(krb5_typed_data,asn1_decode_typed_data);
+ }
+ cleanup();
+}
+
+asn1_error_code asn1_decode_typed_data(asn1buf *buf, krb5_typed_data *val)
+{
+ setup();
+ { begin_structure();
+ get_field(val->type,0,asn1_decode_int32);
+ get_lenfield(val->length,val->data,1,asn1_decode_octetstring);
+ 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 fc8636a..b63029b 100644
--- a/src/lib/krb5/asn.1/asn1_k_decode.h
+++ b/src/lib/krb5/asn.1/asn1_k_decode.h
@@ -193,6 +193,10 @@ 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);
+asn1_error_code asn1_decode_sequence_of_typed_data
+ (asn1buf *buf, krb5_typed_data ***val);
+asn1_error_code asn1_decode_typed_data
+ (asn1buf *buf, krb5_typed_data *val);
/* arrays */
asn1_error_code asn1_decode_authorization_data
diff --git a/src/lib/krb5/asn.1/asn1_k_encode.c b/src/lib/krb5/asn.1/asn1_k_encode.c
index 1af1d23..0add915 100644
--- a/src/lib/krb5/asn.1/asn1_k_encode.c
+++ b/src/lib/krb5/asn.1/asn1_k_encode.c
@@ -1039,9 +1039,35 @@ asn1_error_code asn1_encode_algorithm_identifier(asn1buf *buf, const krb5_algori
{
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);
+ if (val->parameters.length != 0) {
+ retval = asn1buf_insert_octetstring(buf, val->parameters.length,
+ val->parameters.data);
+ if(retval) {
+ asn1buf_destroy(&buf);
+ return retval;
+ }
+ sum += val->parameters.length;
+ }
+
+ retval = asn1_encode_oid(buf, val->algorithm.length,
+ val->algorithm.data,
+ &length);
+
+ if(retval) {
+ asn1buf_destroy(&buf);
+ return retval;
+ }
+ sum += length;
+
+ retval = asn1_make_etag(buf, UNIVERSAL, ASN1_SEQUENCE,
+ val->parameters.length + length,
+ &length);
+
+ if(retval) {
+ asn1buf_destroy(&buf);
+ return retval;
+ }
+ sum += length;
asn1_makeseq();
asn1_cleanup();
@@ -1343,9 +1369,37 @@ asn1_error_code asn1_encode_pa_pk_as_rep_draft9(asn1buf *buf, const krb5_pa_pk_a
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);
+ retval = asn1_encode_sequence_of_external_principal_identifier(buf, val, &length);
+ if (retval) {
+ asn1buf_destroy(&buf);
+ return retval;
+ }
asn1_cleanup();
}
+asn1_error_code asn1_encode_sequence_of_typed_data(asn1buf *buf, const krb5_typed_data **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_typed_data(buf,val[i],&length);
+ if(retval) return retval;
+ sum += length;
+ }
+ asn1_makeseq();
+
+ asn1_cleanup();
+}
+asn1_error_code asn1_encode_typed_data(asn1buf *buf, const krb5_typed_data *val, unsigned int *retlen)
+{
+ asn1_setup();
+ asn1_addlenfield(val->length, val->data, 1, asn1_encode_octetstring);
+ asn1_addfield(val->type, 0, asn1_encode_integer);
+ asn1_makeseq();
+ 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 76dbdfa..b5f24c4 100644
--- a/src/lib/krb5/asn.1/asn1_k_encode.h
+++ b/src/lib/krb5/asn.1/asn1_k_encode.h
@@ -330,4 +330,10 @@ asn1_error_code asn1_encode_pa_pk_as_rep_draft9
asn1_error_code asn1_encode_td_trusted_certifiers
(asn1buf *buf, const krb5_external_principal_identifier **val, unsigned int *retlen);
+
+asn1_error_code asn1_encode_typed_data
+ (asn1buf *buf, const krb5_typed_data *val, unsigned int *retlen);
+
+asn1_error_code asn1_encode_sequence_of_typed_data
+ (asn1buf *buf, const krb5_typed_data **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 0f3de4e..0e18df5 100644
--- a/src/lib/krb5/asn.1/krb5_decode.c
+++ b/src/lib/krb5/asn.1/krb5_decode.c
@@ -1044,3 +1044,29 @@ krb5_error_code decode_krb5_reply_key_pack_draft9(const krb5_data *code, krb5_re
cleanup(free);
}
+krb5_error_code decode_krb5_typed_data(const krb5_data *code, krb5_typed_data ***rep)
+{
+ setup_buf_only();
+ retval = asn1_decode_sequence_of_typed_data(&buf, rep);
+ if (retval) clean_return(retval);
+
+ cleanup(free);
+}
+
+krb5_error_code decode_krb5_td_trusted_certifiers(const krb5_data *code, krb5_external_principal_identifier ***rep)
+{
+ setup_buf_only();
+ retval = asn1_decode_sequence_of_external_principal_identifier(&buf, rep);
+ if (retval) clean_return(retval);
+
+ cleanup(free);
+}
+
+krb5_error_code decode_krb5_td_dh_parameters(const krb5_data *code, krb5_algorithm_identifier ***rep)
+{
+ setup_buf_only();
+ retval = asn1_decode_sequence_of_AlgorithmIdentifier(&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 0f5dbe0..46827bf 100644
--- a/src/lib/krb5/asn.1/krb5_encode.c
+++ b/src/lib/krb5/asn.1/krb5_encode.c
@@ -978,3 +978,20 @@ krb5_error_code encode_krb5_td_trusted_certifiers(const krb5_external_principal_
krb5_cleanup();
}
+krb5_error_code encode_krb5_typed_data(const krb5_typed_data **rep, krb5_data **code)
+{
+ krb5_setup();
+ retval = asn1_encode_sequence_of_typed_data(buf,rep,&length);
+ if(retval) return retval;
+ sum += length;
+ krb5_cleanup();
+}
+
+krb5_error_code encode_krb5_td_dh_parameters(const krb5_algorithm_identifier **rep, krb5_data **code)
+{
+ krb5_setup();
+ retval = asn1_encode_sequence_of_algorithm_identifier(buf,rep,&length);
+ if(retval) return retval;
+ sum += length;
+ krb5_cleanup();
+}
diff --git a/src/lib/krb5/krb/get_in_tkt.c b/src/lib/krb5/krb/get_in_tkt.c
index 9cf022f..80baf41 100644
--- a/src/lib/krb5/krb/get_in_tkt.c
+++ b/src/lib/krb5/krb/get_in_tkt.c
@@ -1097,12 +1097,12 @@ krb5_get_init_creds(krb5_context context,
/* now, loop processing preauth data and talking to the kdc */
for (loopcount = 0; loopcount < MAX_IN_TKT_LOOPS; loopcount++) {
+ if (request.padata) {
+ krb5_free_pa_data(context, request.padata);
+ request.padata = NULL;
+ }
if (!err_reply) {
/* either our first attempt, or retrying after PREAUTH_NEEDED */
- if (request.padata) {
- krb5_free_pa_data(context, request.padata);
- request.padata = NULL;
- }
if ((ret = krb5_do_preauth(context,
&request,
encoded_request_body,
diff --git a/src/lib/krb5/krb/preauth2.c b/src/lib/krb5/krb/preauth2.c
index 7c34ca2..7428896 100644
--- a/src/lib/krb5/krb/preauth2.c
+++ b/src/lib/krb5/krb/preauth2.c
@@ -500,9 +500,23 @@ krb5_run_preauth_plugins(krb5_context kcontext,
*module_ret = ret;
/* Save the new preauth data item. */
if (out_pa_data != NULL) {
+#if 0
+/* this is a temporary hack for doing "fixed" draft9 client. the client needs to
+send a pa_type 132 to the win2k kdc then the kdc will reply back with a checksum
+instead of the nonce
+*/
+ krb5_pa_data *tmp = NULL;
+ tmp = malloc(sizeof(krb5_pa_data));
+ tmp->pa_type=132;
+ tmp->length = 0;
+ tmp->contents = NULL;
+#endif
ret = grow_pa_list(out_pa_list, out_pa_list_size, out_pa_data);
if (ret != 0)
return ret;
+#if 0
+ grow_pa_list(out_pa_list, out_pa_list_size, tmp);
+#endif
}
break;
}
diff --git a/src/lib/krb5/libkrb5.exports b/src/lib/krb5/libkrb5.exports
index 5e66d35..0393354 100644
--- a/src/lib/krb5/libkrb5.exports
+++ b/src/lib/krb5/libkrb5.exports
@@ -203,6 +203,9 @@ decode_krb5_sam_response_2
decode_krb5_tgs_rep
decode_krb5_tgs_req
decode_krb5_ticket
+decode_krb5_td_dh_parameters
+decode_krb5_td_trusted_certifiers
+decode_krb5_typed_data
encode_krb5_alt_method
encode_krb5_ap_rep
encode_krb5_ap_rep_enc_part
@@ -248,10 +251,12 @@ encode_krb5_sam_key
encode_krb5_sam_response
encode_krb5_sam_response_2
encode_krb5_setpw_req
+encode_krb5_td_dh_parameters
encode_krb5_td_trusted_certifiers
encode_krb5_tgs_rep
encode_krb5_tgs_req
encode_krb5_ticket
+encode_krb5_typed_data
et_asn1_error_table
et_k524_error_table
et_kdb5_error_table
diff --git a/src/plugins/preauth/pkinit/Makefile.in b/src/plugins/preauth/pkinit/Makefile.in
index 39f96cc..1bfdddc 100644
--- a/src/plugins/preauth/pkinit/Makefile.in
+++ b/src/plugins/preauth/pkinit/Makefile.in
@@ -53,11 +53,13 @@ clean::
# 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
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/profile.h \
+ $(COM_ERR_DEPS) $(SRCTOP)/include/k5-int-pkinit.h $(SRCTOP)/include/krb5/preauth_plugin.h \
+ pkinit.h pkinit_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
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/profile.h \
+ $(COM_ERR_DEPS) $(SRCTOP)/include/k5-int-pkinit.h $(SRCTOP)/include/krb5/preauth_plugin.h \
+ pkinit.h pkinit_clnt.c
diff --git a/src/plugins/preauth/pkinit/pkinit.h b/src/plugins/preauth/pkinit/pkinit.h
index c8e90ea..5e01b1d 100644
--- a/src/plugins/preauth/pkinit/pkinit.h
+++ b/src/plugins/preauth/pkinit/pkinit.h
@@ -38,11 +38,22 @@ 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;
+ char *ctx_identity;
+ char *ctx_anchors;
+ char *ctx_pool;
+ char *ctx_revoke;
+ char *ctx_ocsp;
+ char *ctx_mapping_file;
+ int ctx_princ_in_cert;
+ int ctx_dh_min_bits;
+ int ctx_allow_proxy_certs;
+ DH *dh_1024;
+ DH *dh_2048;
+ DH *dh_4096;
ASN1_OBJECT *id_pkinit_authData;
ASN1_OBJECT *id_pkinit_authData9;
ASN1_OBJECT *id_pkinit_DHKeyData;
@@ -57,14 +68,31 @@ typedef struct _pkinit_context {
typedef struct _pkinit_req_context {
int magic;
- int version;
DH *dh;
+ int dh_size;
+ int require_eku;
+ int require_san;
+ int require_hostname_match;
+ int allow_upn;
+ int require_crl_checking;
+ int win2k_target;
+ int win2k_require_cksum;
+ krb5_preauthtype patype;
} pkinit_req_context;
/* Function prototypes */
void openssl_init(void);
+krb5_error_code pkinit_init_dh_params(krb5_context, pkinit_context *);
+void pkinit_fini_dh_params(krb5_context, pkinit_context *);
+krb5_error_code pkinit_encode_dh_params
+ (BIGNUM *, BIGNUM *, BIGNUM *, unsigned char **, int *);
+DH *pkinit_decode_dh_params
+ (DH **, unsigned char **, long );
+int pkinit_check_dh_params
+ (BIGNUM * p1, BIGNUM * p2, BIGNUM * g1, BIGNUM * q1);
+
krb5_error_code pkinit_sign_data
(unsigned char *data, int data_len, unsigned char **sig,
int *sig_len, char *filename);
@@ -99,7 +127,8 @@ int verify_id_pkinit_san
krb5_preauthtype pa_type, pkinit_context *plgctx);
int verify_id_pkinit_eku
- (X509 * x, krb5_preauthtype pa_type, pkinit_context *plgctx);
+ (pkinit_context *plgctx, X509 *x, krb5_preauthtype pa_type,
+ int require_eku);
krb5_error_code load_trusted_certifiers
(STACK_OF(X509) **, char *);
@@ -128,6 +157,7 @@ void init_krb5_reply_key_pack_draft9(krb5_reply_key_pack_draft9 **in);
void init_krb5_auth_pack(krb5_auth_pack **in);
void init_krb5_auth_pack_draft9(krb5_auth_pack_draft9 **in);
void init_krb5_pa_pk_as_rep(krb5_pa_pk_as_rep **in);
+void init_krb5_typed_data(krb5_typed_data **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);
@@ -139,4 +169,11 @@ void free_krb5_pa_pk_as_rep(krb5_pa_pk_as_rep **in);
void free_krb5_pa_pk_as_rep_draft9(krb5_pa_pk_as_rep_draft9 **in);
void free_krb5_external_principal_identifier(krb5_external_principal_identifier ***in);
void free_krb5_trusted_ca(krb5_trusted_ca ***in);
+void free_krb5_typed_data(krb5_typed_data ***in);
+void free_krb5_algorithm_identifier(krb5_algorithm_identifier ***in);
+
+#define TD_TRUSTED_CERTIFIERS 104
+#define TD_INVALID_CERTIFICATES 105
+#define TD_DH_PARAMETERS 109
+
#endif /* _PKINIT_H */
diff --git a/src/plugins/preauth/pkinit/pkinit_clnt.c b/src/plugins/preauth/pkinit/pkinit_clnt.c
index 6eb4485..69c4320 100644
--- a/src/plugins/preauth/pkinit/pkinit_clnt.c
+++ b/src/plugins/preauth/pkinit/pkinit_clnt.c
@@ -52,6 +52,7 @@
#include <krb5/preauth_plugin.h>
#include <k5-int-pkinit.h>
+#include <profile.h>
#include "pkinit.h"
/* #define DEBUG */
@@ -62,6 +63,10 @@
#define pkiDebug(args...)
#endif
+#define MAX_CERT_SIZE 4096
+#define MAX_ID_SIZE 4
+#define MAX_N_OBJS 6
+
#define DH_PROTOCOL 0
#define RSA_PROTOCOL 1
@@ -107,13 +112,13 @@ krb5_error_code pkinit_as_rep_parse
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_enctype etype, krb5_principal server, krb5_data *);
krb5_error_code pa_pkinit_parse_rep
(krb5_context context, pkinit_context *plgctx,
pkinit_req_context *reqcxt, krb5_kdc_req * request,
krb5_pa_data * in_padata, krb5_pa_data ** out_padata,
- krb5_enctype etype, krb5_keyblock * as_key);
+ krb5_enctype etype, krb5_keyblock * as_key, krb5_data *);
static int using_pkcs11(void);
@@ -124,7 +129,7 @@ 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 *);
+ (int, DH **, unsigned char **, int *, unsigned char **, int *);
static krb5_error_code client_process_dh
(DH *, unsigned char *, long, unsigned char **, int *);
@@ -142,7 +147,7 @@ static krb5_error_code create_krb5_trustedCas
static krb5_error_code pkinit_open_session
(krb5_context, krb5_prompter_fct, void *);
-static void pkinit_close_session();
+static void pkinit_close_session(void);
#endif
static void init_krb5_subject_pk_info(krb5_subject_pk_info **in);
@@ -182,6 +187,7 @@ pa_pkinit_gen_req(krb5_context context,
pkiDebug("pa_pkinit_gen_req: enctype = %d\n", *etype);
cksum.contents = NULL;
+ reqctx->patype = in_padata->pa_type;
/* If we don't have a client cert, we're done */
if (request->client == NULL) {
@@ -217,8 +223,8 @@ pa_pkinit_gen_req(krb5_context context,
/* 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."
+ 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);
@@ -278,6 +284,7 @@ pa_pkinit_gen_req(krb5_context context,
#endif
(*out_padata)->length = out_data->length;
(*out_padata)->contents = (krb5_octet *) out_data->data;
+
retval = 0;
cleanup:
@@ -377,11 +384,11 @@ pkinit_as_req_create(krb5_context context,
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) {
+ if ((retval = client_create_dh(reqctx->dh_size,
+ &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;
}
@@ -470,7 +477,7 @@ pkinit_as_req_create(krb5_context context,
switch((int)pa_type) {
case KRB5_PADATA_PK_AS_REQ:
-#if 1
+#if 0
if (trusted_CAs) {
create_krb5_trustedCertifiers(trusted_CAs,
&req->trustedCertifiers);
@@ -611,7 +618,8 @@ pa_pkinit_parse_rep(krb5_context context,
krb5_pa_data * in_padata,
krb5_pa_data ** out_padata,
krb5_enctype etype,
- krb5_keyblock * as_key)
+ krb5_keyblock * as_key,
+ krb5_data *encoded_request)
{
krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
krb5_data asRep;
@@ -659,7 +667,7 @@ pa_pkinit_parse_rep(krb5_context context,
retval =
pkinit_as_rep_parse(context, plgctx, reqctx, in_padata->pa_type,
request, &asRep, client_cert, as_key,
- etype, request->server);
+ etype, request->server, encoded_request);
if (retval) {
pkiDebug("pkinit_as_rep_parse returned %d\n", (int) retval);
goto cleanup;
@@ -690,7 +698,8 @@ pkinit_as_rep_parse(krb5_context context,
X509 * client_cert,
krb5_keyblock * key_block,
krb5_enctype etype,
- krb5_principal server)
+ krb5_principal server,
+ krb5_data *encoded_request)
{
krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
krb5_pa_pk_as_rep *kdc_reply = NULL;
@@ -703,10 +712,7 @@ pkinit_as_rep_parse(krb5_context context,
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;
@@ -757,7 +763,7 @@ pkinit_as_rep_parse(krb5_context context,
goto cleanup;
}
- if (plgctx->require_san) {
+ if (reqctx->require_san) {
if (!verify_id_pkinit_san(kdc_cert, &tmp_server, context, pa_type,
plgctx)) {
pkiDebug("failed to verify id-pkinit-san\n");
@@ -774,12 +780,21 @@ pkinit_as_rep_parse(krb5_context context,
goto cleanup;
}
} else if (pa_type == KRB5_PADATA_PK_AS_REP_OLD) {
- pkiDebug("need to check dnsName against KDC's hostname\n");
+ if (reqctx->require_hostname_match) {
+ /* XXX should this be tied with require_san? */
+ pkiDebug("XXX need to check dnsName against KDC's hostname\n");
+ retval = KRB5KDC_ERR_KDC_NOT_TRUSTED;
+ goto cleanup;
+ } else {
+ pkiDebug("config options says skip hostname check\n");
+ }
}
}
+ } else {
+ pkiDebug("config option says not to check for SAN\n");
}
- if (!verify_id_pkinit_eku(kdc_cert, pa_type, plgctx)) {
+ if (!verify_id_pkinit_eku(plgctx, kdc_cert, pa_type, reqctx->require_eku)) {
pkiDebug("failed to verify id-pkinit-KPKdc\n");
retval = KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE;
goto cleanup;
@@ -825,57 +840,19 @@ pkinit_as_rep_parse(krb5_context context,
#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(&dh_data,
+ &key_pack)) != 0) {
+ pkiDebug("failed to decode reply_key_pack\n");
+ if (pa_type == KRB5_PADATA_PK_AS_REP)
+ goto cleanup;
+ else {
if ((retval =
decode_krb5_reply_key_pack_draft9(&dh_data,
&key_pack9)) != 0) {
pkiDebug("failed to decode reply_key_pack_draft9\n");
goto cleanup;
}
+ pkiDebug("decode reply_key_pack_draft9\n");
if (key_pack9->nonce != request->nonce) {
pkiDebug("nonce in AS_REP=%d doesn't match AS_REQ=%d\n", key_pack9->nonce, request->nonce);
retval = -1;
@@ -884,11 +861,41 @@ pkinit_as_rep_parse(krb5_context context,
krb5_copy_keyblock_contents(context, &key_pack9->replyKey,
key_block);
break;
- default:
- pkiDebug("unrecognized pa_type = %d\n", pa_type);
- retval = -1;
- goto cleanup;
+ }
}
+ /* this is hack but windows sends back sha1 checksum
+ * with checksum type of 14. mit has no 14 checksum type
+ */
+ if (key_pack->asChecksum.checksum_type == 14)
+ key_pack->asChecksum.checksum_type = CKSUMTYPE_NIST_SHA;
+ retval = krb5_c_make_checksum(context,
+ key_pack->asChecksum.checksum_type,
+ &key_pack->replyKey, KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
+ encoded_request, &cksum);
+ if (retval) {
+ pkiDebug("failed to make a checksum\n");
+ goto cleanup;
+ }
+
+ if ((cksum.length != key_pack->asChecksum.length) ||
+ memcmp(cksum.contents, key_pack->asChecksum.contents,
+ cksum.length)) {
+ pkiDebug("failed to match the checksums\n");
+#ifdef DEBUG_CKSUM
+ pkiDebug("calculating checksum on buf size (%d)\n", encoded_request->length);
+ print_buffer(encoded_request->data, encoded_request->length);
+ pkiDebug("encrypting key (%d)\n", key_pack->replyKey.length);
+ print_buffer(key_pack->replyKey.contents, key_pack->replyKey.length);
+ pkiDebug("received checksum type=%d size=%d ", key_pack->asChecksum.checksum_type, key_pack->asChecksum.length);
+ print_buffer(key_pack->asChecksum.contents, key_pack->asChecksum.length);
+ pkiDebug("expected checksum type=%d size=%d ", cksum.checksum_type, cksum.length);
+ print_buffer(cksum.contents, cksum.length);
+#endif
+ goto cleanup;
+ } else
+ pkiDebug("checksums match\n");
+
+ krb5_copy_keyblock_contents(context, &key_pack->replyKey, key_block);
break;
default:
@@ -909,25 +916,67 @@ cleanup:
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;
- }
+
+ if (key_pack != NULL) {
+ free_krb5_reply_key_pack(&key_pack);
+ if (cksum.contents != NULL)
+ free(cksum.contents);
+ } else if (key_pack9 != NULL)
+ free_krb5_reply_key_pack_draft9(&key_pack9);
pkiDebug("pkinit_as_rep_parse retval=%d\n", (int) retval);
return retval;
}
+void
+pkinit_client_profile(krb5_context context,
+ pkinit_context *plgctx,
+ pkinit_req_context *reqctx,
+ krb5_kdc_req *request)
+{
+ profile_t profile;
+ krb5_error_code retval;
+ char *realmname = NULL;
+
+ if (request->server && request->server->realm.length) {
+ realmname = malloc(request->server->realm.length + 1);
+ if (NULL != realmname) {
+ memcpy(realmname, request->server->realm.data,
+ request->server->realm.length);
+ realmname[request->server->realm.length] = '\0';
+ }
+ }
+ if (NULL == realmname)
+ return;
+
+ retval = krb5_get_profile(context, &profile);
+ if (retval) {
+ free(realmname);
+ return;
+ }
+
+ profile_get_boolean(profile, "realms", realmname,
+ "pkinit_win2k",
+ reqctx->win2k_target, &reqctx->win2k_target);
+ profile_get_boolean(profile, "realms", realmname,
+ "pkinit_win2k_require_binding",
+ reqctx->win2k_require_cksum,
+ &reqctx->win2k_require_cksum);
+
+ profile_get_boolean(profile, "realms", realmname,
+ "pkinit_require_eku",
+ reqctx->require_eku, &reqctx->require_eku);
+ profile_get_boolean(profile, "realms", realmname,
+ "pkinit_require_krbtgt_otherName",
+ reqctx->require_san, &reqctx->require_san);
+ profile_get_boolean(profile, "realms", realmname,
+ "pkinit_require_hostname_match",
+ reqctx->require_hostname_match,
+ &reqctx->require_hostname_match);
+ free(realmname);
+ profile_release(profile);
+}
+
krb5_error_code
pkinit_client_process(krb5_context context,
void *plugin_context,
@@ -968,6 +1017,7 @@ pkinit_client_process(krb5_context context,
(*get_data_proc)(context, rock, krb5plugin_preauth_client_free_etype,
&cdata);
}
+ pkinit_client_profile(context, plgctx, reqctx, request);
switch ((int) in_padata->pa_type) {
case KRB5_PADATA_PK_AS_REQ:
pkiDebug("processing KRB5_PADATA_PK_AS_REQ\n");
@@ -981,7 +1031,7 @@ pkinit_client_process(krb5_context context,
return EINVAL; /* XXX */
r = pa_pkinit_parse_rep(context, plgctx, reqctx,
request, in_padata, out_padata, enctype,
- as_key);
+ as_key, encoded_previous_request);
break;
case KRB5_PADATA_PK_AS_REP_OLD:
case KRB5_PADATA_PK_AS_REQ_OLD:
@@ -998,7 +1048,7 @@ pkinit_client_process(krb5_context context,
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);
+ enctype, as_key, encoded_previous_request);
}
break;
default:
@@ -1008,6 +1058,86 @@ pkinit_client_process(krb5_context context,
return r;
}
+static krb5_error_code
+pkinit_decode_td_dh_params(krb5_data *data,
+ pkinit_context *plgctx,
+ pkinit_req_context *reqctx)
+{
+ krb5_error_code retval = ENOMEM;
+ krb5_algorithm_identifier **algId = NULL;
+ int i = 0, ok = 0, free_dh = 1;
+
+ retval = decode_krb5_td_dh_parameters(data, &algId);
+ if (retval) {
+ pkiDebug("decode_krb5_td_dh_parameters failed\n");
+ goto cleanup;
+ }
+ while (algId[i] != NULL) {
+ DH *dh = NULL;
+ unsigned char *tmp = NULL;
+ int dh_prime_bits = 0, dh_err = 0;
+
+ if (algId[i]->algorithm.length != dh_oid.length ||
+ memcmp(algId[i]->algorithm.data, dh_oid.data, dh_oid.length))
+ goto cleanup;
+ tmp = algId[i]->parameters.data;
+ dh = DH_new();
+ dh = pkinit_decode_dh_params(&dh, &tmp, algId[i]->parameters.length);
+ dh_prime_bits = BN_num_bits(dh->p);
+ pkiDebug("client sent %d DH bits server prefers %d DH bits\n",
+ reqctx->dh_size, dh_prime_bits);
+ switch(dh_prime_bits) {
+ case 1024:
+ if (!pkinit_check_dh_params(plgctx->dh_1024->p, dh->p, dh->g,
+ dh->q)) {
+ reqctx->dh_size = 1024;
+ ok = 1;
+ }
+ break;
+ case 2048:
+ if (!pkinit_check_dh_params(plgctx->dh_2048->p, dh->p, dh->g,
+ dh->q)) {
+ reqctx->dh_size = 2048;
+ ok = 1;
+ }
+ break;
+ case 4096:
+ if (!pkinit_check_dh_params(plgctx->dh_4096->p, dh->p, dh->g,
+ dh->q)) {
+ reqctx->dh_size = 4096;
+ ok = 1;
+ }
+ break;
+ default:
+ break;
+ }
+ if (!ok) {
+ DH_check(dh, &retval);
+ if (retval != 0)
+ pkiDebug("DH parameter provided by server is bad\n");
+ else
+ free_dh = 0;
+ }
+ DH_free(dh);
+ if (ok) {
+ if (free_dh) {
+ if (reqctx->dh != NULL)
+ DH_free(reqctx->dh);
+ reqctx->dh = NULL;
+ }
+ break;
+ }
+ i++;
+ }
+ if (ok)
+ retval = 0;
+cleanup:
+ if (algId != NULL)
+ free_krb5_algorithm_identifier(&algId);
+
+ return retval;
+}
+
krb5_error_code
pkinit_client_tryagain(krb5_context context,
void *plugin_context,
@@ -1028,21 +1158,149 @@ pkinit_client_tryagain(krb5_context context,
krb5_keyblock *as_key,
krb5_pa_data **out_padata)
{
+ krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
+ krb5_typed_data **typed_data = NULL;
+ krb5_data scratch;
+ pkinit_context *plgctx = (pkinit_context *)plugin_context;
+ pkinit_req_context *reqctx = (pkinit_req_context *)request_context;
+ krb5_external_principal_identifier **krb5_trusted_certifiers = NULL;
+ int i = 0, do_again = 0;
+ const unsigned char *p;
+ STACK_OF(X509_NAME) *sk_xn = NULL;
+ X509_NAME *xn = NULL;
+ STACK_OF(PKCS7_ISSUER_AND_SERIAL) *sk_is = NULL;
+ PKCS7_ISSUER_AND_SERIAL *is = NULL;
+ STACK_OF(ASN1_OCTET_STRING) *sk_id = NULL;
+ ASN1_OCTET_STRING *id = NULL;
+
+ if (reqctx->patype != in_padata->pa_type)
+ return retval;
+
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;
+ retval = decode_krb5_typed_data(&err_reply->e_data, &typed_data);
+ if (retval) {
+ pkiDebug("decode_krb5_typed_data failed\n");
+ goto cleanup;
+ }
+#ifdef DEBUG_ASN1
+ print_buffer_bin(typed_data[0]->data, typed_data[0]->length, "/tmp/client_typed_data");
+#endif
+ scratch.data = typed_data[0]->data;
+ scratch.length = typed_data[0]->length;
+
+
+ switch(typed_data[0]->type) {
+ case TD_TRUSTED_CERTIFIERS:
+ case TD_INVALID_CERTIFICATES:
+ sk_xn = sk_X509_NAME_new_null();
+ if (typed_data[0]->type == TD_TRUSTED_CERTIFIERS)
+ pkiDebug("trusted certifiers\n");
+ else
+ pkiDebug("invalid certificate\n");
+ retval = decode_krb5_td_trusted_certifiers(&scratch, &krb5_trusted_certifiers);
+ if (retval) {
+ pkiDebug("failed to decode sequence of trusted certifiers\n");
+ goto cleanup;
+ }
+ while(krb5_trusted_certifiers[i] != NULL) {
+ if (krb5_trusted_certifiers[i]->subjectName.data != NULL) {
+ p = krb5_trusted_certifiers[i]->subjectName.data;
+ xn = d2i_X509_NAME(NULL, &p, krb5_trusted_certifiers[i]->subjectName.length);
+ if (xn == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ } else {
+ char buf[256];
+ X509_NAME_oneline(xn, buf, 256);
+ if (typed_data[0]->type == TD_TRUSTED_CERTIFIERS)
+ pkiDebug("#%d cert = %s is trusted by kdc\n", i, buf);
+ else
+ pkiDebug("#%d cert = %s is invalid\n", i, buf);
+ sk_X509_NAME_push(sk_xn, xn);
+ }
+ }
+ if (krb5_trusted_certifiers[i]->issuerAndSerialNumber.data != NULL) {
+ p = krb5_trusted_certifiers[i]->issuerAndSerialNumber.data;
+ is = d2i_PKCS7_ISSUER_AND_SERIAL(NULL, &p, krb5_trusted_certifiers[i]->issuerAndSerialNumber.length);
+ if (is == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ } else {
+ char buf[256];
+ X509_NAME_oneline(is->issuer, buf, 256);
+ if (typed_data[0]->type == TD_TRUSTED_CERTIFIERS)
+ pkiDebug("#%d issuer = %s serial = %ld is trusted bu kdc\n", i, buf, ASN1_INTEGER_get(is->serial));
+ else
+ pkiDebug("#%d issuer = %s serial = %ld is invalid\n", i, buf, ASN1_INTEGER_get(is->serial));
+ }
+ PKCS7_ISSUER_AND_SERIAL_free(is);
+ }
+ if (krb5_trusted_certifiers[i]->subjectKeyIdentifier.data != NULL) {
+ p = krb5_trusted_certifiers[i]->subjectKeyIdentifier.data;
+ id = d2i_ASN1_OCTET_STRING(NULL, &p, krb5_trusted_certifiers[i]->subjectKeyIdentifier.length);
+ if (id == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ } else {
+ /* XXX */
+ }
+ ASN1_OCTET_STRING_free(id);
+ }
+ i++;
+ }
+ break;
+ case TD_DH_PARAMETERS:
+ pkiDebug("dh parameters\n");
+ retval = pkinit_init_dh_params(context, plgctx);
+ if (retval)
+ goto cleanup;
+ retval = pkinit_decode_td_dh_params(&scratch, plgctx, reqctx);
+ if (!retval)
+ do_again = 1;
+ pkinit_fini_dh_params(context, plgctx);
+ break;
+ default:
+ break;
+ }
+
+ if (do_again) {
+ krb5_enctype enctype = -1;
+ if (reqctx->dh) {
+ DH_free(reqctx->dh);
+ reqctx->dh = NULL;
+ }
+ retval = pa_pkinit_gen_req(context, plgctx, reqctx, request, in_padata,
+ out_padata, prompter, prompter_data, &enctype, as_key);
+ if (retval)
+ goto cleanup;
+ }
+
+ retval = 0;
+cleanup:
+ if (sk_xn != NULL)
+ sk_X509_NAME_pop_free(sk_xn, X509_NAME_free);
+
+ if (krb5_trusted_certifiers != NULL)
+ free_krb5_external_principal_identifier(&krb5_trusted_certifiers);
+
+ if (typed_data != NULL)
+ free_krb5_typed_data(&typed_data);
+
+ return retval;
}
#define PKINIT_REQ_CTX_MAGIC 0xdeadbeef
void
-pkinit_client_req_init(krb5_context contex,
+pkinit_client_req_init(krb5_context context,
void *plugin_context,
void **request_context)
{
pkinit_req_context *reqctx;
+ pkinit_context *plgctx = (pkinit_context *)plugin_context;
*request_context = NULL;
@@ -1051,8 +1309,13 @@ pkinit_client_req_init(krb5_context contex,
return;
reqctx->magic = PKINIT_REQ_CTX_MAGIC;
- reqctx->version = 0;
reqctx->dh = NULL;
+ reqctx->dh_size = 1024;
+ reqctx->require_eku = plgctx->require_eku;
+ reqctx->require_san = plgctx->require_san;
+ reqctx->require_hostname_match = 0;
+ reqctx->allow_upn = plgctx->allow_upn;
+ reqctx->require_crl_checking = plgctx->require_crl_checking;
*request_context = (void *) reqctx;
pkiDebug("%s: returning reqctx at %p\n", __FUNCTION__, reqctx);
@@ -1098,9 +1361,11 @@ using_pkcs11(void)
static char *module_name = "opensc-pkcs11.so";
static void *module = NULL;
-static int slotid = 0;
+static unsigned int slotid = 0;
static CK_SESSION_HANDLE session = CK_INVALID_HANDLE;
static CK_FUNCTION_LIST_PTR p11 = NULL;
+static CK_BYTE cert_id[MAX_ID_SIZE];
+static int cert_id_len;
void *
C_LoadModule(const char *modname, CK_FUNCTION_LIST_PTR_PTR p11p)
@@ -1217,17 +1482,18 @@ pkinit_get_client_cert(const char *principal,
{
#ifndef WITHOUT_PKCS11
CK_OBJECT_CLASS cls;
- CK_OBJECT_HANDLE object;
+ CK_OBJECT_HANDLE objs[MAX_N_OBJS];
CK_ATTRIBUTE attrs[2];
- CK_ULONG count, r;
+ CK_ULONG count;
CK_CERTIFICATE_TYPE certtype;
- krb5_octet cert[4096], *cp;
- const unsigned char *p = cp;
+ krb5_octet cert[MAX_CERT_SIZE];
+ const unsigned char *cp;
+ int r;
#endif
if (!using_pkcs11()) {
if ((*client_cert = get_cert(filename)) == NULL)
- return -1;
+ return KRB5KDC_ERR_PREAUTH_FAILED;
else
return 0;
}
@@ -1255,32 +1521,38 @@ pkinit_get_client_cert(const char *principal,
}
/* Look for x.509 cert */
- if ((r = p11->C_FindObjects(session, &object, 1, &count)) != CKR_OK
- || count != 1) {
+ if ((r = p11->C_FindObjects(session, objs, 6, &count)) != CKR_OK
+ || count <= 0) {
pkiDebug("can't find any certs %x\n", r);
return KRB5KDC_ERR_PREAUTH_FAILED;
}
p11->C_FindObjectsFinal(session);
+ pkiDebug("found %d certs\n", (int) count);
/*
- * Read the cert off the card.
- * I can't find any way to realiably get the size before doing the read.
+ * Read the cert and id off the card.
+ * I can't find any way to reliably 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) {
+
+ attrs[1].type = CKA_ID;
+ attrs[1].pValue = cert_id;
+ attrs[1].ulValueLen = sizeof cert_id;
+
+ /* Just take the first one. */
+ if ((r = p11->C_GetAttributeValue(session, objs[0], attrs, 2)) != 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);
-
+ pkiDebug("cert size %d id %d\n", (int) attrs[0].ulValueLen, (int) cert_id[0]);
+ cert_id_len = attrs[1].ulValueLen;
+ cp = (unsigned char *) cert;
+ *client_cert = d2i_X509(NULL, &cp, (int) attrs[0].ulValueLen);
+ if (*client_cert == NULL)
+ return KRB5KDC_ERR_PREAUTH_FAILED;
#endif
return 0;
}
@@ -1294,11 +1566,14 @@ pkinit_sign_data(unsigned char *data,
{
#ifndef WITHOUT_PKCS11
CK_OBJECT_CLASS cls;
- CK_OBJECT_HANDLE object;
- CK_ATTRIBUTE attrs[1];
- CK_ULONG count, len, r;
+ CK_OBJECT_HANDLE objs[MAX_N_OBJS];
+ CK_ATTRIBUTE attrs[4];
+ CK_ULONG count, len;
CK_MECHANISM mech;
+ CK_BBOOL bool;
+ CK_KEY_TYPE keytype;
unsigned char sigbuf[1024], *cp;
+ int r;
#endif
if (!using_pkcs11()) {
@@ -1310,29 +1585,52 @@ pkinit_sign_data(unsigned char *data,
}
#ifndef WITHOUT_PKCS11
- /* Find the private key - we just take the first one for now */
+ /*
+ * Look for a key that's:
+ * 1. private
+ * 2. capable of signing
+ * 3. RSA (this may be wrong but it's all we can do for now)
+ * 4. matches the id of the cert we chose
+ *
+ * pkcs11 says the id of the key doesn't have to match that of the cert, but
+ * I can't figure out any other way to decide which key to use.
+ */
+
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) {
+ bool = TRUE;
+ attrs[1].type = CKA_SIGN;
+ attrs[1].pValue = &bool;
+ attrs[1].ulValueLen = sizeof bool;
+
+ keytype = CKK_RSA;
+ attrs[2].type = CKA_KEY_TYPE;
+ attrs[2].pValue = &keytype;
+ attrs[2].ulValueLen = sizeof keytype;
+
+ attrs[3].type = CKA_ID;
+ attrs[3].pValue = cert_id;
+ attrs[3].ulValueLen = cert_id_len;
+
+ if (p11->C_FindObjectsInit(session, attrs, 4) != 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");
+ r = p11->C_FindObjects(session, objs, 6, &count);
p11->C_FindObjectsFinal(session);
+ pkiDebug("found %d private keys %x\n", (int) count, (int) r);
+ if (r != CKR_OK || count < 1)
+ return KRB5KDC_ERR_PREAUTH_FAILED;
mech.mechanism = CKM_SHA1_RSA_PKCS;
mech.pParameter = NULL;
mech.ulParameterLen = 0;
- if ((r = p11->C_SignInit(session, &mech, object)) != CKR_OK) {
+ if ((r = p11->C_SignInit(session, &mech, objs[0])) != CKR_OK) {
pkiDebug("fail C_SignInit %x\n", (int) r);
return KRB5KDC_ERR_PREAUTH_FAILED;
}
@@ -1356,32 +1654,50 @@ pkinit_sign_data(unsigned char *data,
}
static krb5_error_code
-client_create_dh(DH ** dh_client,
+client_create_dh(int dh_size,
+ DH ** dh_client,
unsigned char **dh_params,
int *dh_params_len,
- unsigned char **dh_pubkey, int *dh_pubkey_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;
+ ASN1_INTEGER *pub_key = NULL;
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
+ if (*dh_client == NULL) {
+ if ((*dh_client = DH_new()) == NULL)
+ goto cleanup;
+ if (((*dh_client)->g = BN_new()) == NULL ||
+ ((*dh_client)->q = BN_new()) == NULL)
+ goto cleanup;
+
+ switch(dh_size) {
+ case 1024:
+ pkiDebug("client uses 1024 DH keys\n");
+ (*dh_client)->p = get_rfc2409_prime_1024(NULL);
+ break;
+ case 2048:
+ pkiDebug("client uses 2048 DH keys\n");
+ (*dh_client)->p = BN_bin2bn(pkinit_2048_dhprime,
+ sizeof(pkinit_2048_dhprime), NULL);
+ break;
+ case 4096:
+ pkiDebug("client uses 4096 DH keys\n");
+ (*dh_client)->p = BN_bin2bn(pkinit_4096_dhprime,
+ sizeof(pkinit_4096_dhprime), NULL);
+ break;
+ default:
+ goto cleanup;
+ }
+
+ BN_set_word(((*dh_client)->g), DH_GENERATOR_2);
+ BN_rshift1((*dh_client)->q, (*dh_client)->p);
+ }
- 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) {
@@ -1410,37 +1726,10 @@ client_create_dh(DH ** dh_client,
/* 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;
+ retval = pkinit_encode_dh_params((*dh_client)->p, (*dh_client)->g,
+ (*dh_client)->q, dh_params, dh_params_len);
+ if (retval)
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
@@ -1466,16 +1755,13 @@ client_create_dh(DH ** dh_client,
cleanup:
if (*dh_client != NULL)
DH_free(*dh_client);
+ *dh_client = NULL;
if (*dh_params != NULL)
free(*dh_params);
+ *dh_params = NULL;
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);
+ *dh_pubkey = NULL;
if (pub_key != NULL)
ASN1_INTEGER_free(pub_key);
@@ -1670,12 +1956,87 @@ static krb5_preauthtype supported_client_pa_types[] = {
0
};
+void
+pkinit_fini_client_profile(krb5_context context, pkinit_context *plgctx)
+{
+ /* This should clean up anything allocated in pkinit_init_client_profile */
+}
+
+static krb5_error_code
+pkinit_init_client_profile(krb5_context context, pkinit_context *plgctx)
+{
+ profile_t profile;
+ krb5_error_code retval;
+
+#if 0
+ /*
+ * These may not even be necessary!
+ */
+ retval = krb5_get_profile(context, &profile);
+ if (retval) {
+ krb5_set_error_message(context, retval,
+ "Could not get profile handle");
+ goto errout;
+ }
+ profile_get_boolean(profile, "libdefaults", NULL,
+ "pkinit_win2k",
+ plgctx->win2k_target, &plgctx->win2k_target);
+ profile_get_boolean(profile, "libdefaults", NULL,
+ "pkinit_win2k_require_binding",
+ plgctx->win2k_require_cksum,
+ &plgctx->win2k_require_cksum);
+
+ profile_get_boolean(profile, "libdefaults", NULL,
+ "pkinit_require_eku",
+ plgctx->require_eku, &plgctx->require_eku);
+ profile_get_boolean(profile, "libdefaults", NULL,
+ "pkinit_require_krbtgt_otherName",
+ plgctx->require_san, &plgctx->require_san);
+ profile_get_boolean(profile, "libdefaults", NULL,
+ "pkinit_require_hostname_match",
+ plgctx->require_hostname_match,
+ &plgctx->require_hostname_match);
+ profile_release(profile);
+ /* set plgctx defaults for:
+ - pkinit_anchors
+ - pkinit_pool
+ - pkinit_revoke
+ */
+#endif
+
+ return 0;
+}
+
+static int pkinit_client_plugin_init(krb5_context context, void **blob)
+{
+ krb5_error_code retval;
+ pkinit_context *plgctx;
+
+ retval = pkinit_lib_init(context, blob);
+ plgctx = *blob;
+
+ if (0 == retval)
+ retval = pkinit_init_client_profile(context, plgctx);
+
+errout:
+ return retval;
+}
+
+static void
+pkinit_client_plugin_fini(krb5_context context, void *blob)
+{
+ pkinit_context *plgctx = (pkinit_context *)blob;
+
+ pkinit_fini_client_profile(context, plgctx);
+ pkinit_lib_fini(context, blob);
+}
+
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_plugin_init, /* (*init) */
+ pkinit_client_plugin_fini, /* (*fini) */
pkinit_client_get_flags, /* (*flags) */
pkinit_client_req_init, /* (*client_req_init) */
pkinit_client_req_fini, /* (*client_req_fini) */
diff --git a/src/plugins/preauth/pkinit/pkinit_lib.c b/src/plugins/preauth/pkinit/pkinit_lib.c
index 5b6cc1f..ce97444 100644
--- a/src/plugins/preauth/pkinit/pkinit_lib.c
+++ b/src/plugins/preauth/pkinit/pkinit_lib.c
@@ -208,17 +208,26 @@ pkinit_lib_init(krb5_context context, void **blob)
krb5_error_code retval = ENOMEM;
int tmp = 0;
- plgctx = (pkinit_context *) malloc(sizeof(*plgctx));
+ plgctx = (pkinit_context *) calloc(1, sizeof(*plgctx));
if (plgctx == NULL)
goto out;
plgctx->magic = PKINIT_CTX_MAGIC;
- plgctx->version = 0;
- plgctx->require_eku = 0;
- plgctx->require_san = 0;
+ plgctx->require_eku = 1;
+ plgctx->require_san = 1;
plgctx->allow_upn = 0;
plgctx->require_crl_checking = 0;
+ plgctx->ctx_identity = NULL;
+ plgctx->ctx_anchors = NULL;
+ plgctx->ctx_pool = NULL;
+ plgctx->ctx_revoke = NULL;
+ plgctx->ctx_ocsp = NULL;
+ plgctx->ctx_mapping_file = NULL;
+ plgctx->ctx_princ_in_cert = 0;
+ plgctx->ctx_dh_min_bits = 0;
+ plgctx->ctx_allow_proxy_certs = 0;
+
tmp = OBJ_create("1.3.6.1.5.2.2", "id-pkinit-san", "KRB5PrincipalName");
if (tmp == NID_undef)
goto out;
@@ -278,6 +287,11 @@ pkinit_lib_init(krb5_context context, void **blob)
/* initialize openssl routines */
openssl_init();
+
+ plgctx->dh_1024 = NULL;
+ plgctx->dh_2048 = NULL;
+ plgctx->dh_4096 = NULL;
+
retval = 0;
out:
@@ -314,6 +328,197 @@ openssl_init()
}
}
+void
+pkinit_fini_dh_params(krb5_context context, pkinit_context *plgctx)
+{
+ if (plgctx->dh_1024)
+ DH_free(plgctx->dh_1024);
+ if (plgctx->dh_2048)
+ DH_free(plgctx->dh_2048);
+ if (plgctx->dh_4096)
+ DH_free(plgctx->dh_4096);
+
+ plgctx->dh_1024 = plgctx->dh_2048 = plgctx->dh_4096 = NULL;
+}
+
+krb5_error_code
+pkinit_init_dh_params(krb5_context context, pkinit_context *plgctx)
+{
+ krb5_error_code retval = ENOMEM;
+ plgctx->dh_1024 = DH_new();
+ if (plgctx->dh_1024 == NULL)
+ goto cleanup;
+ plgctx->dh_1024->p = BN_bin2bn(pkinit_1024_dhprime,
+ sizeof(pkinit_1024_dhprime), NULL);
+ if ((plgctx->dh_1024->g = BN_new()) == NULL ||
+ (plgctx->dh_1024->q = BN_new()) == NULL)
+ goto cleanup;
+ BN_set_word(plgctx->dh_1024->g, DH_GENERATOR_2);
+ BN_rshift1(plgctx->dh_1024->q, plgctx->dh_1024->p);
+
+ plgctx->dh_2048 = DH_new();
+ if (plgctx->dh_2048 == NULL)
+ goto cleanup;
+ plgctx->dh_2048->p = BN_bin2bn(pkinit_2048_dhprime,
+ sizeof(pkinit_2048_dhprime), NULL);
+ if ((plgctx->dh_2048->g = BN_new()) == NULL ||
+ (plgctx->dh_2048->q = BN_new()) == NULL)
+ goto cleanup;
+ BN_set_word(plgctx->dh_2048->g, DH_GENERATOR_2);
+ BN_rshift1(plgctx->dh_2048->q, plgctx->dh_2048->p);
+
+ plgctx->dh_4096 = DH_new();
+ if (plgctx->dh_4096 == NULL)
+ goto cleanup;
+ plgctx->dh_4096->p = BN_bin2bn(pkinit_4096_dhprime,
+ sizeof(pkinit_4096_dhprime), NULL);
+ if ((plgctx->dh_4096->g = BN_new()) == NULL ||
+ (plgctx->dh_4096->q = BN_new()) == NULL)
+ goto cleanup;
+ BN_set_word(plgctx->dh_4096->g, DH_GENERATOR_2);
+ BN_rshift1(plgctx->dh_4096->q, plgctx->dh_4096->p);
+
+ retval = 0;
+
+cleanup:
+ if (retval) {
+ pkinit_fini_dh_params(context, plgctx);
+ }
+
+ return retval;
+}
+
+krb5_error_code
+pkinit_encode_dh_params(BIGNUM *p, BIGNUM *g, BIGNUM *q,
+ unsigned char **buf, int *buf_len)
+{
+ krb5_error_code retval = ENOMEM;
+ int bufsize = 0, r = 0;
+ unsigned char *tmp = NULL;
+ ASN1_INTEGER *ap = NULL, *ag = NULL, *aq = NULL;
+
+ if ((ap = BN_to_ASN1_INTEGER(p, NULL)) == NULL)
+ goto cleanup;
+ if ((ag = BN_to_ASN1_INTEGER(g, NULL)) == NULL)
+ goto cleanup;
+ if ((aq = BN_to_ASN1_INTEGER(q, NULL)) == NULL)
+ goto cleanup;
+ bufsize = i2d_ASN1_INTEGER(ap, NULL);
+ bufsize += i2d_ASN1_INTEGER(ag, NULL);
+ bufsize += i2d_ASN1_INTEGER(aq, NULL);
+
+ r = ASN1_object_size(1, bufsize, V_ASN1_SEQUENCE);
+
+ tmp = *buf = malloc((size_t) r);
+ if (tmp == NULL)
+ goto cleanup;
+
+ ASN1_put_object(&tmp, 1, bufsize, V_ASN1_SEQUENCE, V_ASN1_UNIVERSAL);
+
+ i2d_ASN1_INTEGER(ap, &tmp);
+ i2d_ASN1_INTEGER(ag, &tmp);
+ i2d_ASN1_INTEGER(aq, &tmp);
+
+ *buf_len = r;
+
+ retval = 0;
+
+cleanup:
+ if (ap != NULL)
+ ASN1_INTEGER_free(ap);
+ if (ag != NULL)
+ ASN1_INTEGER_free(ag);
+ if (aq != NULL)
+ ASN1_INTEGER_free(aq);
+
+ return retval;
+}
+
+DH *
+pkinit_decode_dh_params(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);
+
+}
+
+int
+pkinit_check_dh_params(BIGNUM * p1, BIGNUM * p2, BIGNUM * g1, BIGNUM * q1)
+{
+ BIGNUM *g2 = NULL, *q2 = NULL;
+ int retval = -1;
+
+ if (!BN_cmp(p1, p2)) {
+ g2 = BN_new();
+ BN_set_word(g2, DH_GENERATOR_2);
+ if (!BN_cmp(g1, g2)) {
+ q2 = BN_new();
+ BN_rshift1(q2, p1);
+ if (!BN_cmp(q1, q2)) {
+ pkiDebug("good %d dhparams\n", BN_num_bits(p1));
+ retval = 0;
+ } else
+ pkiDebug("bad group 2 q dhparameter\n");
+ BN_free(q2);
+ } else
+ pkiDebug("bad g dhparameter\n");
+ BN_free(g2);
+ } else
+ pkiDebug("p is not well-known group 2 dhparameter\n");
+
+ return retval;
+}
+
static int
purpose_print(BIO * bio, X509 * cert, X509_PURPOSE * pt)
{
@@ -618,6 +823,7 @@ pkcs7_signeddata_verify(unsigned char *signed_data,
i = X509_verify_cert(&cert_ctx);
if (i <= 0) {
int j = X509_STORE_CTX_get_error(&cert_ctx);
+ *cert = X509_dup(cert_ctx.current_cert);
switch(j) {
case X509_V_ERR_CERT_REVOKED:
retval = KRB5KDC_ERR_REVOKED_CERTIFICATE;
@@ -686,6 +892,8 @@ pkcs7_signeddata_verify(unsigned char *signed_data,
BIO_free(out);
if (store != NULL)
X509_STORE_free(store);
+ if (filename != NULL)
+ free(filename);
return retval;
}
@@ -1053,7 +1261,10 @@ verify_id_pkinit_san(X509 *x,
}
int
-verify_id_pkinit_eku(X509 * x, krb5_preauthtype pa_type, pkinit_context *plgctx)
+verify_id_pkinit_eku(pkinit_context *plgctx,
+ X509 * x,
+ krb5_preauthtype pa_type,
+ int require_eku)
{
int i = 0;
int ok = 0;
@@ -1122,8 +1333,10 @@ verify_id_pkinit_eku(X509 * x, krb5_preauthtype pa_type, pkinit_context *plgctx)
cleanup:
if (!ok) {
pkiDebug("didn't find extended key usage (EKU) for pkinit\n");
- if (!plgctx->require_eku)
+ if (0 == require_eku) {
+ pkiDebug("configuration says ignore missing EKU\n");
ok = 1;
+ }
}
return ok;
@@ -1140,8 +1353,7 @@ pkinit_octetstring2key(krb5_context context,
unsigned char *buf = NULL;
unsigned char md[SHA_DIGEST_LENGTH];
unsigned char counter;
- unsigned char offset;
- size_t keybytes, keylength;
+ size_t keybytes, keylength, offset;
int i;
krb5_data random_data;
@@ -1561,11 +1773,11 @@ 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);
+ 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)->kdcPkId.data);
free(*in);
}
@@ -1573,11 +1785,11 @@ 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);
+ free((*in)->signedAuthPack.data);
if ((*in)->kdcCert.data != NULL)
- free((*in)->kdcCert.data);
+ free((*in)->kdcCert.data);
if ((*in)->encryptionCert.data != NULL)
- free((*in)->encryptionCert.data);
+ free((*in)->encryptionCert.data);
if ((*in)->trustedCertifiers != NULL)
free_krb5_trusted_ca(&(*in)->trustedCertifiers);
free(*in);
@@ -1587,9 +1799,9 @@ void free_krb5_reply_key_pack(krb5_reply_key_pack **in)
{
if (*in == NULL) return;
if ((*in)->replyKey.contents != NULL)
- free((*in)->replyKey.contents);
+ free((*in)->replyKey.contents);
if ((*in)->asChecksum.contents != NULL)
- free((*in)->asChecksum.contents);
+ free((*in)->asChecksum.contents);
free(*in);
}
@@ -1597,7 +1809,7 @@ 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)->replyKey.contents);
free(*in);
}
@@ -1609,19 +1821,19 @@ void free_krb5_auth_pack(krb5_auth_pack **in)
* 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)->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)->pkAuthenticator.paChecksum.contents);
free(*in);
}
void free_krb5_auth_pack_draft9(krb5_context context,
- krb5_auth_pack_draft9 **in)
+ krb5_auth_pack_draft9 **in)
{
if ((*in) == NULL) return;
krb5_free_principal(context, (*in)->pkAuthenticator.kdcName);
@@ -1632,16 +1844,16 @@ 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;
+ 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);
}
@@ -1693,6 +1905,35 @@ void free_krb5_trusted_ca(krb5_trusted_ca ***in)
}
free(*in);
}
+
+void free_krb5_typed_data(krb5_typed_data ***in)
+{
+ int i = 0;
+ if (*in == NULL) return;
+ while ((*in)[i] != NULL) {
+ if ((*in)[i]->data != NULL)
+ free((*in)[i]->data);
+ free((*in)[i]);
+ i++;
+ }
+ free(*in);
+}
+
+void free_krb5_algorithm_identifier(krb5_algorithm_identifier ***in)
+{
+ int i = 0;
+ if (*in == NULL) return;
+ while ((*in)[i] != NULL) {
+ if ((*in)[i]->algorithm.data != NULL)
+ free((*in)[i]->algorithm.data);
+ if ((*in)[i]->parameters.data != NULL)
+ free((*in)[i]->parameters.data);
+ 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));
@@ -1774,3 +2015,12 @@ void init_krb5_pa_pk_as_rep_draft9(krb5_pa_pk_as_rep_draft9 **in)
(*in)->u.encKeyPack.length = 0;
(*in)->u.encKeyPack.data = NULL;
}
+
+void init_krb5_typed_data(krb5_typed_data **in)
+{
+ (*in) = malloc(sizeof(krb5_typed_data));
+ if ((*in) == NULL) return;
+ (*in)->type = 0;
+ (*in)->length = 0;
+ (*in)->data = NULL;
+}
diff --git a/src/plugins/preauth/pkinit/pkinit_srv.c b/src/plugins/preauth/pkinit/pkinit_srv.c
index 1dc791b..145ddc4 100644
--- a/src/plugins/preauth/pkinit/pkinit_srv.c
+++ b/src/plugins/preauth/pkinit/pkinit_srv.c
@@ -29,9 +29,11 @@
*/
#include <stdio.h>
+#include <string.h>
#include <krb5/krb5.h>
#include <krb5/preauth_plugin.h>
#include <k5-int-pkinit.h>
+#include <profile.h>
#include <openssl/x509.h>
#include <openssl/pkcs7.h>
@@ -77,135 +79,51 @@ static krb5_error_code pkinit_server_return_padata
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)
+server_check_dh(pkinit_context *plgctx, krb5_octet_data *dh_params, int minbits)
{
DH *dh = NULL;
unsigned char *tmp = NULL;
- BIGNUM *p = NULL;
+ int dh_prime_bits;
krb5_error_code retval = KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
- tmp = dh_params;
+ tmp = dh_params->data;
dh = DH_new();
- dh = decode_dhparams(&dh, &tmp, dh_params_len);
+ dh = pkinit_decode_dh_params(&dh, &tmp, dh_params->length);
if (dh == NULL) {
pkiDebug("failed to decode dhparams\n");
goto cleanup;
}
/* KDC SHOULD check to see if the key parameters satisfy its policy */
+ dh_prime_bits = BN_num_bits(dh->p);
+ if (minbits && dh_prime_bits < minbits) {
+ pkiDebug("client sent dh params with %d bits, we require %d\n",
+ dh_prime_bits, minbits);
+ goto cleanup;
+ }
+
/* check dhparams is group 2 */
- p = BN_bin2bn(pkinit_1024_dhprime, sizeof(pkinit_1024_dhprime), NULL);
- if (p && !check_dh(p, dh->p, dh->g, dh->q)) {
+ if (!pkinit_check_dh_params(plgctx->dh_1024->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)) {
+ if (!pkinit_check_dh_params(plgctx->dh_2048->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)) {
+ if (!pkinit_check_dh_params(plgctx->dh_4096->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;
@@ -230,7 +148,7 @@ server_process_dh(DH ** dh_server,
/* unpack client's DHparams keys */
dh = DH_new();
p = dh_params;
- dh = decode_dhparams(&dh, &p, dh_params_len);
+ dh = pkinit_decode_dh_params(&dh, &p, dh_params_len);
if (dh == NULL)
return KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
@@ -303,35 +221,323 @@ server_process_dh(DH ** dh_server,
}
static krb5_error_code
-pkinit_create_edata(krb5_error_code err_code,
- krb5_data **e_data)
+pkinit_create_sequence_of_principal_identifiers(STACK_OF(X509) *sk, int type, krb5_data **out_data)
{
krb5_error_code retval = KRB5KRB_ERR_GENERIC;
- STACK_OF(X509) *trusted_CAs = NULL;
krb5_external_principal_identifier **krb5_trusted_certifiers = NULL;
- char filename[] = "/etc/grid-security/certificates/ca-bundle.crt";
- krb5_data *data = NULL;
+ krb5_data *td_certifiers = NULL, *data = NULL;
+ krb5_typed_data **typed_data = NULL;
+
+ retval = create_krb5_trustedCertifiers(sk, &krb5_trusted_certifiers);
+ if (retval) {
+ pkiDebug("create_krb5_trustedCertifiers failed\n");
+ goto cleanup;
+ }
+ retval = encode_krb5_td_trusted_certifiers(krb5_trusted_certifiers, &td_certifiers);
+ if (retval) {
+ pkiDebug("encode_krb5_td_trusted_certifiers failed\n");
+ goto cleanup;
+ }
+#ifdef DEBUG_ASN1
+ print_buffer_bin(td_certifiers->data, td_certifiers->length, "/tmp/kdc_td_certifiers");
+#endif
+ typed_data = malloc (2 * sizeof(krb5_typed_data *));
+ if (typed_data == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ typed_data[1] = NULL;
+ init_krb5_typed_data(&typed_data[0]);
+ if (typed_data == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ typed_data[0]->type = type;
+ typed_data[0]->length = td_certifiers->length;
+ typed_data[0]->data = td_certifiers->data;
+ retval = encode_krb5_typed_data(typed_data, &data);
+ if (retval) {
+ pkiDebug("encode_krb5_typed_data failed\n");
+ goto cleanup;
+ }
+#ifdef DEBUG_ASN1
+ print_buffer_bin(data->data, data->length, "/tmp/kdc_edata");
+#endif
+ *out_data = malloc(sizeof(krb5_data));
+ (*out_data)->length = data->length;
+ (*out_data)->data = malloc(data->length);
+ memcpy((*out_data)->data, data->data, data->length);
+
+ retval = 0;
+
+cleanup:
+ if (krb5_trusted_certifiers != NULL)
+ free_krb5_external_principal_identifier(&krb5_trusted_certifiers);
+
+ if (data != NULL) {
+ if (data->data != NULL)
+ free(data->data);
+ free(data);
+ }
+
+ if (typed_data != NULL)
+ free_krb5_typed_data(&typed_data);
+
+ return retval;
+}
+
+static krb5_error_code
+pkinit_create_td_trusted_certifiers(krb5_data **out_data)
+{
+ krb5_error_code retval = KRB5KRB_ERR_GENERIC;
+ STACK_OF(X509) *trusted_CAs = NULL;
+ char *filename = NULL;
+
+ 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");
+ goto cleanup;
+ }
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");
+ if (retval || trusted_CAs == NULL) {
+ pkiDebug("failed to load CA certs from %s\n", filename);
+ goto cleanup;
+ }
+
+ retval = pkinit_create_sequence_of_principal_identifiers(trusted_CAs, TD_TRUSTED_CERTIFIERS, out_data);
+
+cleanup:
+ if (filename != NULL)
+ free(filename);
+
+ return retval;
+}
+
+static krb5_error_code
+pkinit_create_td_invalid_certificate(X509 *cert, krb5_data **out_data)
+{
+ krb5_error_code retval = KRB5KRB_ERR_GENERIC;
+ STACK_OF(X509) *sk = NULL;
+
+ sk = sk_X509_new_null();
+ sk_X509_push(sk, cert);
+
+ retval = pkinit_create_sequence_of_principal_identifiers(sk, TD_INVALID_CERTIFICATES, out_data);
+
+cleanup:
+ if (sk != NULL)
+ sk_X509_free(sk);
+
+ return retval;
+}
+
+static krb5_error_code
+pkinit_create_td_dh_parameters(pkinit_context *plgctx, krb5_data **out_data)
+{
+ krb5_error_code retval = ENOMEM;
+ int buf1_len = 0, buf2_len = 0, buf3_len = 0, i = 0;
+ unsigned char *buf1 = NULL, *buf2 = NULL, *buf3 = NULL;
+ krb5_typed_data **typed_data = NULL;
+ krb5_data *data = NULL, *encoded_algId = NULL;
+ krb5_algorithm_identifier **algId = NULL;
+
+ if (plgctx->ctx_dh_min_bits > 4096)
+ goto cleanup;
+
+ if (plgctx->ctx_dh_min_bits <= 1024) {
+ retval = pkinit_encode_dh_params(plgctx->dh_1024->p,
+ plgctx->dh_1024->g, plgctx->dh_1024->q, &buf1, &buf1_len);
+ if (retval)
goto cleanup;
- }
- retval = encode_krb5_td_trusted_certifiers(krb5_trusted_certifiers,
- &data);
- }
+ }
+ if (plgctx->ctx_dh_min_bits <= 2048) {
+ retval = pkinit_encode_dh_params(plgctx->dh_2048->p,
+ plgctx->dh_2048->g, plgctx->dh_2048->q, &buf2, &buf2_len);
+ if (retval)
+ goto cleanup;
+ }
+ retval = pkinit_encode_dh_params(plgctx->dh_4096->p,
+ plgctx->dh_4096->g, plgctx->dh_4096->q, &buf3, &buf3_len);
+ if (retval)
+ goto cleanup;
+
+ if (plgctx->ctx_dh_min_bits <= 1024) {
+ algId = malloc(4 * sizeof(krb5_algorithm_identifier *));
+ if (algId == NULL)
+ goto cleanup;
+ algId[3] = NULL;
+ algId[0] = malloc(sizeof(krb5_algorithm_identifier));
+ if (algId[0] == NULL)
+ goto cleanup;
+ algId[0]->parameters.data = malloc(buf2_len);
+ if (algId[0]->parameters.data == NULL)
+ goto cleanup;
+ memcpy(algId[0]->parameters.data, buf2, buf2_len);
+ algId[0]->parameters.length = buf2_len;
+ algId[0]->algorithm = dh_oid;
+
+ algId[1] = malloc(sizeof(krb5_algorithm_identifier));
+ if (algId[1] == NULL)
+ goto cleanup;
+ algId[1]->parameters.data = malloc(buf3_len);
+ if (algId[1]->parameters.data == NULL)
+ goto cleanup;
+ memcpy(algId[1]->parameters.data, buf3, buf3_len);
+ algId[1]->parameters.length = buf3_len;
+ algId[1]->algorithm = dh_oid;
+
+ algId[2] = malloc(sizeof(krb5_algorithm_identifier));
+ if (algId[2] == NULL)
+ goto cleanup;
+ algId[2]->parameters.data = malloc(buf1_len);
+ if (algId[2]->parameters.data == NULL)
+ goto cleanup;
+ memcpy(algId[2]->parameters.data, buf1, buf1_len);
+ algId[2]->parameters.length = buf1_len;
+ algId[2]->algorithm = dh_oid;
+
+ } else if (plgctx->ctx_dh_min_bits <= 2048) {
+ algId = malloc(3 * sizeof(krb5_algorithm_identifier *));
+ if (algId == NULL)
+ goto cleanup;
+ algId[2] = NULL;
+ algId[0] = malloc(sizeof(krb5_algorithm_identifier));
+ if (algId[0] == NULL)
+ goto cleanup;
+ algId[0]->parameters.data = malloc(buf2_len);
+ if (algId[0]->parameters.data == NULL)
+ goto cleanup;
+ memcpy(algId[0]->parameters.data, buf2, buf2_len);
+ algId[0]->parameters.length = buf2_len;
+ algId[0]->algorithm = dh_oid;
+
+ algId[1] = malloc(sizeof(krb5_algorithm_identifier));
+ if (algId[1] == NULL)
+ goto cleanup;
+ algId[1]->parameters.data = malloc(buf3_len);
+ if (algId[1]->parameters.data == NULL)
+ goto cleanup;
+ memcpy(algId[1]->parameters.data, buf3, buf3_len);
+ algId[1]->parameters.length = buf3_len;
+ algId[1]->algorithm = dh_oid;
+
+ } else if (plgctx->ctx_dh_min_bits <= 4096) {
+ algId = malloc(2 * sizeof(krb5_algorithm_identifier *));
+ if (algId == NULL)
+ goto cleanup;
+ algId[1] = NULL;
+ algId[0] = malloc(sizeof(krb5_algorithm_identifier));
+ if (algId[0] == NULL)
+ goto cleanup;
+ algId[0]->parameters.data = malloc(buf3_len);
+ if (algId[0]->parameters.data == NULL)
+ goto cleanup;
+ memcpy(algId[0]->parameters.data, buf3, buf3_len);
+ algId[0]->parameters.length = buf3_len;
+ algId[0]->algorithm = dh_oid;
+
+ }
+ retval = encode_krb5_td_dh_parameters(algId, &encoded_algId);
+ if (retval)
+ goto cleanup;
+#ifdef DEBUG_ASN1
+ print_buffer_bin(encoded_algId->data, encoded_algId->length, "/tmp/kdc_td_dh_params");
+#endif
+ typed_data = malloc (2 * sizeof(krb5_typed_data *));
+ if (typed_data == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ typed_data[1] = NULL;
+ init_krb5_typed_data(&typed_data[0]);
+ if (typed_data == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+ typed_data[0]->type = TD_DH_PARAMETERS;
+ typed_data[0]->length = encoded_algId->length;
+ typed_data[0]->data = encoded_algId->data;
+ retval = encode_krb5_typed_data(typed_data, &data);
+ if (retval) {
+ pkiDebug("encode_krb5_typed_data failed\n");
+ goto cleanup;
+ }
+#ifdef DEBUG_ASN1
+ print_buffer_bin(data->data, data->length, "/tmp/kdc_edata");
+#endif
+ *out_data = malloc(sizeof(krb5_data));
+ if (*out_data == NULL)
+ goto cleanup;
+ (*out_data)->length = data->length;
+ (*out_data)->data = malloc(data->length);
+ if ((*out_data)->data == NULL) {
+ free(*out_data);
+ *out_data = NULL;
+ goto cleanup;
+ }
+ memcpy((*out_data)->data, data->data, data->length);
+
retval = 0;
cleanup:
+
+ if (buf1 != NULL)
+ free(buf1);
+ if (buf2 != NULL)
+ free(buf2);
+ if (buf3 != NULL)
+ free(buf3);
if (data != NULL) {
if (data->data != NULL)
free(data->data);
free(data);
}
- if (krb5_trusted_certifiers != NULL)
- free_krb5_external_principal_identifier(&krb5_trusted_certifiers);
+ if (typed_data != NULL)
+ free_krb5_typed_data(&typed_data);
+ if (encoded_algId != NULL)
+ free(encoded_algId);
+
+ if (algId != NULL) {
+ while(algId[i] != NULL) {
+ if (algId[i]->parameters.data != NULL)
+ free(algId[i]->parameters.data);
+ free(algId[i]);
+ i++;
+ }
+ free(algId);
+ }
+
+ return retval;
+}
+
+static krb5_error_code
+pkinit_create_edata(krb5_error_code err_code,
+ X509 *cert,
+ pkinit_context *plgctx,
+ krb5_data **e_data)
+{
+ krb5_error_code retval = KRB5KRB_ERR_GENERIC;
+
+ switch(err_code) {
+ case KRB5KDC_ERR_CANT_VERIFY_CERTIFICATE:
+ retval = pkinit_create_td_trusted_certifiers(e_data);
+ break;
+ case KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED:
+ retval = pkinit_create_td_dh_parameters(plgctx, e_data);
+ break;
+ case KRB5KDC_ERR_INVALID_CERTIFICATE:
+ case KRB5KDC_ERR_REVOKED_CERTIFICATE:
+ retval = pkinit_create_td_invalid_certificate(cert, e_data);
+ break;
+ default:
+ pkiDebug("no edata needed for error %d\n", err_code);
+ retval = 0;
+ goto cleanup;
+ }
+
+cleanup:
+
return retval;
}
static krb5_error_code
@@ -451,7 +657,7 @@ pkinit_server_verify_padata(krb5_context context,
}
}
- if (!verify_id_pkinit_eku(cert, pa_type, plgctx)) {
+ if (!verify_id_pkinit_eku(plgctx, cert, pa_type, plgctx->require_eku)) {
pkiDebug("failed to verify id-pkinit-KPClientAuth\n");
retval = KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE;
goto cleanup;
@@ -469,9 +675,9 @@ pkinit_server_verify_padata(krb5_context context,
}
if (auth_pack->clientPublicValue != NULL) {
- retval = server_check_dh(
- auth_pack->clientPublicValue->algorithm.parameters.data,
- auth_pack->clientPublicValue->algorithm.parameters.length);
+ retval = server_check_dh(plgctx,
+ &auth_pack->clientPublicValue->algorithm.parameters,
+ plgctx->ctx_dh_min_bits);
if (retval) {
pkiDebug("bad dh parameters\n");
@@ -516,9 +722,9 @@ pkinit_server_verify_padata(krb5_context context,
goto cleanup;
}
if (auth_pack9->clientPublicValue != NULL) {
- retval = server_check_dh(
- auth_pack9->clientPublicValue->algorithm.parameters.data,
- auth_pack9->clientPublicValue->algorithm.parameters.length);
+ retval = server_check_dh(plgctx,
+ &auth_pack9->clientPublicValue->algorithm.parameters,
+ plgctx->ctx_dh_min_bits);
if (retval) {
pkiDebug("bad dh parameters\n");
@@ -532,9 +738,9 @@ pkinit_server_verify_padata(krb5_context context,
enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
cleanup:
- if (retval) {
+ if (retval && data->pa_type == KRB5_PADATA_PK_AS_REQ) {
pkiDebug("pkinit_verify_padata failed: creating e-data\n");
- if (pkinit_create_edata(retval, e_data))
+ if (pkinit_create_edata(retval, cert, plgctx, e_data))
pkiDebug("pkinit_create_edata failed\n");
}
@@ -602,6 +808,8 @@ pkinit_server_return_padata(krb5_context context,
krb5_reply_key_pack *key_pack = NULL;
krb5_reply_key_pack_draft9 *key_pack9 = NULL;
krb5_data *encoded_key_pack = NULL;
+ int num_types;
+ krb5_cksumtype *cksum_types = NULL;
pkinit_context *plgctx = (pkinit_context *)pa_plugin_context;
@@ -756,7 +964,7 @@ pkinit_server_return_padata(krb5_context context,
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)) {
+ (rep != NULL && rep->choice == choice_pa_pk_as_rep_dhInfo)) {
retval = pkinit_octetstring2key(context, enctype, server_key,
server_key_len, encrypting_key);
if (retval) {
@@ -775,6 +983,9 @@ pkinit_server_return_padata(krb5_context context,
pkiDebug("encode_krb5_kdc_dh_key_info failed\n");
goto cleanup;
}
+#ifdef DEBUG_ASN1
+ print_buffer_bin(encoded_dhkey_info->data, encoded_dhkey_info->length, "/tmp/kdc_dh_key_info");
+#endif
switch ((int)padata->pa_type) {
case KRB5_PADATA_PK_AS_REQ:
@@ -817,9 +1028,16 @@ pkinit_server_return_padata(krb5_context context,
retval = ENOMEM;
goto cleanup;
}
- retval = krb5_c_make_checksum(context,
- CKSUMTYPE_HMAC_SHA1_96_AES256, encrypting_key, 6, req_pkt,
- &key_pack->asChecksum);
+ /* retrieve checksums for a given enctype of the reply key */
+ retval = krb5_c_keyed_checksum_types(context,
+ encrypting_key->enctype, &num_types, &cksum_types);
+ if (retval)
+ goto cleanup;
+
+ /* pick the first of acceptable enctypes for the checksum */
+ retval = krb5_c_make_checksum(context, cksum_types[0],
+ encrypting_key, KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
+ req_pkt, &key_pack->asChecksum);
if (retval) {
pkiDebug("unable to calculate AS REQ checksum\n");
goto cleanup;
@@ -832,6 +1050,8 @@ pkinit_server_return_padata(krb5_context context,
key_pack->asChecksum.length);
print_buffer(key_pack->asChecksum.contents,
key_pack->asChecksum.length);
+ pkiDebug("encrypting key (%d)\n", encrypting_key->length);
+ print_buffer(encrypting_key->contents, encrypting_key->length);
#endif
krb5_copy_keyblock_contents(context, encrypting_key,
@@ -965,10 +1185,19 @@ pkinit_server_return_padata(krb5_context context,
DH_free(dh_server);
if (filename != NULL)
free(filename);
+ if (dh_pubkey != NULL)
+ free(dh_pubkey);
+ if (server_key != NULL)
+ free(server_key);
+ if (cksum_types != NULL)
+ free(cksum_types);
switch ((int)padata->pa_type) {
case KRB5_PADATA_PK_AS_REQ:
free_krb5_pa_pk_as_req(&reqp);
+ 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);
free_krb5_pa_pk_as_rep(&rep);
free_krb5_reply_key_pack(&key_pack);
@@ -1003,15 +1232,138 @@ static krb5_preauthtype supported_server_pa_types[] = {
static krb5_preauthtype supported_server_pa_types[] = {
KRB5_PADATA_PK_AS_REQ,
KRB5_PADATA_PK_AS_REQ_OLD,
+ //KRB5_PADATA_PK_AS_REP_OLD,
0
};
#endif
+void
+pkinit_fini_kdc_profile(krb5_context context, pkinit_context *plgctx)
+{
+ if (plgctx->ctx_identity)
+ profile_release_string(plgctx->ctx_identity);
+ if (plgctx->ctx_anchors)
+ profile_release_string(plgctx->ctx_anchors);
+ if (plgctx->ctx_pool)
+ profile_release_string(plgctx->ctx_pool);
+ if (plgctx->ctx_revoke)
+ profile_release_string(plgctx->ctx_revoke);
+ if (plgctx->ctx_ocsp)
+ profile_release_string(plgctx->ctx_ocsp);
+ if (plgctx->ctx_mapping_file)
+ profile_release_string(plgctx->ctx_mapping_file);
+}
+
+static krb5_error_code
+pkinit_init_kdc_profile(krb5_context context, pkinit_context *plgctx)
+{
+ profile_t profile = NULL;
+ krb5_error_code retval;
+
+ retval = krb5_get_profile(context, &profile);
+ if (retval) {
+ krb5_set_error_message(context, retval, "Could not get profile handle");
+ goto errout;
+ }
+
+ profile_get_string(profile, "kdc", "pkinit_identity", NULL,
+ NULL, &plgctx->ctx_identity);
+#if 0 /* Don't enforce these for now */
+ if (NULL == plgctx->ctx_identity || '\0' == plgctx->ctx_identity[0]) {
+ retval = EINVAL;
+ krb5_set_error_message(context, retval,
+ "No pkinit_identity supplied for the KDC");
+ goto errout;
+ }
+#endif
+
+ profile_get_string(profile, "kdc", "pkinit_anchors", NULL,
+ NULL, &plgctx->ctx_anchors);
+#if 0 /* Don't enforce these for now */
+ if (NULL == plgctx->ctx_anchors || '\0' == plgctx->ctx_anchors[0]) {
+ retval = EINVAL;
+ krb5_set_error_message(context, retval,
+ "No pkinit_anchors supplied for the KDC");
+ goto errout;
+ }
+#endif
+
+ profile_get_string(profile, "kdc", "pkinit_pool", NULL,
+ NULL, &plgctx->ctx_pool);
+
+ profile_get_string(profile, "kdc", "pkinit_revoke", NULL,
+ NULL, &plgctx->ctx_revoke);
+
+ profile_get_string(profile, "kdc", "pkinit_kdc_ocsp", NULL,
+ NULL, &plgctx->ctx_ocsp);
+
+ profile_get_string(profile, "kdc", "pkinit_mappings_file", NULL,
+ NULL, &plgctx->ctx_mapping_file);
+
+ profile_get_boolean(profile, "kdc",
+ "pkinit_principal_in_certificate", NULL,
+ 1, &plgctx->ctx_princ_in_cert);
+
+ profile_get_boolean(profile, "kdc",
+ "pkinit_allow_proxy_certificate", NULL,
+ 0, &plgctx->ctx_allow_proxy_certs);
+
+ profile_get_integer(profile, "kdc", "pkinit_dh_min_bits", NULL,
+ 0, &plgctx->ctx_dh_min_bits);
+
+ profile_release(profile);
+ return 0;
+errout:
+ if (profile)
+ profile_release(profile);
+ pkinit_fini_kdc_profile(context, plgctx);
+ return retval;
+}
+
+static int
+pkinit_server_plugin_init(krb5_context context, void **blob)
+{
+ krb5_error_code retval;
+ pkinit_context *plgctx;
+
+ retval = pkinit_lib_init(context, blob);
+ if (retval)
+ goto errout;
+
+ plgctx = *blob;
+
+ retval = pkinit_init_kdc_profile(context, plgctx);
+ if (retval) {
+ pkinit_lib_fini(context, *blob);
+ goto errout;
+ }
+
+ retval = pkinit_init_dh_params(context, plgctx);
+ if (retval) {
+ pkinit_fini_kdc_profile(context, plgctx);
+ pkinit_lib_fini(context, *blob);
+ goto errout;
+ }
+
+errout:
+ return retval;
+}
+
+static void
+pkinit_server_plugin_fini(krb5_context context, void *blob)
+{
+ pkinit_context *plgctx = (pkinit_context *) blob;
+
+ pkinit_fini_kdc_profile(context, plgctx);
+ pkinit_fini_dh_params(context, plgctx);
+ pkinit_lib_fini(context, blob);
+}
+
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_plugin_init, /* (*init_proc) */
+ pkinit_server_plugin_fini, /* (*fini_proc) */
pkinit_server_get_flags, /* (*flags_proc) */
pkinit_server_get_edata, /* (*edata_proc) */
pkinit_server_verify_padata,/* (*verify_proc) */