diff options
author | Kevin Coffman <kwc@citi.umich.edu> | 2006-12-03 18:06:39 +0000 |
---|---|---|
committer | Kevin Coffman <kwc@citi.umich.edu> | 2006-12-03 18:06:39 +0000 |
commit | cd5746c0ada286da49410ee803f08a55144082e0 (patch) | |
tree | d68519e9f460a9100db688cdab16b9c26b2feea2 | |
parent | 21dff76131f8f3f0e71df175972f807c8417d3e1 (diff) | |
download | krb5-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.h | 22 | ||||
-rw-r--r-- | src/lib/krb5/asn.1/asn1_k_decode.c | 48 | ||||
-rw-r--r-- | src/lib/krb5/asn.1/asn1_k_decode.h | 4 | ||||
-rw-r--r-- | src/lib/krb5/asn.1/asn1_k_encode.c | 64 | ||||
-rw-r--r-- | src/lib/krb5/asn.1/asn1_k_encode.h | 6 | ||||
-rw-r--r-- | src/lib/krb5/asn.1/krb5_decode.c | 26 | ||||
-rw-r--r-- | src/lib/krb5/asn.1/krb5_encode.c | 17 | ||||
-rw-r--r-- | src/lib/krb5/krb/get_in_tkt.c | 8 | ||||
-rw-r--r-- | src/lib/krb5/krb/preauth2.c | 14 | ||||
-rw-r--r-- | src/lib/krb5/libkrb5.exports | 5 | ||||
-rw-r--r-- | src/plugins/preauth/pkinit/Makefile.in | 10 | ||||
-rw-r--r-- | src/plugins/preauth/pkinit/pkinit.h | 43 | ||||
-rw-r--r-- | src/plugins/preauth/pkinit/pkinit_clnt.c | 705 | ||||
-rw-r--r-- | src/plugins/preauth/pkinit/pkinit_lib.c | 316 | ||||
-rw-r--r-- | src/plugins/preauth/pkinit/pkinit_srv.c | 612 |
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) */ |