diff options
author | Kevin Coffman <kwc@citi.umich.edu> | 2006-12-12 03:08:07 +0000 |
---|---|---|
committer | Kevin Coffman <kwc@citi.umich.edu> | 2006-12-12 03:08:07 +0000 |
commit | 34731fd9dd830ebdc3c3e3ce270b826bd8bbd350 (patch) | |
tree | 06e9792934a6ae3ff25ce5d8cc17f041a65ae0a4 | |
parent | 9767459e6b9f4937b3ad2dd06affac3c8d7f4375 (diff) | |
download | krb5-34731fd9dd830ebdc3c3e3ce270b826bd8bbd350.zip krb5-34731fd9dd830ebdc3c3e3ce270b826bd8bbd350.tar.gz krb5-34731fd9dd830ebdc3c3e3ce270b826bd8bbd350.tar.bz2 |
Pull in changes for the extended get_init_creds_opt structure
Pull in changes to add get_init_creds_opt_set_pa(),
get_init_creds_opt_get_pa(), and get_init_creds_opt_free_pa()
Change client interface to pass in the get_init_creds_opt structure
to the process and tryagain functions.
Pull in changes to kinit to pass preauth options entered with "-X"
Create typedefs for all the preauth plugin client and server
interface functions and use them. Eliminates mismatches
and enables better type checking of the interface paremeters.
Add *temporary* code to client side of pkinit to handle preauth options
and set the appropriate environment variables.
(Currently only X509_user_identity, X509_anchors, and
flag_RSA_PROTOCOL are handled.)
Add code to use krb5int_accessor to obtain pointers to internal functions
for ASN.1 encode/decode routines rather than exporting them from
libkrb5.
Various updates and improvements in the pkinit smartcard code.
git-svn-id: svn://anonsvn.mit.edu/krb5/users/coffman/pkinit@18937 dc483132-0cff-0310-8789-dd5450dbe970
27 files changed, 2301 insertions, 616 deletions
diff --git a/src/clients/kinit/kinit.c b/src/clients/kinit/kinit.c index 452d98c..f8e5b77 100644 --- a/src/clients/kinit/kinit.c +++ b/src/clients/kinit/kinit.c @@ -38,6 +38,7 @@ #include <string.h> #include <stdio.h> #include <time.h> +#include <errno.h> #include <com_err.h> #ifdef GETOPT_LONG @@ -143,6 +144,9 @@ struct k_opts char* k4_cache_name; action_type action; + + int num_pa_opts; + krb5_gic_opt_pa_data *pa_opts; }; struct k5_data @@ -283,6 +287,37 @@ static void extended_com_err_fn (const char *myprog, errcode_t code, fprintf (stderr, "\n"); } +static int +add_preauth_opt(struct k_opts *opts, char *av) +{ + char *sep, *v; + krb5_gic_opt_pa_data *p, *x; + + if (opts->num_pa_opts == 0) { + opts->pa_opts = malloc(sizeof(krb5_gic_opt_pa_data)); + if (opts->pa_opts == NULL) + return ENOMEM; + } else { + size_t newsize = (opts->num_pa_opts + 1) * sizeof(krb5_gic_opt_pa_data); + x = realloc(opts->pa_opts, newsize); + if (x == NULL) + return ENOMEM; + opts->pa_opts = x; + } + p = &opts->pa_opts[opts->num_pa_opts]; + sep = strchr(av, '='); + if (sep) { + *sep = '\0'; + v = ++sep; + p->value = v; + } else { + p->value = "yes"; + } + p->attr = av; + opts->num_pa_opts++; + return 0; +} + static char * parse_options(argc, argv, opts, progname) int argc; @@ -296,7 +331,7 @@ parse_options(argc, argv, opts, progname) int use_k5 = 0; int i; - while ((i = GETOPT(argc, argv, "r:fpFP54aAVl:s:c:kt:RS:v")) + while ((i = GETOPT(argc, argv, "r:fpFP54aAVl:s:c:kt:RS:vX:")) != -1) { switch (i) { case 'V': @@ -380,6 +415,14 @@ parse_options(argc, argv, opts, progname) opts->k5_cache_name = optarg; } break; + case 'X': + code = add_preauth_opt(opts, optarg); + if (code) + { + com_err(progname, code, "while adding preauth option"); + errflg++; + } + break; #if 0 /* A little more work is needed before we can enable this @@ -752,12 +795,15 @@ k5_kinit(opts, k5) krb5_keytab keytab = 0; krb5_creds my_creds; krb5_error_code code = 0; - krb5_get_init_creds_opt options; + krb5_get_init_creds_opt *options = NULL; + int i; if (!got_k5) return 0; - krb5_get_init_creds_opt_init(&options); + code = krb5_get_init_creds_opt_alloc(k5->ctx, &options); + if (code) + goto cleanup; memset(&my_creds, 0, sizeof(my_creds)); /* @@ -766,17 +812,17 @@ k5_kinit(opts, k5) */ if (opts->lifetime) - krb5_get_init_creds_opt_set_tkt_life(&options, opts->lifetime); + krb5_get_init_creds_opt_set_tkt_life(options, opts->lifetime); if (opts->rlife) - krb5_get_init_creds_opt_set_renew_life(&options, opts->rlife); + krb5_get_init_creds_opt_set_renew_life(options, opts->rlife); if (opts->forwardable) - krb5_get_init_creds_opt_set_forwardable(&options, 1); + krb5_get_init_creds_opt_set_forwardable(options, 1); if (opts->not_forwardable) - krb5_get_init_creds_opt_set_forwardable(&options, 0); + krb5_get_init_creds_opt_set_forwardable(options, 0); if (opts->proxiable) - krb5_get_init_creds_opt_set_proxiable(&options, 1); + krb5_get_init_creds_opt_set_proxiable(options, 1); if (opts->not_proxiable) - krb5_get_init_creds_opt_set_proxiable(&options, 0); + krb5_get_init_creds_opt_set_proxiable(options, 0); if (opts->addresses) { krb5_address **addresses = NULL; @@ -785,10 +831,10 @@ k5_kinit(opts, k5) com_err(progname, code, "getting local addresses"); goto cleanup; } - krb5_get_init_creds_opt_set_address_list(&options, addresses); + krb5_get_init_creds_opt_set_address_list(options, addresses); } if (opts->no_addresses) - krb5_get_init_creds_opt_set_address_list(&options, NULL); + krb5_get_init_creds_opt_set_address_list(options, NULL); if ((opts->action == INIT_KT) && opts->keytab_name) { @@ -800,20 +846,49 @@ k5_kinit(opts, k5) } } + for (i = 0; i < opts->num_pa_opts; i++) { + code = krb5_get_init_creds_opt_set_pa(k5->ctx, options, + opts->pa_opts[i].attr, + opts->pa_opts[i].value); + if (code != 0) { + com_err(progname, code, "while setting '%s'='%s'", + opts->pa_opts[i].attr, opts->pa_opts[i].value); + goto cleanup; + } + } + +#if 0 /* XXX Testing... */ + code = krb5_get_init_creds_opt_set_pkinit( + k5->ctx, /* context */ + options, /* get_init_creds_opt */ + NULL, /* principal */ + "/tmp/x509up_u20010", /* X509_user_identity */ + "/etc/grid-security/certificates", /* X509_anchors */ + NULL, /* X509_chain_list */ + NULL, /* X509_revoke_list */ + 0, /* flags */ + NULL, /* prompter_fct */ + NULL, /* prompter_data */ + NULL); /* password */ + if (code) { + com_err(progname, code, "while setting pkinit options"); + goto cleanup; + } +#endif switch (opts->action) { case INIT_PW: code = krb5_get_init_creds_password(k5->ctx, &my_creds, k5->me, 0, kinit_prompter, 0, opts->starttime, opts->service_name, - &options); + options); break; case INIT_KT: code = krb5_get_init_creds_keytab(k5->ctx, &my_creds, k5->me, keytab, opts->starttime, opts->service_name, - &options); + options); break; case VALIDATE: code = krb5_get_validated_creds(k5->ctx, &my_creds, k5->me, k5->cc, @@ -876,9 +951,16 @@ k5_kinit(opts, k5) notix = 0; cleanup: + if (options) + krb5_get_init_creds_opt_free(k5->ctx, options); if (my_creds.client == k5->me) { my_creds.client = 0; } + if (opts->pa_opts) { + free(opts->pa_opts); + opts->pa_opts = NULL; + opts->num_pa_opts = 0; + } krb5_free_cred_contents(k5->ctx, &my_creds); if (keytab) krb5_kt_close(k5->ctx, keytab); diff --git a/src/clients/kpasswd/kpasswd.c b/src/clients/kpasswd/kpasswd.c index 95e33ff..204a8bf 100644 --- a/src/clients/kpasswd/kpasswd.c +++ b/src/clients/kpasswd/kpasswd.c @@ -49,7 +49,7 @@ int main(int argc, char *argv[]) krb5_principal princ; char *pname; krb5_ccache ccache; - krb5_get_init_creds_opt opts; + krb5_get_init_creds_opt *opts = NULL; krb5_creds creds; char pw[1024]; @@ -102,26 +102,31 @@ int main(int argc, char *argv[]) get_name_from_passwd_file(argv[0], context, &princ); } - krb5_get_init_creds_opt_init(&opts); - krb5_get_init_creds_opt_set_tkt_life(&opts, 5*60); - krb5_get_init_creds_opt_set_renew_life(&opts, 0); - krb5_get_init_creds_opt_set_forwardable(&opts, 0); - krb5_get_init_creds_opt_set_proxiable(&opts, 0); + if ((ret = krb5_get_init_creds_opt_alloc(context, &opts))) { + com_err(argv[0], ret, "allocating krb5_get_init_creds_opt"); + exit(1); + } + krb5_get_init_creds_opt_set_tkt_life(opts, 5*60); + krb5_get_init_creds_opt_set_renew_life(opts, 0); + krb5_get_init_creds_opt_set_forwardable(opts, 0); + krb5_get_init_creds_opt_set_proxiable(opts, 0); if ((ret = krb5_get_init_creds_password(context, &creds, princ, NULL, krb5_prompter_posix, NULL, - 0, "kadmin/changepw", &opts))) { + 0, "kadmin/changepw", opts))) { if (ret == KRB5KRB_AP_ERR_BAD_INTEGRITY) com_err(argv[0], 0, "Password incorrect while getting initial ticket"); else com_err(argv[0], ret, "getting initial ticket"); + krb5_get_init_creds_opt_free(context, opts); exit(1); } pwlen = sizeof(pw); if ((ret = krb5_read_password(context, P1, P2, pw, &pwlen))) { com_err(argv[0], ret, "while reading password"); + krb5_get_init_creds_opt_free(context, opts); exit(1); } @@ -129,6 +134,7 @@ int main(int argc, char *argv[]) &result_code, &result_code_string, &result_string))) { com_err(argv[0], ret, "changing password"); + krb5_get_init_creds_opt_free(context, opts); exit(1); } @@ -138,6 +144,7 @@ int main(int argc, char *argv[]) result_string.length?": ":"", (int) result_string.length, result_string.data ? result_string.data : ""); + krb5_get_init_creds_opt_free(context, opts); exit(2); } @@ -145,6 +152,7 @@ int main(int argc, char *argv[]) free(result_string.data); if (result_code_string.data != NULL) free(result_code_string.data); + krb5_get_init_creds_opt_free(context, opts); printf("Password changed.\n"); exit(0); diff --git a/src/clients/kpasswd/ksetpwd.c b/src/clients/kpasswd/ksetpwd.c index 148e686..2eec397 100644 --- a/src/clients/kpasswd/ksetpwd.c +++ b/src/clients/kpasswd/ksetpwd.c @@ -34,8 +34,6 @@ static void get_init_creds_opt_init( krb5_get_init_creds_opt *outOptions ) { krb5_preauthtype preauth[] = { KRB5_PADATA_ENC_TIMESTAMP }; krb5_enctype etypes[] = {ENCTYPE_DES_CBC_MD5, ENCTYPE_DES_CBC_CRC}; - memset( outOptions, 0, sizeof(*outOptions) ); - krb5_get_init_creds_opt_init(outOptions); krb5_get_init_creds_opt_set_address_list(outOptions, NULL); krb5_get_init_creds_opt_set_etype_list( outOptions, etypes, sizeof(etypes)/sizeof(krb5_enctype) ); krb5_get_init_creds_opt_set_preauth_list(outOptions, preauth, sizeof(preauth)/sizeof(krb5_preauthtype) ); @@ -128,17 +126,21 @@ static kbrccache_t userinitcontext( } if( kres != 0 || have_credentials == 0 ) { - krb5_get_init_creds_opt options; - get_init_creds_opt_init(&options); + krb5_get_init_creds_opt *options = NULL; + kres = krb5_get_init_creds_opt_alloc(kcontext, &options); + if ( kres == 0 ) + { + get_init_creds_opt_init(options); /* ** no valid credentials - get new ones */ - kres = krb5_get_init_creds_password( kcontext, &kcreds, kme, pPass, - NULL /*prompter*/, - NULL /*data*/, - 0 /*starttime*/, - 0 /*in_tkt_service*/, - &options /*options*/ ); + kres = krb5_get_init_creds_password( kcontext, &kcreds, kme, pPass, + NULL /*prompter*/, + NULL /*data*/, + 0 /*starttime*/, + 0 /*in_tkt_service*/, + options /*options*/ ); + } if( kres == 0 ) { if( numCreds <= 0 ) @@ -148,6 +150,7 @@ static kbrccache_t userinitcontext( if( kres == 0 ) have_credentials = 1; } + krb5_get_init_creds_opt_free(kcontext, options); } #ifdef NOTUSED if( have_credentials ) diff --git a/src/include/k5-int-pkinit.h b/src/include/k5-int-pkinit.h index 60c02cb..bf6073e 100644 --- a/src/include/k5-int-pkinit.h +++ b/src/include/k5-int-pkinit.h @@ -215,12 +215,12 @@ krb5_error_code encode_krb5_reply_key_pack krb5_error_code encode_krb5_reply_key_pack_draft9 (const krb5_reply_key_pack_draft9 *, krb5_data **code); -krb5_error_code encode_krb5_td_trusted_certifiers - (const krb5_external_principal_identifier **, krb5_data **code); - krb5_error_code encode_krb5_typed_data (const krb5_typed_data **, krb5_data **code); +krb5_error_code encode_krb5_td_trusted_certifiers + (const krb5_external_principal_identifier **, krb5_data **code); + krb5_error_code encode_krb5_td_dh_parameters (const krb5_algorithm_identifier **, krb5_data **code); diff --git a/src/include/k5-int.h b/src/include/k5-int.h index 2eac815..1d89355 100644 --- a/src/include/k5-int.h +++ b/src/include/k5-int.h @@ -891,51 +891,17 @@ typedef struct _krb5_preauth_context { krb5_enctype *enctypes; /* The plugin's per-plugin context and a function to clear it. */ void *plugin_context; - void (*client_fini)(krb5_context context, void *plugin_context); + preauth_client_plugin_fini_proc client_fini; /* The module's table, and some of its members, copied here for * convenience when we populated the list. */ struct krb5plugin_preauth_client_ftable_v0 *ftable; const char *name; int flags, use_count; - krb5_error_code (*client_process)(krb5_context context, - void *plugin_context, - void *request_context, - preauth_get_client_data_proc get_data_proc, - krb5_preauth_client_rock *rock, - krb5_kdc_req *request, - krb5_data *encoded_request_body, - krb5_data *encoded_previous_request, - krb5_pa_data *pa_data, - krb5_prompter_fct prompter, - void *prompter_data, - preauth_get_as_key_proc gak_fct, - void *gak_data, - krb5_data *salt, - krb5_data *s2kparams, - krb5_keyblock *as_key, - krb5_pa_data **out_pa_data); - krb5_error_code (*client_tryagain)(krb5_context context, - void *plugin_context, - void *request_context, - preauth_get_client_data_proc get_data_proc, - krb5_preauth_client_rock *rock, - krb5_kdc_req *request, - krb5_data *encoded_request_body, - krb5_data *encoded_previous_request, - krb5_pa_data *old_pa_data, - krb5_error *err_reply, - krb5_prompter_fct prompter, - void *prompter_data, - preauth_get_as_key_proc gak_fct, - void *gak_data, - krb5_data *salt, - krb5_data *s2kparams, - krb5_keyblock *as_key, - krb5_pa_data **new_pa_data); - void (*client_req_init)(krb5_context context, void *plugin_context, - void **request_context); - void (*client_req_fini)(krb5_context context, void *plugin_context, - void *request_context); + preauth_client_process_proc client_process; + preauth_client_tryagain_proc client_tryagain; + preauth_client_supply_gic_opts_proc client_supply_gic_opts; + preauth_client_request_init_proc client_req_init; + preauth_client_request_fini_proc client_req_fini; /* The per-request context which the client_req_init() function * might allocate, which we'll need to clean up later by * calling the client_req_fini() function. */ @@ -1038,6 +1004,74 @@ void krb5_free_etype_info /* * End "preauth.h" */ + +/* + * Extending the krb5_get_init_creds_opt structure. The original + * krb5_get_init_creds_opt structure is defined publicly. The + * new extended version is private. The original interface + * assumed a pre-allocated structure which was passed to + * krb5_get_init_creds_init(). The new interface assumes that + * the caller will call krb5_get_init_creds_alloc() and + * krb5_get_init_creds_free(). + * + * Callers MUST NOT call krb5_get_init_creds_init() after allocating an + * opts structure using krb5_get_init_creds_alloc(). To do so will + * introduce memory leaks. Unfortunately, there is no way to enforce + * this behavior. + * + * Two private flags are added for backward compatibility. + * KRB5_GET_INIT_CREDS_OPT_EXTENDED says that the structure was allocated + * with the new krb5_get_init_creds_opt_alloc() function. + * KRB5_GET_INIT_CREDS_OPT_SHADOWED is set to indicate that the extended + * structure is a shadow copy of an original krb5_get_init_creds_opt + * structure. + * If KRB5_GET_INIT_CREDS_OPT_SHADOWED is set after a call to + * krb5int_gic_opt_to_opte(), the resulting extended structure should be + * freed (using krb5_get_init_creds_free). Otherwise, the original + * structure was already extended and there is no need to free it. + */ + +#define KRB5_GET_INIT_CREDS_OPT_EXTENDED 0x80000000 +#define KRB5_GET_INIT_CREDS_OPT_SHADOWED 0x40000000 + +#define krb5_gic_opt_is_extended(s) \ + (((s)->flags & KRB5_GET_INIT_CREDS_OPT_EXTENDED) ? 1 : 0) +#define krb5_gic_opt_is_shadowed(s) \ + (((s)->flags & KRB5_GET_INIT_CREDS_OPT_SHADOWED) ? 1 : 0) + + +typedef struct _krb5_gic_opt_private { + int num_preauth_data; + krb5_gic_opt_pa_data *preauth_data; +} krb5_gic_opt_private; + +typedef struct _krb5_gic_opt_ext { + krb5_flags flags; + krb5_deltat tkt_life; + krb5_deltat renew_life; + int forwardable; + int proxiable; + krb5_enctype *etype_list; + int etype_list_length; + krb5_address **address_list; + krb5_preauthtype *preauth_list; + int preauth_list_length; + krb5_data *salt; + /* + * Do not change anything above this point in this structure. + * It is identical to the public krb5_get_init_creds_opt structure. + * New members must be added below. + */ + krb5_gic_opt_private *opt_private; +} krb5_gic_opt_ext; + +krb5_error_code +krb5int_gic_opt_to_opte(krb5_context context, + krb5_get_init_creds_opt *opt, + krb5_gic_opt_ext **opte, + unsigned int force, + const char *where); + krb5_error_code krb5int_copy_data_contents (krb5_context, const krb5_data *, krb5_data *); @@ -1061,14 +1095,14 @@ krb5_get_init_creds void *prompter_data, krb5_deltat start_time, char *in_tkt_service, - krb5_get_init_creds_opt *gic_options, + krb5_gic_opt_ext *gic_options, krb5_gic_get_as_key_fct gak, void *gak_data, int *master, krb5_kdc_rep **as_reply); -void krb5int_populate_gic_opt ( - krb5_context, krb5_get_init_creds_opt *, +krb5_error_code krb5int_populate_gic_opt ( + krb5_context, krb5_gic_opt_ext **, krb5_flags options, krb5_address * const *addrs, krb5_enctype *ktypes, krb5_preauthtype *pre_auth_types, krb5_creds *creds); @@ -1083,7 +1117,8 @@ krb5_error_code KRB5_CALLCONV krb5_do_preauth krb5_enctype *etype, krb5_keyblock *as_key, krb5_prompter_fct prompter, void *prompter_data, krb5_gic_get_as_key_fct gak_fct, void *gak_data, - krb5_preauth_client_rock *get_data_rock); + krb5_preauth_client_rock *get_data_rock, + krb5_gic_opt_ext *opte); krb5_error_code KRB5_CALLCONV krb5_do_preauth_tryagain (krb5_context context, krb5_kdc_req *request, @@ -1095,7 +1130,8 @@ krb5_error_code KRB5_CALLCONV krb5_do_preauth_tryagain krb5_enctype *etype, krb5_keyblock *as_key, krb5_prompter_fct prompter, void *prompter_data, krb5_gic_get_as_key_fct gak_fct, void *gak_data, - krb5_preauth_client_rock *get_data_rock); + krb5_preauth_client_rock *get_data_rock, + krb5_gic_opt_ext *opte); void KRB5_CALLCONV krb5_init_preauth_context (krb5_context); void KRB5_CALLCONV krb5_free_preauth_context @@ -1103,7 +1139,7 @@ void KRB5_CALLCONV krb5_free_preauth_context void KRB5_CALLCONV krb5_clear_preauth_context_use_counts (krb5_context); void KRB5_CALLCONV krb5_preauth_prepare_request - (krb5_context, krb5_get_init_creds_opt *, krb5_kdc_req *); + (krb5_context, krb5_gic_opt_ext *, krb5_kdc_req *); void KRB5_CALLCONV krb5_preauth_request_context_init (krb5_context); void KRB5_CALLCONV krb5_preauth_request_context_fini @@ -1814,7 +1850,7 @@ void krb5int_free_srv_dns_data(struct srv_dns_entry *); /* To keep happy libraries which are (for now) accessing internal stuff */ /* Make sure to increment by one when changing the struct */ -#define KRB5INT_ACCESS_STRUCT_VERSION 10 +#define KRB5INT_ACCESS_STRUCT_VERSION 11 #ifndef ANAME_SZ struct ktext; /* from krb.h, for krb524 support */ @@ -1868,6 +1904,62 @@ typedef struct _krb5int_access { struct _krb5_key_data **out, krb5_int16 *n_key_data, int *mkvno); + + /* + * pkinit asn.1 encode/decode functions + */ + krb5_error_code (*encode_krb5_auth_pack) + (const krb5_auth_pack *rep, krb5_data **code); + krb5_error_code (*encode_krb5_auth_pack_draft9) + (const krb5_auth_pack_draft9 *rep, krb5_data **code); + krb5_error_code (*encode_krb5_kdc_dh_key_info) + (const krb5_kdc_dh_key_info *rep, krb5_data **code); + krb5_error_code (*encode_krb5_pa_pk_as_rep) + (const krb5_pa_pk_as_rep *rep, krb5_data **code); + krb5_error_code (*encode_krb5_pa_pk_as_rep_draft9) + (const krb5_pa_pk_as_rep_draft9 *rep, krb5_data **code); + krb5_error_code (*encode_krb5_pa_pk_as_req) + (const krb5_pa_pk_as_req *rep, krb5_data **code); + krb5_error_code (*encode_krb5_pa_pk_as_req_draft9) + (const krb5_pa_pk_as_req_draft9 *rep, krb5_data **code); + krb5_error_code (*encode_krb5_reply_key_pack) + (const krb5_reply_key_pack *, krb5_data **code); + krb5_error_code (*encode_krb5_reply_key_pack_draft9) + (const krb5_reply_key_pack_draft9 *, krb5_data **code); + krb5_error_code (*encode_krb5_td_dh_parameters) + (const krb5_algorithm_identifier **, krb5_data **code); + 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 (*decode_krb5_auth_pack) + (const krb5_data *, krb5_auth_pack **); + krb5_error_code (*decode_krb5_auth_pack_draft9) + (const krb5_data *, krb5_auth_pack_draft9 **); + krb5_error_code (*decode_krb5_pa_pk_as_req) + (const krb5_data *, krb5_pa_pk_as_req **); + krb5_error_code (*decode_krb5_pa_pk_as_req_draft9) + (const krb5_data *, krb5_pa_pk_as_req_draft9 **); + krb5_error_code (*decode_krb5_pa_pk_as_rep) + (const krb5_data *, krb5_pa_pk_as_rep **); + krb5_error_code (*decode_krb5_pa_pk_as_rep_draft9) + (const krb5_data *, krb5_pa_pk_as_rep_draft9 **); + krb5_error_code (*decode_krb5_kdc_dh_key_info) + (const krb5_data *, krb5_kdc_dh_key_info **); + krb5_error_code (*decode_krb5_principal_name) + (const krb5_data *, krb5_principal_data **); + krb5_error_code (*decode_krb5_reply_key_pack) + (const krb5_data *, krb5_reply_key_pack **); + krb5_error_code (*decode_krb5_reply_key_pack_draft9) + (const krb5_data *, krb5_reply_key_pack_draft9 **); + krb5_error_code (*decode_krb5_td_dh_parameters) + (const krb5_data *, krb5_algorithm_identifier ***); + krb5_error_code (*decode_krb5_td_trusted_certifiers) + (const krb5_data *, krb5_external_principal_identifier ***); + krb5_error_code (*decode_krb5_typed_data) + (const krb5_data *, krb5_typed_data ***); + } krb5int_access; #define KRB5INT_ACCESS_VERSION \ diff --git a/src/include/krb5/krb5.hin b/src/include/krb5/krb5.hin index 424325f..24b9476 100644 --- a/src/include/krb5/krb5.hin +++ b/src/include/krb5/krb5.hin @@ -2437,6 +2437,16 @@ typedef struct _krb5_get_init_creds_opt { #define KRB5_GET_INIT_CREDS_OPT_SALT 0x0080 +krb5_error_code KRB5_CALLCONV +krb5_get_init_creds_opt_alloc +(krb5_context context, + krb5_get_init_creds_opt **opt); + +void KRB5_CALLCONV +krb5_get_init_creds_opt_free +(krb5_context context, + krb5_get_init_creds_opt *opt); + void KRB5_CALLCONV krb5_get_init_creds_opt_init (krb5_get_init_creds_opt *opt); @@ -2484,6 +2494,26 @@ krb5_get_init_creds_opt_set_salt krb5_data *salt); +/* Generic preauth option attribute/value pairs */ +typedef struct _krb5_gic_opt_pa_data { + char *attr; + char *value; +} krb5_gic_opt_pa_data; + +/* + * This function allows the caller to supply options to preauth + * plugins. Preauth plugin modules are given a chance to look + * at each option at the time this function is called in ordre + * to check the validity of the option. + * The 'opt' pointer supplied to this function must have been + * obtained using krb5_get_init_creds_opt_alloc() + */ +krb5_error_code KRB5_CALLCONV +krb5_get_init_creds_opt_set_pa + (krb5_context context, + krb5_get_init_creds_opt *opt, + const char *attr, + const char *value); krb5_error_code KRB5_CALLCONV krb5_get_init_creds_password diff --git a/src/include/krb5/preauth_plugin.h b/src/include/krb5/preauth_plugin.h index f8a9db1..4307f6e 100644 --- a/src/include/krb5/preauth_plugin.h +++ b/src/include/krb5/preauth_plugin.h @@ -93,34 +93,30 @@ struct _krb5_preauth_client_rock; * which gets sent over the wire. */ #define PA_PSEUDO 0x00000080 + +/*************************************************************************** + * + * Client-side preauthentication plugin interface definition. + * + ***************************************************************************/ + /* - * A server module's callback functions are allowed to request specific types - * of information about the given client or server record or request, even - * though the database records themselves are opaque to the module. + * A callback which will obtain the user's long-term AS key by prompting the + * user for the password, then salting it properly, and so on. For the moment, + * it's identical to the get_as_key callback used inside of libkrb5, but we + * define a new typedef here instead of making the existing one public to + * isolate ourselves from potential future changes. */ -enum krb5plugin_preauth_entry_request_type { - /* The returned krb5_data item holds a DER-encoded X.509 certificate. */ - krb5plugin_preauth_entry_request_certificate = 1, - /* The returned krb5_data_item holds a krb5_deltat. */ - krb5plugin_preauth_entry_max_time_skew = 2, - /* The returned krb5_data_item holds an array of krb5_keyblock structures, - * terminated by an entry with key type = 0. - * Each keyblock should have its contents freed in turn, and then the data - * item itself should be freed. */ - krb5plugin_preauth_keys = 3, - /* The returned krb5_data_item holds the request structure, re-encoded - * using DER. Unless the client implementation is the same as the server - * implementation, there's a good chance that the result will not match - * what the client sent, so don't go creating any fatal errors if it - * doesn't match up. */ - krb5plugin_preauth_request_body = 4 -}; typedef krb5_error_code -(*preauth_get_entry_data_proc)(krb5_context, - krb5_kdc_req *, - struct _krb5_db_entry_new *, - krb5_int32 request_type, - krb5_data **); +(*preauth_get_as_key_proc)(krb5_context, + krb5_principal, + krb5_enctype, + krb5_prompter_fct, + void *prompter_data, + krb5_data *salt, + krb5_data *s2kparams, + krb5_keyblock *as_key, + void *gak_data); /* * A client module's callback functions are allowed to request various @@ -139,24 +135,102 @@ typedef krb5_error_code krb5_int32 request_type, krb5_data **); +/* Per-plugin initialization/cleanup. The init function is called + * by libkrb5 when the plugin is loaded, and the fini function is + * called before the plugin is unloaded. Both are optional and + * may be called multiple times in case the plugin is used in + * multiple contexts. The returned context lives the lifetime of + * the krb5_context */ +typedef krb5_error_code +(*preauth_client_plugin_init_proc)(krb5_context context, + void **plugin_context); +typedef void +(*preauth_client_plugin_fini_proc)(krb5_context context, + void *plugin_context); + +/* A callback which returns flags indicating if the module is a "real" or + * an "info" mechanism, and so on. This function is called for each entry + * in the client_pa_type_list. */ +typedef int +(*preauth_client_get_flags_proc)(krb5_context context, + krb5_preauthtype pa_type); + +/* Per-request initialization/cleanup. The request_init function is + * called when beginning to process a get_init_creds request and the + * request_fini function is called when processing of the request is + * complete. This is optional. It may be called multiple times in + * the lifetime of a krb5_context. */ +typedef void +(*preauth_client_request_init_proc)(krb5_context context, + void *plugin_context, + void **request_context); +typedef void +(*preauth_client_request_fini_proc)(krb5_context context, + void *plugin_context, + void *request_context); + +/* Client function which processes server-supplied data in pa_data, + * returns created data in out_pa_data, storing any of its own state in + * client_context if data for the associated preauthentication type is + * needed. It is also called after the AS-REP is received if the AS-REP + * includes preauthentication data of the associated type. + * NOTE! the encoded_previous_request will be NULL the first time this + * function is called, because it is expected to only ever contain the data + * obtained from a previous call to this function. */ +typedef krb5_error_code +(*preauth_client_process_proc)(krb5_context context, + void *plugin_context, + void *request_context, + krb5_get_init_creds_opt *opt, + preauth_get_client_data_proc get_data_proc, + struct _krb5_preauth_client_rock *rock, + krb5_kdc_req *request, + krb5_data *encoded_request_body, + krb5_data *encoded_previous_request, + krb5_pa_data *pa_data, + krb5_prompter_fct prompter, + void *prompter_data, + preauth_get_as_key_proc gak_fct, + void *gak_data, + krb5_data *salt, krb5_data *s2kparams, + krb5_keyblock *as_key, + krb5_pa_data **out_pa_data); + +/* Client function which can attempt to use e-data in the error response to + * try to recover from the given error. If this function is not NULL, and + * it stores data in out_pa_data which is different data from the contents + * of in_pa_data, then the client library will retransmit the request. */ +typedef krb5_error_code +(*preauth_client_tryagain_proc)(krb5_context context, + void *plugin_context, + void *request_context, + krb5_get_init_creds_opt *opt, + preauth_get_client_data_proc get_data_proc, + struct _krb5_preauth_client_rock *rock, + krb5_kdc_req *request, + krb5_data *encoded_request_body, + krb5_data *encoded_previous_request, + krb5_pa_data *in_pa_data, + krb5_error *error, + krb5_prompter_fct prompter, + void *prompter_data, + preauth_get_as_key_proc gak_fct, + void *gak_data, + krb5_data *salt, krb5_data *s2kparams, + krb5_keyblock *as_key, + krb5_pa_data **out_pa_data); + /* - * A callback which will obtain the user's long-term AS key by prompting the - * user for the password, then salting it properly, and so on. For the moment, - * it's identical to the get_as_key callback used inside of libkrb5, but we - * define a new typedef here instead of making the existing one public to - * isolate ourselves from potential future changes. + * Client function which receives krb5_get_init_creds_opt information. + * The attr and value information supplied should be copied locally by + * the module if it wishes to reference it after returning from this call. */ typedef krb5_error_code -(*preauth_get_as_key_proc)(krb5_context, - krb5_principal, - krb5_enctype, - krb5_prompter_fct, - void *prompter_data, - krb5_data *salt, - krb5_data *s2kparams, - krb5_keyblock *as_key, - void *gak_data); - +(*preauth_client_supply_gic_opts_proc)(krb5_context context, + void *plugin_context, + krb5_get_init_creds_opt *opt, + const char *attr, + const char *value); /* * The function table / structure which a preauth client module must export as * "preauthentication_client_0". If the interfaces work correctly, future @@ -181,21 +255,22 @@ typedef struct krb5plugin_preauth_client_ftable_v0 { * may be called multiple times in case the plugin is used in * multiple contexts. The returned context lives the lifetime of * the krb5_context */ - krb5_error_code (*init)(krb5_context context, void **plugin_context); - void (*fini)(krb5_context context, void *plugin_context); + preauth_client_plugin_init_proc init; + preauth_client_plugin_fini_proc fini; + /* A callback which returns flags indicating if the module is a "real" or * an "info" mechanism, and so on. This function is called for each entry * in the client_pa_type_list. */ - int (*flags)(krb5_context context, krb5_preauthtype pa_type); + preauth_client_get_flags_proc flags; + /* Per-request initialization/cleanup. The request_init function is * called when beginning to process a get_init_creds request and the * request_fini function is called when processing of the request is * complete. This is optional. It may be called multiple times in * the lifetime of a krb5_context. */ - void (*request_init)(krb5_context context, void *plugin_context, - void **request_context); - void (*request_fini)(krb5_context context, void *plugin_context, - void *request_context); + preauth_client_request_init_proc request_init; + preauth_client_request_fini_proc request_fini; + /* Client function which processes server-supplied data in pa_data, * returns created data in out_pa_data, storing any of its own state in * client_context if data for the associated preauthentication type is @@ -204,45 +279,136 @@ typedef struct krb5plugin_preauth_client_ftable_v0 { * NOTE! the encoded_previous_request will be NULL the first time this * function is called, because it is expected to only ever contain the data * obtained from a previous call to this function. */ - krb5_error_code (*process)(krb5_context context, - void *plugin_context, - void *request_context, - preauth_get_client_data_proc get_data_proc, - struct _krb5_preauth_client_rock *rock, - krb5_kdc_req *request, - krb5_data *encoded_request_body, - krb5_data *encoded_previous_request, - krb5_pa_data *pa_data, - krb5_prompter_fct prompter, - void *prompter_data, - preauth_get_as_key_proc gak_fct, - void *gak_data, - krb5_data *salt, krb5_data *s2kparams, - krb5_keyblock *as_key, - krb5_pa_data **out_pa_data); + preauth_client_process_proc process; + /* Client function which can attempt to use e-data in the error response to * try to recover from the given error. If this function is not NULL, and * it stores data in out_pa_data which is different data from the contents * of in_pa_data, then the client library will retransmit the request. */ - krb5_error_code (*tryagain)(krb5_context context, - void *plugin_context, - void *request_context, - preauth_get_client_data_proc get_data_proc, - struct _krb5_preauth_client_rock *rock, - krb5_kdc_req *request, - krb5_data *encoded_request_body, - krb5_data *encoded_previous_request, - krb5_pa_data *in_pa_data, - krb5_error *error, - krb5_prompter_fct prompter, - void *prompter_data, - preauth_get_as_key_proc gak_fct, - void *gak_data, - krb5_data *salt, krb5_data *s2kparams, - krb5_keyblock *as_key, - krb5_pa_data **out_pa_data); + preauth_client_tryagain_proc tryagain; + + /* + * Client function which receives krb5_get_init_creds_opt information. + * The attr and value information supplied should be copied locally by + * the module if it wishes to reference it after returning from this call. + */ + preauth_client_supply_gic_opts_proc gic_opts; } krb5plugin_preauth_client_ftable_v0; +/*************************************************************************** + * + * Server-side preauthentication plugin interface definition. + * + ***************************************************************************/ + +/* + * A server module's callback functions are allowed to request specific types + * of information about the given client or server record or request, even + * though the database records themselves are opaque to the module. + */ +enum krb5plugin_preauth_entry_request_type { + /* The returned krb5_data item holds a DER-encoded X.509 certificate. */ + krb5plugin_preauth_entry_request_certificate = 1, + /* The returned krb5_data_item holds a krb5_deltat. */ + krb5plugin_preauth_entry_max_time_skew = 2, + /* The returned krb5_data_item holds an array of krb5_keyblock structures, + * terminated by an entry with key type = 0. + * Each keyblock should have its contents freed in turn, and then the data + * item itself should be freed. */ + krb5plugin_preauth_keys = 3, + /* The returned krb5_data_item holds the request structure, re-encoded + * using DER. Unless the client implementation is the same as the server + * implementation, there's a good chance that the result will not match + * what the client sent, so don't go creating any fatal errors if it + * doesn't match up. */ + krb5plugin_preauth_request_body = 4 +}; + +typedef krb5_error_code +(*preauth_get_entry_data_proc)(krb5_context, + krb5_kdc_req *, + struct _krb5_db_entry_new *, + krb5_int32 request_type, + krb5_data **); + +/* Preauth plugin initialization function */ +typedef krb5_error_code +(*preauth_server_init_proc)(krb5_context context, void **plugin_context); + +/* Preauth plugin cleanup function */ +typedef void +(*preauth_server_fini_proc)(krb5_context context, void *plugin_context); + +/* Return the flags which the KDC should use for this module. This is a + * callback instead of a static value because the module may or may not + * wish to count itself as a hardware preauthentication module (in other + * words, the flags may be affected by the configuration, for example if a + * site administrator can force a particular preauthentication type to be + * supported using only hardware). This function is called for each entry + * entry in the server_pa_type_list. */ +typedef int +(*preauth_server_flags_proc)(krb5_context context, krb5_preauthtype patype); + +/* Get preauthentication data to send to the client as part of the "you + * need to use preauthentication" error. The module doesn't need to + * actually provide data if the protocol doesn't require it, but it should + * return either zero or non-zero to control whether its padata type is + * included in the list which is sent back to the client. Is not allowed + * to create a context because we have no guarantee that the client will + * ever call again (or that it will hit this server if it does), in which + * case a context might otherwise hang around forever. */ +typedef krb5_error_code +(*preauth_server_edata_proc)(krb5_context, + krb5_kdc_req *request, + struct _krb5_db_entry_new *client, + struct _krb5_db_entry_new *server, + preauth_get_entry_data_proc, + void *pa_module_context, + krb5_pa_data *data); + +/* Verify preauthentication data sent by the client, setting the + * TKT_FLG_PRE_AUTH or TKT_FLG_HW_AUTH flag in the enc_tkt_reply's "flags" + * field as appropriate, and returning nonzero on failure. Can create + * context data for consumption by the return_proc or freepa_proc below. */ +typedef krb5_error_code +(*preauth_server_verify_proc)(krb5_context context, + struct _krb5_db_entry_new *client, + krb5_data *req_pkt, + krb5_kdc_req *request, + krb5_enc_tkt_part *enc_tkt_reply, + krb5_pa_data *data, + preauth_get_entry_data_proc, + void *pa_module_context, + void **pa_request_context, + krb5_data **e_data); + +/* Generate preauthentication response data to send to the client as part + * of the AS-REP. If it needs to override the key which is used to encrypt + * the response, it can do so. The module is expected (but not required, + * if a freepa_proc is also provided) to free any context data it saved in + * "request_pa_context". */ +typedef krb5_error_code +(*preauth_server_return_proc)(krb5_context context, + krb5_pa_data * padata, + struct _krb5_db_entry_new *client, + krb5_data *req_pkt, + krb5_kdc_req *request, + krb5_kdc_rep *reply, + struct _krb5_key_data *client_keys, + krb5_keyblock *encrypting_key, + krb5_pa_data **send_pa, + preauth_get_entry_data_proc, + void *pa_module_context, + void **pa_request_context); + +/* Free up the server-side per-request context, in cases where + * server_return_proc() didn't or for whatever reason was not called. + * Can be NULL. */ +typedef krb5_error_code +(*preauth_server_free_reqcontext_proc)(krb5_context, + void *pa_module_context, + void **request_pa_context); + /* * The function table / structure which a preauth server module must export as * "preauthentication_server_0". NOTE: replace "0" with "1" for the type and @@ -262,8 +428,9 @@ typedef struct krb5plugin_preauth_server_ftable_v0 { /* Per-plugin initialization/cleanup. The init function is called by the * KDC when the plugin is loaded, and the fini function is called before * the plugin is unloaded. Both are optional. */ - krb5_error_code (*init_proc)(krb5_context, void **); - void (*fini_proc)(krb5_context, void *); + preauth_server_init_proc init_proc; + preauth_server_fini_proc fini_proc; + /* Return the flags which the KDC should use for this module. This is a * callback instead of a static value because the module may or may not * wish to count itself as a hardware preauthentication module (in other @@ -271,7 +438,8 @@ typedef struct krb5plugin_preauth_server_ftable_v0 { * site administrator can force a particular preauthentication type to be * supported using only hardware). This function is called for each entry * entry in the server_pa_type_list. */ - int (*flags_proc)(krb5_context, krb5_preauthtype); + preauth_server_flags_proc flags_proc; + /* Get preauthentication data to send to the client as part of the "you * need to use preauthentication" error. The module doesn't need to * actually provide data if the protocol doesn't require it, but it should @@ -280,47 +448,52 @@ typedef struct krb5plugin_preauth_server_ftable_v0 { * to create a context because we have no guarantee that the client will * ever call again (or that it will hit this server if it does), in which * case a context might otherwise hang around forever. */ - krb5_error_code (*edata_proc)(krb5_context, krb5_kdc_req *request, - struct _krb5_db_entry_new *client, - struct _krb5_db_entry_new *server, - preauth_get_entry_data_proc, - void *pa_module_context, - krb5_pa_data *data); + preauth_server_edata_proc edata_proc; + /* Verify preauthentication data sent by the client, setting the * TKT_FLG_PRE_AUTH or TKT_FLG_HW_AUTH flag in the enc_tkt_reply's "flags" * field as appropriate, and returning nonzero on failure. Can create * context data for consumption by the return_proc or freepa_proc below. */ - krb5_error_code (*verify_proc)(krb5_context, - struct _krb5_db_entry_new *client, - krb5_data *req_pkt, - krb5_kdc_req *request, - krb5_enc_tkt_part *enc_tkt_reply, - krb5_pa_data *data, - preauth_get_entry_data_proc, - void *pa_module_context, - void **pa_request_context, - krb5_data **e_data); + preauth_server_verify_proc verify_proc; + /* Generate preauthentication response data to send to the client as part * of the AS-REP. If it needs to override the key which is used to encrypt * the response, it can do so. The module is expected (but not required, * if a freepa_proc is also provided) to free any context data it saved in * "request_pa_context". */ - krb5_error_code (*return_proc)(krb5_context, krb5_pa_data * padata, - struct _krb5_db_entry_new *client, - krb5_data *req_pkt, - krb5_kdc_req *request, - krb5_kdc_rep *reply, - struct _krb5_key_data *client_keys, - krb5_keyblock *encrypting_key, - krb5_pa_data **send_pa, - preauth_get_entry_data_proc, - void *pa_module_context, - void **pa_request_context); + preauth_server_return_proc return_proc; + /* Free up the server-side per-request context, in cases where - * server_return_proc() didn't or for whatever reason was not called. Can - * be NULL. */ - krb5_error_code (*freepa_reqcontext_proc)(krb5_context, - void *pa_module_context, - void **request_pa_context); + * server_return_proc() didn't or for whatever reason was not called. + * Can be NULL. */ + preauth_server_free_reqcontext_proc freepa_reqcontext_proc; + } krb5plugin_preauth_server_ftable_v0; + + +/* + * This function allows a preauth plugin to obtain preauth + * options. The preauth_data returned from this function + * should be freed by calling krb5_get_init_creds_opt_free_pa(). + * + * The 'opt' pointer supplied to this function must have been + * obtained using krb5_get_init_creds_opt_alloc() + */ +krb5_error_code KRB5_CALLCONV +krb5_get_init_creds_opt_get_pa + (krb5_context context, + krb5_get_init_creds_opt *opt, + int *num_preauth_data, + krb5_gic_opt_pa_data **preauth_data); + +/* + * This function frees the preauth_data that was returned by + * krb5_get_init_creds_opt_get_pa(). + */ +void KRB5_CALLCONV +krb5_get_init_creds_opt_free_pa + (krb5_context context, + int num_preauth_data, + krb5_gic_opt_pa_data *preauth_data); + #endif /* KRB5_PREAUTH_PLUGIN_H_INCLUDED */ diff --git a/src/kdc/kdc_preauth.c b/src/kdc/kdc_preauth.c index 7d06d99..67b7226 100644 --- a/src/kdc/kdc_preauth.c +++ b/src/kdc/kdc_preauth.c @@ -77,54 +77,17 @@ typedef des_cblock mit_des_cblock; extern void mit_des_fixup_key_parity (mit_des_cblock ); extern int mit_des_is_weak_key (mit_des_cblock ); -typedef krb5_error_code (*verify_proc) - (krb5_context, krb5_db_entry *client, - krb5_data *req_pkt, - krb5_kdc_req *request, - krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data *data, - preauth_get_entry_data_proc get_entry_data, - void *pa_module_context, - void **pa_request_context, - krb5_data **e_data); - -typedef krb5_error_code (*edata_proc) - (krb5_context, krb5_kdc_req *request, - krb5_db_entry *client, krb5_db_entry *server, - preauth_get_entry_data_proc get_entry_data, - void *pa_module_context, - krb5_pa_data *data); - -typedef krb5_error_code (*return_proc) - (krb5_context, krb5_pa_data * padata, - krb5_db_entry *client, - krb5_data *req_pkt, - krb5_kdc_req *request, krb5_kdc_rep *reply, - krb5_key_data *client_key, - krb5_keyblock *encrypting_key, - krb5_pa_data **send_pa, - preauth_get_entry_data_proc get_entry_data, - void *pa_module_context, - void **pa_request_context); - -typedef krb5_error_code (*freepa_proc) - (krb5_context, void *pa_module_context, void **pa_request_context); - -typedef krb5_error_code (*init_proc) - (krb5_context, void **); -typedef void (*fini_proc) - (krb5_context, void *); - typedef struct _krb5_preauth_systems { const char *name; int type; int flags; void *plugin_context; - init_proc init; - fini_proc fini; - edata_proc get_edata; - verify_proc verify_padata; - return_proc return_padata; - freepa_proc free_pa_request_context; + preauth_server_init_proc init; + preauth_server_fini_proc fini; + preauth_server_edata_proc get_edata; + preauth_server_verify_proc verify_padata; + preauth_server_return_proc return_padata; + preauth_server_free_reqcontext_proc free_pa_reqctx; } krb5_preauth_systems; static krb5_error_code verify_enc_timestamp @@ -305,7 +268,7 @@ load_preauth_plugins(krb5_context context) struct krb5plugin_preauth_server_ftable_v0 *ftable; int module_count, i, j, k; void *plugin_context; - init_proc server_init_proc = NULL; + preauth_server_init_proc server_init_proc = NULL; memset(&err, 0, sizeof(err)); @@ -428,7 +391,7 @@ load_preauth_plugins(krb5_context context) preauth_systems[k].get_edata = ftable->edata_proc; preauth_systems[k].verify_padata = ftable->verify_proc; preauth_systems[k].return_padata = ftable->return_proc; - preauth_systems[k].free_pa_request_context = + preauth_systems[k].free_pa_reqctx = ftable->freepa_reqcontext_proc; k++; } @@ -527,10 +490,9 @@ free_padata_context(krb5_context kcontext, void **padata_context) if (context->contexts[i].pa_context != NULL) { preauth_system = context->contexts[i].pa_system; mctx = preauth_system->plugin_context; - if (preauth_system->free_pa_request_context != NULL) { + if (preauth_system->free_pa_reqctx != NULL) { pctx = &context->contexts[i].pa_context; - (*preauth_system->free_pa_request_context)(kcontext, mctx, - pctx); + (*preauth_system->free_pa_reqctx)(kcontext, mctx, pctx); } context->contexts[i].pa_context = NULL; } diff --git a/src/lib/krb5/asn.1/asn1_k_decode.c b/src/lib/krb5/asn.1/asn1_k_decode.c index 9ccbffd..0b739bd 100644 --- a/src/lib/krb5/asn.1/asn1_k_decode.c +++ b/src/lib/krb5/asn.1/asn1_k_decode.c @@ -1653,7 +1653,17 @@ asn1_error_code asn1_decode_auth_pack(asn1buf *buf, krb5_auth_pack *val) } #endif +#if 0 opt_field(val->supportedCMSTypes, 2, asn1_decode_sequence_of_AlgorithmIdentifier, NULL); +#else + if (asn1buf_remains(&subbuf, seqindef)) { + if (tagnum == 2) { + asn1_decode_sequence_of_AlgorithmIdentifier(&subbuf,val->supportedCMSTypes); + if (!taglen && indef) { get_eoc(); } + next_tag(); + } else val->supportedCMSTypes = NULL; + } +#endif opt_lenfield(val->clientDHNonce.length, val->clientDHNonce.data, 3, asn1_decode_octetstring); end_structure(); } diff --git a/src/lib/krb5/asn.1/asn1_k_encode.c b/src/lib/krb5/asn.1/asn1_k_encode.c index 0add915..111f61d 100644 --- a/src/lib/krb5/asn.1/asn1_k_encode.c +++ b/src/lib/krb5/asn.1/asn1_k_encode.c @@ -1010,7 +1010,6 @@ asn1_error_code asn1_encode_krb_saved_safe_body(asn1buf *buf, const krb5_data *b asn1_error_code asn1_encode_pk_authenticator(asn1buf *buf, const krb5_pk_authenticator *val, unsigned int *retlen) { asn1_setup(); - asn1_addlenfield(val->paChecksum.length, val->paChecksum.contents, 3, asn1_encode_octetstring); asn1_addfield(val->nonce, 2, asn1_encode_integer); asn1_addfield(val->ctime, 1, asn1_encode_kerberos_time); diff --git a/src/lib/krb5/krb/get_in_tkt.c b/src/lib/krb5/krb/get_in_tkt.c index 80baf41..598354b 100644 --- a/src/lib/krb5/krb/get_in_tkt.c +++ b/src/lib/krb5/krb/get_in_tkt.c @@ -843,7 +843,7 @@ krb5_get_init_creds(krb5_context context, void *prompter_data, krb5_deltat start_time, char *in_tkt_service, - krb5_get_init_creds_opt *options, + krb5_gic_opt_ext *options, krb5_gic_get_as_key_fct gak_fct, void *gak_data, int *use_master, @@ -1111,7 +1111,7 @@ krb5_get_init_creds(krb5_context context, &salt, &s2kparams, &etype, &as_key, prompter, prompter_data, gak_fct, gak_data, - &get_data_rock))) + &get_data_rock, options))) goto cleanup; } else { /* retrying after an error other than PREAUTH_NEEDED, using e-data @@ -1125,7 +1125,7 @@ krb5_get_init_creds(krb5_context context, &salt, &s2kparams, &etype, &as_key, prompter, prompter_data, gak_fct, gak_data, - &get_data_rock)) { + &get_data_rock, options)) { /* couldn't come up with anything better */ ret = err_reply->error + ERROR_TABLE_BASE_krb5; krb5_free_error(context, err_reply); @@ -1206,7 +1206,7 @@ krb5_get_init_creds(krb5_context context, local_as_reply->padata, &kdc_padata, &salt, &s2kparams, &etype, &as_key, prompter, prompter_data, gak_fct, gak_data, - &get_data_rock))) + &get_data_rock, options))) goto cleanup; /* XXX For 1.1.1 and prior KDC's, when SAM is used w/ USE_SAD_AS_KEY, diff --git a/src/lib/krb5/krb/gic_keytab.c b/src/lib/krb5/krb/gic_keytab.c index ba704e6..cae0495 100644 --- a/src/lib/krb5/krb/gic_keytab.c +++ b/src/lib/krb5/krb/gic_keytab.c @@ -76,11 +76,18 @@ krb5_get_as_key_keytab( } krb5_error_code KRB5_CALLCONV -krb5_get_init_creds_keytab(krb5_context context, krb5_creds *creds, krb5_principal client, krb5_keytab arg_keytab, krb5_deltat start_time, char *in_tkt_service, krb5_get_init_creds_opt *options) +krb5_get_init_creds_keytab(krb5_context context, + krb5_creds *creds, + krb5_principal client, + krb5_keytab arg_keytab, + krb5_deltat start_time, + char *in_tkt_service, + krb5_get_init_creds_opt *options) { krb5_error_code ret, ret2; int use_master; krb5_keytab keytab; + krb5_gic_opt_ext *opte = NULL; if (arg_keytab == NULL) { if ((ret = krb5_kt_default(context, &keytab))) @@ -89,12 +96,17 @@ krb5_get_init_creds_keytab(krb5_context context, krb5_creds *creds, krb5_princip keytab = arg_keytab; } + ret = krb5int_gic_opt_to_opte(context, options, &opte, 1, + "krb5_get_init_creds_keytab"); + if (ret) + return ret; + use_master = 0; /* first try: get the requested tkt from any kdc */ ret = krb5_get_init_creds(context, creds, client, NULL, NULL, - start_time, in_tkt_service, options, + start_time, in_tkt_service, opte, krb5_get_as_key_keytab, (void *) keytab, &use_master,NULL); @@ -115,7 +127,7 @@ krb5_get_init_creds_keytab(krb5_context context, krb5_creds *creds, krb5_princip use_master = 1; ret2 = krb5_get_init_creds(context, creds, client, NULL, NULL, - start_time, in_tkt_service, options, + start_time, in_tkt_service, opte, krb5_get_as_key_keytab, (void *) keytab, &use_master, NULL); @@ -139,6 +151,8 @@ krb5_get_init_creds_keytab(krb5_context context, krb5_creds *creds, krb5_princip do any prompting or changing for keytabs, that's it. */ cleanup: + if (opte && krb5_gic_opt_is_shadowed(opte)) + krb5_get_init_creds_opt_free(context, (krb5_get_init_creds_opt *)opte); if (arg_keytab == NULL) krb5_kt_close(context, keytab); @@ -152,15 +166,18 @@ krb5_get_in_tkt_with_keytab(krb5_context context, krb5_flags options, krb5_creds *creds, krb5_kdc_rep **ret_as_reply) { krb5_error_code retval; - krb5_get_init_creds_opt opt; + krb5_gic_opt_ext *opte; char * server = NULL; krb5_keytab keytab; krb5_principal client_princ, server_princ; int use_master = 0; - krb5int_populate_gic_opt(context, &opt, - options, addrs, ktypes, - pre_auth_types, creds); + retval = krb5int_populate_gic_opt(context, &opte, + options, addrs, ktypes, + pre_auth_types, creds); + if (retval) + return retval; + if (arg_keytab == NULL) { retval = krb5_kt_default(context, &keytab); if (retval) @@ -176,10 +193,11 @@ krb5_get_in_tkt_with_keytab(krb5_context context, krb5_flags options, retval = krb5_get_init_creds (context, creds, creds->client, krb5_prompter_posix, NULL, - 0, server, &opt, + 0, server, opte, krb5_get_as_key_keytab, (void *)keytab, &use_master, ret_as_reply); krb5_free_unparsed_name( context, server); + krb5_get_init_creds_opt_free(context, (krb5_get_init_creds_opt *)opte); if (retval) { goto cleanup; } diff --git a/src/lib/krb5/krb/gic_opt.c b/src/lib/krb5/krb/gic_opt.c index 58d07b0..3ac7c52 100644 --- a/src/lib/krb5/krb/gic_opt.c +++ b/src/lib/krb5/krb/gic_opt.c @@ -63,3 +63,420 @@ krb5_get_init_creds_opt_set_salt(krb5_get_init_creds_opt *opt, krb5_data *salt) opt->flags |= KRB5_GET_INIT_CREDS_OPT_SALT; opt->salt = salt; } + +/* + * Extending the krb5_get_init_creds_opt structure. The original + * krb5_get_init_creds_opt structure is defined publicly. The + * new extended version is private. The original interface + * assumed a pre-allocated structure which was passed to + * krb5_get_init_creds_init(). The new interface assumes that + * the caller will call krb5_get_init_creds_alloc() and + * krb5_get_init_creds_free(). + * + * Callers MUST NOT call krb5_get_init_creds_init() after allocating an + * opts structure using krb5_get_init_creds_alloc(). To do so will + * introduce memory leaks. Unfortunately, there is no way to enforce + * this behavior. + * + * Two private flags are added for backward compatibility. + * KRB5_GET_INIT_CREDS_OPT_EXTENDED says that the structure was allocated + * with the new krb5_get_init_creds_opt_alloc() function. + * KRB5_GET_INIT_CREDS_OPT_SHADOWED is set to indicate that the extended + * structure is a shadow copy of an original krb5_get_init_creds_opt + * structure. + * If KRB5_GET_INIT_CREDS_OPT_SHADOWED is set after a call to + * krb5int_gic_opt_to_opte(), the resulting extended structure should be + * freed (using krb5_get_init_creds_free). Otherwise, the original + * structure was already extended and there is no need to free it. + */ + +/* Forward prototype */ +static void +free_gic_opt_ext_preauth_data(krb5_context context, + krb5_gic_opt_ext *opte); + +static krb5_error_code +krb5int_gic_opte_private_alloc(krb5_context context, krb5_gic_opt_ext *opte) +{ + if (NULL == opte || !krb5_gic_opt_is_extended(opte)) + return EINVAL; + + opte->opt_private = calloc(1, sizeof(*opte->opt_private)); + if (NULL == opte->opt_private) { + return ENOMEM; + } + /* Allocate any private stuff */ + opte->opt_private->num_preauth_data = 0; + opte->opt_private->preauth_data = NULL; + return 0; +} + +static krb5_error_code +krb5int_gic_opte_private_free(krb5_context context, krb5_gic_opt_ext *opte) +{ + if (NULL == opte || !krb5_gic_opt_is_extended(opte)) + return EINVAL; + + /* Free up any private stuff */ + if (opte->opt_private->preauth_data != NULL) + free_gic_opt_ext_preauth_data(context, opte); + free(opte->opt_private); + opte->opt_private = NULL; + return 0; +} + +static krb5_gic_opt_ext * +krb5int_gic_opte_alloc(krb5_context context) +{ + krb5_gic_opt_ext *opte; + krb5_error_code code; + + opte = calloc(1, sizeof(*opte)); + if (NULL == opte) + return NULL; + opte->flags = KRB5_GET_INIT_CREDS_OPT_EXTENDED; + + code = krb5int_gic_opte_private_alloc(context, opte); + if (code) { + krb5int_set_error(&context->err, code, + "krb5int_gic_opte_alloc: krb5int_gic_opte_private_alloc failed"); + free(opte); + return NULL; + } + return(opte); +} + +krb5_error_code KRB5_CALLCONV +krb5_get_init_creds_opt_alloc(krb5_context context, + krb5_get_init_creds_opt **opt) +{ + krb5_gic_opt_ext *opte; + + if (NULL == opt) + return EINVAL; + *opt = NULL; + + /* + * We return a new extended structure cast as a krb5_get_init_creds_opt + */ + opte = krb5int_gic_opte_alloc(context); + if (NULL == opte) + return ENOMEM; + + *opt = (krb5_get_init_creds_opt *) opte; + return 0; +} + +void KRB5_CALLCONV +krb5_get_init_creds_opt_free(krb5_context context, + krb5_get_init_creds_opt *opt) +{ + krb5_gic_opt_ext *opte; + + if (NULL == opt) + return; + + /* Don't touch it if we didn't allocate it */ + if (!krb5_gic_opt_is_extended(opt)) + return; + + opte = (krb5_gic_opt_ext *)opt; + if (opte->opt_private) + krb5int_gic_opte_private_free(context, opte); + + free(opte); +} + +static krb5_error_code +krb5int_gic_opte_copy(krb5_context context, + krb5_get_init_creds_opt *opt, + krb5_gic_opt_ext **opte) +{ + krb5_gic_opt_ext *oe; + + oe = krb5int_gic_opte_alloc(context); + if (NULL == oe) + return ENOMEM; + memcpy(oe, opt, sizeof(*opt)); + /* Fix these -- overwritten by the copy */ + oe->flags |= ( KRB5_GET_INIT_CREDS_OPT_EXTENDED | + KRB5_GET_INIT_CREDS_OPT_SHADOWED); + + *opte = oe; + return 0; +} + +/* + * Convert a krb5_get_init_creds_opt pointer to a pointer to + * an extended, krb5_gic_opt_ext pointer. If the original + * pointer already points to an extended structure, then simply + * return the original pointer. Otherwise, if 'force' is non-zero, + * allocate an extended structure and copy the original over it. + * If the original pointer did not point to an extended structure + * and 'force' is zero, then return an error. This is used in + * cases where the original *should* be an extended structure. + */ +krb5_error_code +krb5int_gic_opt_to_opte(krb5_context context, + krb5_get_init_creds_opt *opt, + krb5_gic_opt_ext **opte, + unsigned int force, + const char *where) +{ + if (!krb5_gic_opt_is_extended(opt)) { + if (force) { + return krb5int_gic_opte_copy(context, opt, opte); + } else { + krb5int_set_error(&context->err, EINVAL, + "%s: attempt to convert non-extended krb5_get_init_creds_opt", + where); + return EINVAL; + } + } + /* If it is already extended, just return it */ + *opte = (krb5_gic_opt_ext *)opt; + return 0; +} + +static void +free_gic_opt_ext_preauth_data(krb5_context context, + krb5_gic_opt_ext *opte) +{ + int i; + + if (NULL == opte || !krb5_gic_opt_is_extended(opte)) + return; + if (NULL == opte->opt_private || NULL == opte->opt_private->preauth_data) + return; + + for (i = 0; i < opte->opt_private->num_preauth_data; i++) { + if (opte->opt_private->preauth_data[i].attr != NULL) + free(opte->opt_private->preauth_data[i].attr); + if (opte->opt_private->preauth_data[i].value != NULL) + free(opte->opt_private->preauth_data[i].value); + } + free(opte->opt_private->preauth_data); + opte->opt_private->preauth_data = NULL; + opte->opt_private->num_preauth_data = 0; +} + +static krb5_error_code +add_gic_opt_ext_preauth_data(krb5_context context, + krb5_gic_opt_ext *opte, + const char *attr, + const char *value) +{ + size_t newsize; + int i; + krb5_gic_opt_pa_data *newpad; + + newsize = opte->opt_private->num_preauth_data + 1; + newsize = newsize * sizeof(*opte->opt_private->preauth_data); + if (opte->opt_private->preauth_data == NULL) + newpad = malloc(newsize); + else + newpad = realloc(opte->opt_private->preauth_data, newsize); + if (newpad == NULL) + return ENOMEM; + + i = opte->opt_private->num_preauth_data; + newpad[i].attr = strdup(attr); + if (newpad[i].attr == NULL) + return ENOMEM; + newpad[i].value = strdup(value); + if (newpad[i].value == NULL) { + free(newpad[i].attr); + return ENOMEM; + } + opte->opt_private->num_preauth_data += 1; + opte->opt_private->preauth_data = newpad; + return 0; +} + +/* + * This function allows the caller to supply options to preauth + * plugins. Preauth plugin modules are given a chance to look + * at each option at the time this function is called in ordre + * to check the validity of the option. + * The 'opt' pointer supplied to this function must have been + * obtained using krb5_get_init_creds_opt_alloc() + */ +krb5_error_code KRB5_CALLCONV +krb5_get_init_creds_opt_set_pa(krb5_context context, + krb5_get_init_creds_opt *opt, + const char *attr, + const char *value) +{ + krb5_error_code retval; + krb5_gic_opt_ext *opte; + + retval = krb5int_gic_opt_to_opte(context, opt, &opte, 0, + "krb5_get_init_creds_opt_set_pa"); + if (retval) + return retval; + + /* + * Copy the option into the extended get_init_creds_opt structure + */ + retval = add_gic_opt_ext_preauth_data(context, opte, attr, value); + if (retval) + return retval; + + /* + * Give the plugins a chance to look at the option now. + */ + retval = krb5_preauth_supply_preauth_data(context, opte, attr, value); + return retval; +} + +/* + * This function allows a preauth plugin to obtain preauth + * options. The preauth_data returned from this function + * should be freed by calling krb5_get_init_creds_opt_free_pa(). + * + * The 'opt' pointer supplied to this function must have been + * obtained using krb5_get_init_creds_opt_alloc() + */ +krb5_error_code KRB5_CALLCONV +krb5_get_init_creds_opt_get_pa(krb5_context context, + krb5_get_init_creds_opt *opt, + int *num_preauth_data, + krb5_gic_opt_pa_data **preauth_data) +{ + krb5_error_code retval; + krb5_gic_opt_ext *opte; + krb5_gic_opt_pa_data *p = NULL; + int i; + size_t allocsize; + + retval = krb5int_gic_opt_to_opte(context, opt, &opte, 0, + "krb5_get_init_creds_opt_get_pa"); + if (retval) + return retval; + + if (num_preauth_data == NULL || preauth_data == NULL) + return EINVAL; + + *num_preauth_data = 0; + *preauth_data = NULL; + + if (opte->opt_private->num_preauth_data == 0) + return 0; + + allocsize = + opte->opt_private->num_preauth_data * sizeof(krb5_gic_opt_pa_data); + p = malloc(allocsize); + if (p == NULL) + return ENOMEM; + + /* Init these to make cleanup easier */ + for (i = 0; i < opte->opt_private->num_preauth_data; i++) { + p[i].attr = NULL; + p[i].value = NULL; + } + + for (i = 0; i < opte->opt_private->num_preauth_data; i++) { + p[i].attr = strdup(opte->opt_private->preauth_data[i].attr); + p[i].value = strdup(opte->opt_private->preauth_data[i].value); + if (p[i].attr == NULL || p[i].value == NULL) + goto cleanup; + } + *num_preauth_data = i; + *preauth_data = p; + return 0; +cleanup: + for (i = 0; i < opte->opt_private->num_preauth_data; i++) { + if (p[i].attr != NULL) + free(p[i].attr); + if (p[i].value != NULL) + free(p[i].value); + } + free(p); + return ENOMEM; +} + +/* + * This function frees the preauth_data that was returned by + * krb5_get_init_creds_opt_get_pa(). + */ +void KRB5_CALLCONV +krb5_get_init_creds_opt_free_pa(krb5_context context, + int num_preauth_data, + krb5_gic_opt_pa_data *preauth_data) +{ + int i; + + if (num_preauth_data <= 0 || preauth_data == NULL) + return; + + for (i = 0; i < num_preauth_data; i++) { + if (preauth_data[i].attr != NULL) + free(preauth_data[i].attr); + if (preauth_data[i].value != NULL) + free(preauth_data[i].value); + } + free(preauth_data); +} + + +/* + * This function is provided for compatibility with Heimdal's + * function of the same name. We ignore the principal, + * password, and prompter parameters. + */ +krb5_error_code KRB5_CALLCONV +krb5_get_init_creds_opt_set_pkinit(krb5_context context, + krb5_get_init_creds_opt *opt, + krb5_principal principal, + const char *x509_user_identity, + const char *x509_anchors, + char * const * x509_chain_list, + char * const * x509_revoke_list, + int flags, + krb5_prompter_fct prompter, + void *prompter_data, + char *password) +{ + int i; + krb5_error_code retval; + +#define PKINIT_RSA_PROTOCOL 0x00000002 /* XXX */ + + if (x509_user_identity != NULL) { + retval = krb5_get_init_creds_opt_set_pa(context, opt, + "X509_user_identity", x509_user_identity); + if (retval) + return retval; + } + if (x509_anchors != NULL) { + retval = krb5_get_init_creds_opt_set_pa(context, opt, + "X509_anchors", x509_anchors); + if (retval) + return retval; + } + if (x509_chain_list != NULL) { + for (i = 0; x509_chain_list[i] != NULL; i++) { + retval = krb5_get_init_creds_opt_set_pa(context, opt, + "X509_chain_list", x509_chain_list[i]); + if (retval) + return retval; + } + } + if (x509_revoke_list != NULL) { + for (i = 0; x509_revoke_list[i] != NULL; i++) { + retval = krb5_get_init_creds_opt_set_pa(context, opt, + "X509_revoke_list", x509_revoke_list[i]); + if (retval) + return retval; + } + } + if (flags != 0) { + if (flags & PKINIT_RSA_PROTOCOL) { + retval = krb5_get_init_creds_opt_set_pa(context, opt, + "flag_RSA_PROTOCOL", "yes"); + if (retval) + return retval; + } + } + return retval; +} diff --git a/src/lib/krb5/krb/gic_pwd.c b/src/lib/krb5/krb/gic_pwd.c index 40288b9..5bf3000 100644 --- a/src/lib/krb5/krb/gic_pwd.c +++ b/src/lib/krb5/krb/gic_pwd.c @@ -85,18 +85,28 @@ krb5_get_as_key_password( } krb5_error_code KRB5_CALLCONV -krb5_get_init_creds_password(krb5_context context, krb5_creds *creds, krb5_principal client, char *password, krb5_prompter_fct prompter, void *data, krb5_deltat start_time, char *in_tkt_service, krb5_get_init_creds_opt *options) +krb5_get_init_creds_password(krb5_context context, + krb5_creds *creds, + krb5_principal client, + char *password, + krb5_prompter_fct prompter, + void *data, + krb5_deltat start_time, + char *in_tkt_service, + krb5_get_init_creds_opt *options) { krb5_error_code ret, ret2; int use_master; krb5_kdc_rep *as_reply; int tries; krb5_creds chpw_creds; - krb5_get_init_creds_opt chpw_opts; + krb5_get_init_creds_opt *chpw_opts = NULL; krb5_data pw0, pw1; char banner[1024], pw0array[1024], pw1array[1024]; krb5_prompt prompt[2]; krb5_prompt_type prompt_types[sizeof(prompt)/sizeof(prompt[0])]; + krb5_gic_opt_ext *opte = NULL; + krb5_gic_opt_ext *chpw_opte = NULL; use_master = 0; as_reply = NULL; @@ -119,10 +129,15 @@ krb5_get_init_creds_password(krb5_context context, krb5_creds *creds, krb5_princ pw1.data[0] = '\0'; pw1.length = sizeof(pw1array); + ret = krb5int_gic_opt_to_opte(context, options, &opte, 1, + "krb5_get_init_creds_password"); + if (ret) + goto cleanup; + /* first try: get the requested tkt from any kdc */ ret = krb5_get_init_creds(context, creds, client, prompter, data, - start_time, in_tkt_service, options, + start_time, in_tkt_service, opte, krb5_get_as_key_password, (void *) &pw0, &use_master, &as_reply); @@ -151,7 +166,7 @@ krb5_get_init_creds_password(krb5_context context, krb5_creds *creds, krb5_princ as_reply = NULL; } ret2 = krb5_get_init_creds(context, creds, client, prompter, data, - start_time, in_tkt_service, options, + start_time, in_tkt_service, opte, krb5_get_as_key_password, (void *) &pw0, &use_master, &as_reply); @@ -189,15 +204,21 @@ krb5_get_init_creds_password(krb5_context context, krb5_creds *creds, krb5_princ /* use a minimal set of options */ - krb5_get_init_creds_opt_init(&chpw_opts); - krb5_get_init_creds_opt_set_tkt_life(&chpw_opts, 5*60); - krb5_get_init_creds_opt_set_renew_life(&chpw_opts, 0); - krb5_get_init_creds_opt_set_forwardable(&chpw_opts, 0); - krb5_get_init_creds_opt_set_proxiable(&chpw_opts, 0); + ret = krb5_get_init_creds_opt_alloc(context, &chpw_opts); + if (ret) + goto cleanup; + krb5_get_init_creds_opt_set_tkt_life(chpw_opts, 5*60); + krb5_get_init_creds_opt_set_renew_life(chpw_opts, 0); + krb5_get_init_creds_opt_set_forwardable(chpw_opts, 0); + krb5_get_init_creds_opt_set_proxiable(chpw_opts, 0); + ret = krb5int_gic_opt_to_opte(context, chpw_opts, &chpw_opte, 0, + "krb5_get_init_creds_password (changing password)"); + if (ret) + goto cleanup; if ((ret = krb5_get_init_creds(context, &chpw_creds, client, prompter, data, - start_time, "kadmin/changepw", &chpw_opts, + start_time, "kadmin/changepw", chpw_opte, krb5_get_as_key_password, (void *) &pw0, &use_master, NULL))) goto cleanup; @@ -285,7 +306,7 @@ krb5_get_init_creds_password(krb5_context context, krb5_creds *creds, krb5_princ is final. */ ret = krb5_get_init_creds(context, creds, client, prompter, data, - start_time, in_tkt_service, options, + start_time, in_tkt_service, opte, krb5_get_as_key_password, (void *) &pw0, &use_master, &as_reply); @@ -365,6 +386,10 @@ cleanup: } } + if (chpw_opts) + krb5_get_init_creds_opt_free(context, chpw_opts); + if (opte && krb5_gic_opt_is_shadowed(opte)) + krb5_get_init_creds_opt_free(context, (krb5_get_init_creds_opt *)opte); memset(pw0array, 0, sizeof(pw0array)); memset(pw1array, 0, sizeof(pw1array)); krb5_free_cred_contents(context, &chpw_creds); @@ -373,15 +398,20 @@ cleanup: return(ret); } -void krb5int_populate_gic_opt ( - krb5_context context, krb5_get_init_creds_opt *opt, +krb5_error_code krb5int_populate_gic_opt ( + krb5_context context, krb5_gic_opt_ext **opte, krb5_flags options, krb5_address * const *addrs, krb5_enctype *ktypes, krb5_preauthtype *pre_auth_types, krb5_creds *creds) { int i; krb5_int32 starttime; + krb5_get_init_creds_opt *opt; + krb5_error_code retval; + + retval = krb5_get_init_creds_opt_alloc(context, &opt); + if (retval) + return(retval); - krb5_get_init_creds_opt_init(opt); if (addrs) krb5_get_init_creds_opt_set_address_list(opt, (krb5_address **) addrs); if (ktypes) { @@ -405,6 +435,8 @@ void krb5int_populate_gic_opt ( if (creds->times.starttime) starttime = creds->times.starttime; krb5_get_init_creds_opt_set_tkt_life(opt, creds->times.endtime - starttime); } + return krb5int_gic_opt_to_opte(context, opt, opte, 0, + "krb5int_populate_gic_opt"); } /* @@ -437,10 +469,10 @@ krb5_get_in_tkt_with_password(krb5_context context, krb5_flags options, krb5_error_code retval; krb5_data pw0; char pw0array[1024]; - krb5_get_init_creds_opt opt; char * server; krb5_principal server_princ, client_princ; int use_master = 0; + krb5_gic_opt_ext *opte = NULL; pw0array[0] = '\0'; pw0.data = pw0array; @@ -454,21 +486,26 @@ krb5_get_in_tkt_with_password(krb5_context context, krb5_flags options, } else { pw0.length = sizeof(pw0array); } - krb5int_populate_gic_opt(context, &opt, - options, addrs, ktypes, - pre_auth_types, creds); - retval = krb5_unparse_name( context, creds->server, &server); + retval = krb5int_populate_gic_opt(context, &opte, + options, addrs, ktypes, + pre_auth_types, creds); if (retval) return (retval); + retval = krb5_unparse_name( context, creds->server, &server); + if (retval) { + return (retval); + krb5_get_init_creds_opt_free(context, (krb5_get_init_creds_opt *)opte); + } server_princ = creds->server; client_princ = creds->client; retval = krb5_get_init_creds (context, creds, creds->client, krb5_prompter_posix, NULL, - 0, server, &opt, + 0, server, opte, krb5_get_as_key_password, &pw0, &use_master, ret_as_reply); krb5_free_unparsed_name( context, server); + krb5_get_init_creds_opt_free(context, (krb5_get_init_creds_opt *)opte); if (retval) { return (retval); } diff --git a/src/lib/krb5/krb/preauth2.c b/src/lib/krb5/krb/preauth2.c index 7428896..05fb08e 100644 --- a/src/lib/krb5/krb/preauth2.c +++ b/src/lib/krb5/krb/preauth2.c @@ -161,6 +161,10 @@ krb5_init_preauth_context(krb5_context kcontext) context->modules[k].use_count = 0; context->modules[k].client_process = table->process; context->modules[k].client_tryagain = table->tryagain; + if (j == 0) + context->modules[k].client_supply_gic_opts = table->gic_opts; + else + context->modules[k].client_supply_gic_opts = NULL; context->modules[k].request_context = NULL; /* * Only call request_init and request_fini once per plugin. @@ -209,6 +213,52 @@ krb5_clear_preauth_context_use_counts(krb5_context context) } } +/* + * Give all the preauth plugins a look at the preauth option which + * has just been set + */ +krb5_error_code +krb5_preauth_supply_preauth_data(krb5_context context, + krb5_gic_opt_ext *opte, + const char *attr, + const char *value) +{ + krb5_error_code retval; + int i; + void *pctx; + const char *emsg = NULL; + + if (context->preauth_context == NULL) + krb5_init_preauth_context(context); + if (context->preauth_context == NULL) { + retval = EINVAL; + krb5int_set_error(&context->err, retval, + "krb5_preauth_supply_preauth_data: " + "Unable to initialize preauth context"); + return retval; + } + + /* + * Go down the list of preauth modules, and supply them with the + * attribute/value pair. + */ + for (i = 0; i < context->preauth_context->n_modules; i++) { + if (context->preauth_context->modules[i].client_supply_gic_opts == NULL) + continue; + pctx = context->preauth_context->modules[i].plugin_context; + retval = (*context->preauth_context->modules[i].client_supply_gic_opts) + (context, pctx, + (krb5_get_init_creds_opt *)opte, attr, value); + if (retval) { + emsg = krb5_get_error_message(context, retval); + krb5int_set_error(&context->err, retval, "Preauth plugin %s: %s", + context->preauth_context->modules[i].name, emsg); + break; + } + } + return retval; +} + /* Free the per-krb5_context preauth_context. This means clearing any * plugin-specific context which may have been created, and then * freeing the context itself. */ @@ -403,7 +453,7 @@ client_data_proc(krb5_context kcontext, * involved things. */ void krb5_preauth_prepare_request(krb5_context kcontext, - krb5_get_init_creds_opt *options, + krb5_gic_opt_ext *opte, krb5_kdc_req *request) { int i, j; @@ -413,7 +463,7 @@ krb5_preauth_prepare_request(krb5_context kcontext, } /* Add the module-specific enctype list to the request, but only if * it's something we can safely modify. */ - if (!(options && (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST))) { + if (!(opte && (opte->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST))) { for (i = 0; i < kcontext->preauth_context->n_modules; i++) { if (kcontext->preauth_context->modules[i].enctypes == NULL) continue; @@ -446,7 +496,8 @@ krb5_run_preauth_plugins(krb5_context kcontext, krb5_pa_data ***out_pa_list, int *out_pa_list_size, int *module_ret, - int *module_flags) + int *module_flags, + krb5_gic_opt_ext *opte) { int i; krb5_pa_data *out_pa_data; @@ -485,6 +536,7 @@ krb5_run_preauth_plugins(krb5_context kcontext, ret = module->client_process(kcontext, module->plugin_context, *module->request_context_pp, + (krb5_get_init_creds_opt *)opte, client_data_proc, get_data_rock, request, @@ -1311,7 +1363,8 @@ krb5_do_preauth_tryagain(krb5_context kcontext, krb5_keyblock *as_key, krb5_prompter_fct prompter, void *prompter_data, krb5_gic_get_as_key_fct gak_fct, void *gak_data, - krb5_preauth_client_rock *get_data_rock) + krb5_preauth_client_rock *get_data_rock, + krb5_gic_opt_ext *opte) { krb5_error_code ret; krb5_pa_data *out_padata; @@ -1342,6 +1395,7 @@ krb5_do_preauth_tryagain(krb5_context kcontext, if ((*module->client_tryagain)(kcontext, module->plugin_context, *module->request_context_pp, + (krb5_get_init_creds_opt *)opte, client_data_proc, get_data_rock, request, @@ -1374,7 +1428,8 @@ krb5_do_preauth(krb5_context context, krb5_keyblock *as_key, krb5_prompter_fct prompter, void *prompter_data, krb5_gic_get_as_key_fct gak_fct, void *gak_data, - krb5_preauth_client_rock *get_data_rock) + krb5_preauth_client_rock *get_data_rock, + krb5_gic_opt_ext *opte) { int h, i, j, out_pa_list_size; int seen_etype_info2 = 0; @@ -1567,7 +1622,8 @@ krb5_do_preauth(krb5_context context, &out_pa_list, &out_pa_list_size, &module_ret, - &module_flags); + &module_flags, + opte); if (ret == 0) { if (module_ret == 0) { if (paorder[h] == PA_REAL) { diff --git a/src/lib/krb5/libkrb5.exports b/src/lib/krb5/libkrb5.exports index 0393354..553dc0f 100644 --- a/src/lib/krb5/libkrb5.exports +++ b/src/lib/krb5/libkrb5.exports @@ -164,8 +164,6 @@ decode_krb5_as_rep decode_krb5_as_req decode_krb5_authdata decode_krb5_authenticator -decode_krb5_auth_pack -decode_krb5_auth_pack_draft9 decode_krb5_cred decode_krb5_enc_cred_part decode_krb5_enc_data @@ -179,20 +177,13 @@ decode_krb5_encryption_key decode_krb5_error decode_krb5_etype_info decode_krb5_etype_info2 -decode_krb5_kdc_dh_key_info decode_krb5_kdc_req_body decode_krb5_pa_enc_ts -decode_krb5_pa_pk_as_rep -decode_krb5_pa_pk_as_req -decode_krb5_pa_pk_as_req_draft9 decode_krb5_padata_sequence decode_krb5_predicted_sam_response -decode_krb5_principal_name decode_krb5_priv decode_krb5_pwd_data decode_krb5_pwd_sequence -decode_krb5_reply_key_pack -decode_krb5_reply_key_pack_draft9 decode_krb5_safe decode_krb5_safe_with_body decode_krb5_sam_challenge @@ -203,9 +194,6 @@ 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 @@ -214,8 +202,6 @@ encode_krb5_as_rep encode_krb5_as_req encode_krb5_authdata encode_krb5_authenticator -encode_krb5_auth_pack -encode_krb5_auth_pack_draft9 encode_krb5_cred encode_krb5_enc_cred_part encode_krb5_enc_data @@ -228,20 +214,13 @@ encode_krb5_encryption_key encode_krb5_error encode_krb5_etype_info encode_krb5_etype_info2 -encode_krb5_kdc_dh_key_info encode_krb5_kdc_req_body encode_krb5_pa_enc_ts -encode_krb5_pa_pk_as_rep -encode_krb5_pa_pk_as_rep_draft9 -encode_krb5_pa_pk_as_req -encode_krb5_pa_pk_as_req_draft9 encode_krb5_padata_sequence encode_krb5_predicted_sam_response encode_krb5_priv encode_krb5_pwd_data encode_krb5_pwd_sequence -encode_krb5_reply_key_pack -encode_krb5_reply_key_pack_draft9 encode_krb5_safe encode_krb5_safe_with_body encode_krb5_sam_challenge @@ -251,12 +230,9 @@ 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 @@ -460,10 +436,15 @@ krb5_get_in_tkt_with_password krb5_get_in_tkt_with_skey krb5_get_init_creds krb5_get_init_creds_keytab +krb5_get_init_creds_opt_alloc +krb5_get_init_creds_opt_free +krb5_get_init_creds_opt_free_pa +krb5_get_init_creds_opt_get_pa krb5_get_init_creds_opt_init krb5_get_init_creds_opt_set_address_list krb5_get_init_creds_opt_set_etype_list krb5_get_init_creds_opt_set_forwardable +krb5_get_init_creds_opt_set_pa krb5_get_init_creds_opt_set_preauth_list krb5_get_init_creds_opt_set_proxiable krb5_get_init_creds_opt_set_renew_life @@ -761,3 +742,4 @@ krb5_get_error_message krb5_free_error_message krb5_clear_error_message krb5int_init_context_kdc +krb5_get_init_creds_opt_set_pkinit diff --git a/src/lib/krb5/os/accessor.c b/src/lib/krb5/os/accessor.c index cd7d0c3..ba87ad7 100644 --- a/src/lib/krb5/os/accessor.c +++ b/src/lib/krb5/os/accessor.c @@ -77,6 +77,30 @@ krb5int_accessor(krb5int_access *internals, krb5_int32 version) S (krb5_ser_unpack_int64, krb5_ser_unpack_int64), S (asn1_ldap_encode_sequence_of_keys, krb5int_ldap_encode_sequence_of_keys), S (asn1_ldap_decode_sequence_of_keys, krb5int_ldap_decode_sequence_of_keys), + S (encode_krb5_pa_pk_as_req, encode_krb5_pa_pk_as_req), + S (encode_krb5_pa_pk_as_req_draft9, encode_krb5_pa_pk_as_req_draft9), S (encode_krb5_pa_pk_as_rep, encode_krb5_pa_pk_as_rep), + S (encode_krb5_pa_pk_as_rep_draft9, encode_krb5_pa_pk_as_rep_draft9), + S (encode_krb5_auth_pack, encode_krb5_auth_pack), + S (encode_krb5_auth_pack_draft9, encode_krb5_auth_pack_draft9), + S (encode_krb5_kdc_dh_key_info, encode_krb5_kdc_dh_key_info), + S (encode_krb5_reply_key_pack, encode_krb5_reply_key_pack), + S (encode_krb5_reply_key_pack_draft9, encode_krb5_reply_key_pack_draft9), + S (encode_krb5_typed_data, encode_krb5_typed_data), + S (encode_krb5_td_trusted_certifiers, encode_krb5_td_trusted_certifiers), + S (encode_krb5_td_dh_parameters, encode_krb5_td_dh_parameters), + S (decode_krb5_pa_pk_as_req, decode_krb5_pa_pk_as_req), + S (decode_krb5_pa_pk_as_req_draft9, decode_krb5_pa_pk_as_req_draft9), + S (decode_krb5_pa_pk_as_rep, decode_krb5_pa_pk_as_rep), + S (decode_krb5_pa_pk_as_rep_draft9, decode_krb5_pa_pk_as_rep_draft9), + S (decode_krb5_auth_pack, decode_krb5_auth_pack), + S (decode_krb5_auth_pack_draft9, decode_krb5_auth_pack_draft9), + S (decode_krb5_kdc_dh_key_info, decode_krb5_kdc_dh_key_info), + S (decode_krb5_principal_name, decode_krb5_principal_name), + S (decode_krb5_reply_key_pack, decode_krb5_reply_key_pack), + S (decode_krb5_reply_key_pack_draft9, decode_krb5_reply_key_pack_draft9), + S (decode_krb5_typed_data, decode_krb5_typed_data), + S (decode_krb5_td_trusted_certifiers, decode_krb5_td_trusted_certifiers), + S (decode_krb5_td_dh_parameters, decode_krb5_td_dh_parameters), #if DESIGNATED_INITIALIZERS }; #else diff --git a/src/lib/krb5_32.def b/src/lib/krb5_32.def index 6a9060b..d347835 100644 --- a/src/lib/krb5_32.def +++ b/src/lib/krb5_32.def @@ -155,7 +155,12 @@ krb5_c_string_to_key_with_params krb5_get_in_tkt_with_password ; DEPRECATED krb5_get_in_tkt_with_skey ; DEPRECATED krb5_get_init_creds_keytab + krb5_get_init_creds_opt_alloc + krb5_get_init_creds_opt_free + krb5_get_init_creds_opt_free_pa + krb5_get_init_creds_opt_get_pa krb5_get_init_creds_opt_init + krb5_get_init_creds_opt_set_pa krb5_get_init_creds_opt_set_address_list krb5_get_init_creds_opt_set_etype_list krb5_get_init_creds_opt_set_forwardable diff --git a/src/plugins/preauth/cksum_body/cksum_body_main.c b/src/plugins/preauth/cksum_body/cksum_body_main.c index 6b46b00..59bf525 100644 --- a/src/plugins/preauth/cksum_body/cksum_body_main.c +++ b/src/plugins/preauth/cksum_body/cksum_body_main.c @@ -68,6 +68,11 @@ struct server_stats{ int successes, failures; }; +typedef struct _test_svr_req_ctx { + int value1; + int value2; +} test_svr_req_ctx; + static int client_get_flags(krb5_context kcontext, krb5_preauthtype pa_type) { @@ -78,6 +83,7 @@ static krb5_error_code client_process(krb5_context kcontext, void *client_plugin_context, void *client_request_context, + krb5_get_init_creds_opt *opt, preauth_get_client_data_proc client_get_data_proc, struct _krb5_preauth_client_rock *rock, krb5_kdc_req *request, @@ -99,6 +105,27 @@ client_process(krb5_context kcontext, krb5_error_code status = 0; krb5_int32 cksumtype, *enctypes; unsigned int i, n_enctypes, cksumtype_count; + int num_gic_info = 0; + krb5_gic_opt_pa_data *gic_info; + + status = krb5_get_init_creds_opt_get_pa(kcontext, opt, + &num_gic_info, &gic_info); + if (status && status != ENOENT) { +#ifdef DEBUG + fprintf(stderr, "Error from krb5_get_init_creds_opt_get_pa: %s\n", + error_message(status)); +#endif + return status; + } +#ifdef DEBUG + fprintf(stderr, "(cksum_body) Got the following gic options:\n"); +#endif + for (i = 0; i < num_gic_info; i++) { +#ifdef DEBUG + fprintf(stderr, " '%s' = '%s'\n", gic_info[i].attr, gic_info[i].value); +#endif + } + krb5_get_init_creds_opt_free_pa(kcontext, num_gic_info, gic_info); memset(&checksum, 0, sizeof(checksum)); @@ -193,6 +220,20 @@ client_process(krb5_context kcontext, return 0; } +static krb5_error_code +client_gic_opt(krb5_context kcontext, + void *plugin_context, + krb5_get_init_creds_opt *opt, + const char *attr, + const char *value) +{ +#ifdef DEBUG + fprintf(stderr, "(cksum_body) client_gic_opt: received '%s' = '%s'\n", + attr, value); +#endif + return 0; +} + /* Initialize and tear down the server-side module, and do stat tracking. */ static krb5_error_code server_init(krb5_context kcontext, void **module_context) @@ -200,7 +241,7 @@ server_init(krb5_context kcontext, void **module_context) struct server_stats *stats; stats = malloc(sizeof(struct server_stats)); if (stats == NULL) - return ENOMEM; + return ENOMEM; stats->successes = 0; stats->failures = 0; *module_context = stats; @@ -304,6 +345,7 @@ server_verify(krb5_context kcontext, krb5_error_code status; struct server_stats *stats; krb5_data *test_edata; + test_svr_req_ctx *svr_req_ctx; stats = pa_module_context; @@ -456,6 +498,18 @@ server_verify(krb5_context kcontext, } } + /* Return a request context to exercise code that handles it */ + svr_req_ctx = malloc(sizeof(*svr_req_ctx)); + if (svr_req_ctx != NULL) { + svr_req_ctx->value1 = 111111; + svr_req_ctx->value2 = 222222; +#ifdef DEBUG + fprintf(stderr, "server_verify: returning context at %p\n", + svr_req_ctx); +#endif + } + *pa_request_context = svr_req_ctx; + /* Note that preauthentication succeeded. */ enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH; stats->successes++; @@ -482,6 +536,37 @@ server_return(krb5_context kcontext, return 0; } +/* Test server request context freeing */ +static krb5_error_code +server_free_reqctx(krb5_context kcontext, + void *pa_module_context, + void **pa_request_context) +{ + test_svr_req_ctx *svr_req_ctx; +#ifdef DEBUG + fprintf(stderr, "server_free_reqctx: entered!\n"); +#endif + if (pa_request_context == NULL) + return 0; + + svr_req_ctx = *pa_request_context; + if (svr_req_ctx == NULL) + return 0; + + if (svr_req_ctx->value1 != 111111 || svr_req_ctx->value2 != 222222) { + fprintf(stderr, "server_free_reqctx: got invalid req context " + "at %p with values %d and %d\n", + svr_req_ctx, svr_req_ctx->value1, svr_req_ctx->value2); + return EINVAL; + } +#ifdef DEBUG + fprintf(stderr, "server_free_reqctx: freeing context at %p\n", svr_req_ctx); +#endif + free(svr_req_ctx); + *pa_request_context = NULL; + return 0; +} + static int server_get_flags(krb5_context kcontext, krb5_preauthtype pa_type) { @@ -506,6 +591,7 @@ struct krb5plugin_preauth_client_ftable_v0 preauthentication_client_0 = { NULL, /* request fini function */ client_process, /* process function */ NULL, /* try_again function */ + client_gic_opt /* get init creds opt function */ }; struct krb5plugin_preauth_server_ftable_v0 preauthentication_server_0 = { @@ -517,5 +603,5 @@ struct krb5plugin_preauth_server_ftable_v0 preauthentication_server_0 = { server_get_edata, server_verify, server_return, - NULL + server_free_reqctx }; diff --git a/src/plugins/preauth/pkinit/Makefile.in b/src/plugins/preauth/pkinit/Makefile.in index 1bfdddc..30ab4d0 100644 --- a/src/plugins/preauth/pkinit/Makefile.in +++ b/src/plugins/preauth/pkinit/Makefile.in @@ -27,12 +27,14 @@ SHLIB_DIRS=-L$(TOPLIBD) SHLIB_RDIRS=$(KRB5_LIBDIR) SRCS= \ + $(srcdir)/pkinit_accessor.c \ $(srcdir)/pkinit_srv.c \ $(srcdir)/pkinit_lib.c \ $(srcdir)/pkinit_clnt.c STOBJLISTS=OBJS.ST STLIBOBJS= \ + pkinit_accessor.o \ pkinit_srv.o \ pkinit_lib.o \ pkinit_clnt.o diff --git a/src/plugins/preauth/pkinit/pkinit.h b/src/plugins/preauth/pkinit/pkinit.h index 89a86e9..cea8efb 100644 --- a/src/plugins/preauth/pkinit/pkinit.h +++ b/src/plugins/preauth/pkinit/pkinit.h @@ -31,6 +31,11 @@ #ifndef _PKINIT_H #define _PKINIT_H +#include "pkinit_accessor.h" + +#define DH_PROTOCOL 1 +#define RSA_PROTOCOL 2 + extern const krb5_octet_data dh_oid; extern unsigned char pkinit_1024_dhprime[1024/8]; extern unsigned char pkinit_2048_dhprime[2048/8]; @@ -38,9 +43,11 @@ extern unsigned char pkinit_4096_dhprime[4096/8]; typedef struct _pkinit_context { int magic; + krb5_context context; int require_eku; int require_san; int allow_upn; + int dh_or_rsa; int require_crl_checking; char *ctx_identity; char *ctx_anchors; @@ -68,20 +75,47 @@ typedef struct _pkinit_context { typedef struct _pkinit_req_context { int magic; + pkinit_context *plugctx; DH *dh; int dh_size; int require_eku; int require_san; int require_hostname_match; int allow_upn; + int dh_or_rsa; int require_crl_checking; int win2k_target; int win2k_require_cksum; krb5_preauthtype patype; + krb5_prompter_fct prompter; + void *prompter_data; + int pkcs11_method; +#ifndef WITHOUT_PKCS11 + char *p11_module_name; + void *p11_module; + unsigned int slotid; + CK_SESSION_HANDLE session; + CK_FUNCTION_LIST_PTR p11; + CK_BYTE_PTR cert_id; + int cert_id_len; + CK_MECHANISM_TYPE mech; +#endif + void *credctx; } pkinit_req_context; -/* Function prototypes */ +typedef struct _pkinit_cred_context { + STACK_OF(X509) *cert; + STACK_OF(X509) *trustedCAs; + STACK_OF(X509) *untrustedCAs; + DH *dh; +} pkinit_cred_context; + +int pkinit_get_certs(int type, STACK_OF(X509) **certs); +int get_file_certs(char *name, STACK_OF(X509) **certs); +int get_dir_certs(char *name, STACK_OF(X509) **certs); +int get_pkcs11_certs(char *name, STACK_OF(X509) **certs); +/* Function prototypes */ void openssl_init(void); krb5_error_code pkinit_init_dh_params(krb5_context, pkinit_context *); @@ -94,14 +128,14 @@ 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); + (pkinit_req_context *, unsigned char *data, int data_len, + unsigned char **sig, int *sig_len, char *filename); krb5_error_code create_signature (unsigned char **, int *, unsigned char *, int, char *); krb5_error_code pkinit_decode_data - (unsigned char *data, int data_len, unsigned char **decoded, + (pkinit_req_context *, unsigned char *data, int data_len, unsigned char **decoded, int *decoded_len, char *filename, X509 *cert); krb5_error_code decode_data @@ -109,7 +143,7 @@ krb5_error_code decode_data krb5_error_code pkcs7_signeddata_create (unsigned char *, int, unsigned char **, int *, X509 *, - char *, ASN1_OBJECT *, krb5_context); + char *, ASN1_OBJECT *, krb5_context, pkinit_req_context *); krb5_error_code pkcs7_signeddata_verify (unsigned char *, int, char **, int *, X509 **, @@ -127,7 +161,7 @@ krb5_error_code pkcs7_envelopeddata_create krb5_error_code pkcs7_envelopeddata_verify (unsigned char *, int, char **, int *, X509 *, char *, - krb5_preauthtype , pkinit_context *, X509 **, krb5_context); + krb5_preauthtype, X509 **, pkinit_req_context *); int verify_id_pkinit_san (X509 * x, krb5_principal *out, krb5_context context, diff --git a/src/plugins/preauth/pkinit/pkinit_accessor.c b/src/plugins/preauth/pkinit/pkinit_accessor.c new file mode 100644 index 0000000..bdd7810 --- /dev/null +++ b/src/plugins/preauth/pkinit/pkinit_accessor.c @@ -0,0 +1,100 @@ +/* + * COPYRIGHT (C) 2006 + * THE REGENTS OF THE UNIVERSITY OF MICHIGAN + * ALL RIGHTS RESERVED + * + * Permission is granted to use, copy, create derivative works + * and redistribute this software and such derivative works + * for any purpose, so long as the name of The University of + * Michigan is not used in any advertising or publicity + * pertaining to the use of distribution of this software + * without specific, written prior authorization. If the + * above copyright notice or any other identification of the + * University of Michigan is included in any copy of any + * portion of this software, then the disclaimer below must + * also be included. + * + * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION + * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY + * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF + * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING + * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE + * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE + * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR + * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING + * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN + * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGES. + */ + +#include <k5-int.h> + +#define DEF_FUNC_PTRS(type) \ +krb5_error_code (*k5int_encode_##type)(const type *, krb5_data **); \ +krb5_error_code (*k5int_decode_##type)(const krb5_data *, type **) + +#define DEF_FUNC_PTRS_ARRAY(type) \ +krb5_error_code (*k5int_encode_##type)(const type **, krb5_data **); \ +krb5_error_code (*k5int_decode_##type)(const krb5_data *, type ***) + +DEF_FUNC_PTRS(krb5_auth_pack); +DEF_FUNC_PTRS(krb5_auth_pack_draft9); +DEF_FUNC_PTRS(krb5_kdc_dh_key_info); +DEF_FUNC_PTRS(krb5_pa_pk_as_rep); +DEF_FUNC_PTRS(krb5_pa_pk_as_rep_draft9); +DEF_FUNC_PTRS(krb5_pa_pk_as_req); +DEF_FUNC_PTRS(krb5_pa_pk_as_req_draft9); +DEF_FUNC_PTRS(krb5_reply_key_pack); +DEF_FUNC_PTRS(krb5_reply_key_pack_draft9); +DEF_FUNC_PTRS_ARRAY(krb5_typed_data); + +/* special cases... */ +krb5_error_code (*k5int_decode_krb5_principal_name) + (const krb5_data *, krb5_principal_data **); + +krb5_error_code (*k5int_encode_krb5_td_dh_parameters) + (const krb5_algorithm_identifier **, krb5_data **code); +krb5_error_code (*k5int_decode_krb5_td_dh_parameters) + (const krb5_data *, krb5_algorithm_identifier ***); + +krb5_error_code (*k5int_encode_krb5_td_trusted_certifiers) + (const krb5_external_principal_identifier **, krb5_data **code); +krb5_error_code (*k5int_decode_krb5_td_trusted_certifiers) + (const krb5_data *, krb5_external_principal_identifier ***); + + + +/* + * Grab internal function pointers from the krb5int_accessor + * structure and make them available + */ +krb5_error_code pkinit_accessor_init() +{ + krb5_error_code retval; + krb5int_access k5int; + + retval = krb5int_accessor(&k5int, KRB5INT_ACCESS_VERSION); + if (retval) + return retval; +#define SET_PTRS(type) \ +k5int_encode_##type = k5int.encode_##type; \ +k5int_decode_##type = k5int.decode_##type; + + SET_PTRS(krb5_auth_pack); + SET_PTRS(krb5_auth_pack_draft9); + SET_PTRS(krb5_kdc_dh_key_info); + SET_PTRS(krb5_pa_pk_as_rep); + SET_PTRS(krb5_pa_pk_as_rep_draft9); + SET_PTRS(krb5_pa_pk_as_req); + SET_PTRS(krb5_pa_pk_as_req_draft9); + SET_PTRS(krb5_reply_key_pack); + SET_PTRS(krb5_reply_key_pack_draft9); + SET_PTRS(krb5_td_dh_parameters); + SET_PTRS(krb5_td_trusted_certifiers); + SET_PTRS(krb5_typed_data); + + /* special cases... */ + k5int_decode_krb5_principal_name = k5int.decode_krb5_principal_name; + return 0; +} diff --git a/src/plugins/preauth/pkinit/pkinit_accessor.h b/src/plugins/preauth/pkinit/pkinit_accessor.h new file mode 100644 index 0000000..1e1cf2e --- /dev/null +++ b/src/plugins/preauth/pkinit/pkinit_accessor.h @@ -0,0 +1,67 @@ +/* + * COPYRIGHT (C) 2006 + * THE REGENTS OF THE UNIVERSITY OF MICHIGAN + * ALL RIGHTS RESERVED + * + * Permission is granted to use, copy, create derivative works + * and redistribute this software and such derivative works + * for any purpose, so long as the name of The University of + * Michigan is not used in any advertising or publicity + * pertaining to the use of distribution of this software + * without specific, written prior authorization. If the + * above copyright notice or any other identification of the + * University of Michigan is included in any copy of any + * portion of this software, then the disclaimer below must + * also be included. + * + * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION + * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY + * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF + * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING + * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE + * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE + * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR + * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING + * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN + * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGES. + */ + +#ifndef _PKINIT_ACCESSOR_H +#define _PKINIT_ACCESSOR_H + +#define DEF_EXT_FUNC_PTRS(type) \ +extern krb5_error_code (*k5int_encode_##type)(const type *, krb5_data **); \ +extern krb5_error_code (*k5int_decode_##type)(const krb5_data *, type **) + +#define DEF_EXT_FUNC_PTRS_ARRAY(type) \ +extern krb5_error_code (*k5int_encode_##type)(const type **, krb5_data **); \ +extern krb5_error_code (*k5int_decode_##type)(const krb5_data *, type ***) + +DEF_EXT_FUNC_PTRS(krb5_auth_pack); +DEF_EXT_FUNC_PTRS(krb5_auth_pack_draft9); +DEF_EXT_FUNC_PTRS(krb5_kdc_dh_key_info); +DEF_EXT_FUNC_PTRS(krb5_pa_pk_as_rep); +DEF_EXT_FUNC_PTRS(krb5_pa_pk_as_rep_draft9); +DEF_EXT_FUNC_PTRS(krb5_pa_pk_as_req); +DEF_EXT_FUNC_PTRS(krb5_pa_pk_as_req_draft9); +DEF_EXT_FUNC_PTRS(krb5_reply_key_pack); +DEF_EXT_FUNC_PTRS(krb5_reply_key_pack_draft9); +DEF_EXT_FUNC_PTRS_ARRAY(krb5_typed_data); + +/* special cases... */ +extern krb5_error_code (*k5int_decode_krb5_principal_name) + (const krb5_data *, krb5_principal_data **); + +extern krb5_error_code (*k5int_encode_krb5_td_dh_parameters) + (const krb5_algorithm_identifier **, krb5_data **code); +extern krb5_error_code (*k5int_decode_krb5_td_dh_parameters) + (const krb5_data *, krb5_algorithm_identifier ***); + +extern krb5_error_code (*k5int_encode_krb5_td_trusted_certifiers) + (const krb5_external_principal_identifier **, krb5_data **code); +extern krb5_error_code (*k5int_decode_krb5_td_trusted_certifiers) + (const krb5_data *, krb5_external_principal_identifier ***); + +#endif /* _PKINIT_ACCESSOR_H */ diff --git a/src/plugins/preauth/pkinit/pkinit_clnt.c b/src/plugins/preauth/pkinit/pkinit_clnt.c index 77ea3b7..b3233b1 100644 --- a/src/plugins/preauth/pkinit/pkinit_clnt.c +++ b/src/plugins/preauth/pkinit/pkinit_clnt.c @@ -35,6 +35,7 @@ #include <ctype.h> #include <assert.h> #include <dlfcn.h> +#include <sys/stat.h> #include <openssl/x509.h> #include <openssl/x509v3.h> @@ -63,15 +64,12 @@ #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 +#define PKCS11_MODNAME "opensc-pkcs11.so" +#define PK_SIGLEN_GUESS 1000 krb5_error_code pkinit_client_process (krb5_context context, void *plugin_context, void *request_context, + krb5_get_init_creds_opt *opt, preauth_get_client_data_proc get_data_proc, struct _krb5_preauth_client_rock *rock, krb5_kdc_req * request, krb5_data *encoded_request_body, @@ -83,6 +81,7 @@ krb5_error_code pkinit_client_process krb5_error_code pkinit_client_tryagain (krb5_context context, void *plugin_context, void *request_context, + krb5_get_init_creds_opt *opt, preauth_get_client_data_proc get_data_proc, struct _krb5_preauth_client_rock *rock, krb5_kdc_req * request, krb5_data *encoded_request_body, @@ -120,10 +119,11 @@ krb5_error_code pa_pkinit_parse_rep krb5_pa_data * in_padata, krb5_pa_data ** out_padata, krb5_enctype etype, krb5_keyblock * as_key, krb5_data *); -static int using_pkcs11(void); +krb5_error_code pkinit_find_private_key + (pkinit_req_context *, CK_ATTRIBUTE_TYPE usage, CK_OBJECT_HANDLE *objp); krb5_error_code pkinit_get_client_cert - (const char *principal, char *filename, X509 ** client_cert); + (pkinit_req_context *, const char *, char *, X509 **); krb5_error_code create_issuerAndSerial (X509 *cert, unsigned char **out, int *out_len); @@ -144,10 +144,8 @@ static krb5_error_code create_krb5_trustedCas (STACK_OF(X509) *, int flag, krb5_trusted_ca ***); #ifndef WITHOUT_PKCS11 -static krb5_error_code pkinit_open_session - (krb5_context, krb5_prompter_fct, void *); - -static void pkinit_close_session(void); +static krb5_error_code pkinit_login(pkinit_req_context *reqctx, CK_TOKEN_INFO *tip); +static krb5_error_code pkinit_open_session(pkinit_req_context *); #endif static void init_krb5_subject_pk_info(krb5_subject_pk_info **in); @@ -188,6 +186,8 @@ 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; + reqctx->prompter = prompter; + reqctx->prompter_data = prompter_data; /* If we don't have a client cert, we're done */ if (request->client == NULL) { @@ -198,21 +198,14 @@ pa_pkinit_gen_req(krb5_context context, if (retval) goto cleanup; - if (using_pkcs11()) { -#ifndef WITHOUT_PKCS11 - if (pkinit_open_session(context, prompter, prompter_data)) { - pkiDebug("can't open pkcs11 session\n"); - return KRB5KDC_ERR_PREAUTH_FAILED; - } -#endif - } else { + if (!reqctx->pkcs11_method) { if (get_filename(&filename, "X509_USER_CERT", 0) != 0) { pkiDebug("failed to get user's cert\n"); return KRB5KDC_ERR_PREAUTH_FAILED; } } - retval = pkinit_get_client_cert(client_principal, filename, &client_cert); + retval = pkinit_get_client_cert(reqctx, client_principal, filename, &client_cert); if (filename != NULL) free(filename); free(client_principal); @@ -243,6 +236,10 @@ pa_pkinit_gen_req(krb5_context context, der_req, &cksum); if (retval) goto cleanup; +#ifdef DEBUG_CKSUM + pkiDebug("calculating checksum on buf size (%d)\n", der_req->length); + print_buffer(der_req->data, der_req->length); +#endif retval = krb5_us_timeofday(context, &ctsec, &cusec); if (retval) @@ -331,7 +328,7 @@ pkinit_as_req_create(krb5_context context, krb5_pa_pk_as_req *req = NULL; krb5_auth_pack_draft9 *auth_pack9 = NULL; krb5_pa_pk_as_req_draft9 *req9 = NULL; - int protocol = DH_PROTOCOL; + int protocol = reqctx->dh_or_rsa; char *filename = NULL; #ifdef KDC_CERT X509 *kdc_cert = NULL; @@ -415,10 +412,10 @@ pkinit_as_req_create(krb5_context context, /* Encode the authpack */ switch((int)pa_type) { case KRB5_PADATA_PK_AS_REQ: - retval = encode_krb5_auth_pack(auth_pack, &coded_auth_pack); + retval = k5int_encode_krb5_auth_pack(auth_pack, &coded_auth_pack); break; case KRB5_PADATA_PK_AS_REQ_OLD: - retval = encode_krb5_auth_pack_draft9(auth_pack9, &coded_auth_pack); + retval = k5int_encode_krb5_auth_pack_draft9(auth_pack9, &coded_auth_pack); break; } if (retval) { @@ -429,7 +426,7 @@ pkinit_as_req_create(krb5_context context, print_buffer_bin(coded_auth_pack->data, coded_auth_pack->length, "/tmp/client_auth_pack"); #endif - if (!using_pkcs11() && get_filename(&filename, "X509_USER_KEY", 0) != 0) { + if (!reqctx->pkcs11_method && get_filename(&filename, "X509_USER_KEY", 0) != 0) { pkiDebug("failed to get user key filename\n"); retval = -1; goto cleanup; @@ -446,7 +443,7 @@ pkinit_as_req_create(krb5_context context, retval = pkcs7_signeddata_create(coded_auth_pack->data, coded_auth_pack->length, &req->signedAuthPack.data, &req->signedAuthPack.length, cert, filename, - plgctx->id_pkinit_authData, context); + plgctx->id_pkinit_authData, context, reqctx); break; case KRB5_PADATA_PK_AS_REQ_OLD: init_krb5_pa_pk_as_req_draft9(&req9); @@ -457,7 +454,7 @@ pkinit_as_req_create(krb5_context context, retval = pkcs7_signeddata_create(coded_auth_pack->data, coded_auth_pack->length, &req9->signedAuthPack.data, &req9->signedAuthPack.length, cert, filename, - plgctx->id_pkinit_authData9, context); + plgctx->id_pkinit_authData9, context, reqctx); break; } krb5_free_data(context, coded_auth_pack); @@ -491,7 +488,7 @@ pkinit_as_req_create(krb5_context context, goto cleanup; #endif /* Encode the as-req */ - retval = encode_krb5_pa_pk_as_req(req, as_req); + retval = k5int_encode_krb5_pa_pk_as_req(req, as_req); break; case KRB5_PADATA_PK_AS_REQ_OLD: if (trusted_CAs) { @@ -506,11 +503,12 @@ pkinit_as_req_create(krb5_context context, goto cleanup; #endif /* Encode the as-req */ - retval = encode_krb5_pa_pk_as_req_draft9(req9, as_req); + retval = k5int_encode_krb5_pa_pk_as_req_draft9(req9, as_req); break; } #ifdef DEBUG_ASN1 - print_buffer_bin((*as_req)->data, (*as_req)->length, "/tmp/client_as_req"); + if (!retval) + print_buffer_bin((*as_req)->data, (*as_req)->length, "/tmp/client_as_req"); #endif cleanup: @@ -649,12 +647,12 @@ pa_pkinit_parse_rep(krb5_context context, if (retval) return retval; - if (!using_pkcs11() && get_filename(&filename, "X509_USER_CERT", 0) != 0) { + if (!reqctx->pkcs11_method && get_filename(&filename, "X509_USER_CERT", 0) != 0) { pkiDebug("failed to get user's cert\n"); retval = -1; goto cleanup; } - retval = pkinit_get_client_cert(princ_name, filename, &client_cert); + retval = pkinit_get_client_cert(reqctx, princ_name, filename, &client_cert); if (filename != NULL) free(filename); if (retval) { @@ -714,7 +712,6 @@ pkinit_as_rep_parse(krb5_context context, long der_len = 0; char *filename = NULL; krb5_checksum cksum = {0, 0, 0, NULL}; - int i = 0; krb5_principal tmp_server; assert((as_rep != NULL) && (key_block != NULL)); @@ -723,7 +720,7 @@ pkinit_as_rep_parse(krb5_context context, print_buffer_bin(as_rep->data, as_rep->length, "/tmp/client_as_rep"); #endif - if ((retval = decode_krb5_pa_pk_as_rep(as_rep, &kdc_reply))) { + if ((retval = k5int_decode_krb5_pa_pk_as_rep(as_rep, &kdc_reply))) { pkiDebug("decode_krb5_as_rep failed %d\n", retval); return retval; } @@ -731,6 +728,9 @@ pkinit_as_rep_parse(krb5_context context, switch(kdc_reply->choice) { case choice_pa_pk_as_rep_dhInfo: pkiDebug("as_rep: DH key transport algorithm\n"); +#ifdef DEBUG_ASN1 + print_buffer_bin(kdc_reply->u.dh_Info.dhSignedData.data, kdc_reply->u.dh_Info.dhSignedData.length, "/tmp/client_kdc_signeddata"); +#endif if ((retval = pkcs7_signeddata_verify( kdc_reply->u.dh_Info.dhSignedData.data, kdc_reply->u.dh_Info.dhSignedData.length, @@ -743,7 +743,7 @@ pkinit_as_rep_parse(krb5_context context, break; case choice_pa_pk_as_rep_encKeyPack: pkiDebug("as_rep: RSA key transport algorithm\n"); - if (!using_pkcs11() && get_filename(&filename, "X509_USER_KEY", 0) != 0) { + if (!reqctx->pkcs11_method && get_filename(&filename, "X509_USER_KEY", 0) != 0) { pkiDebug("failed to get client's key filename\n"); goto cleanup; } @@ -751,7 +751,7 @@ pkinit_as_rep_parse(krb5_context context, kdc_reply->u.encKeyPack.data, kdc_reply->u.encKeyPack.length, &dh_data.data, &dh_data.length, client_cert, filename, - pa_type, plgctx, &kdc_cert, context)) != 0) { + pa_type, &kdc_cert, reqctx)) != 0) { pkiDebug("failed to verify pkcs7 enveloped data\n"); goto cleanup; } @@ -806,7 +806,7 @@ pkinit_as_rep_parse(krb5_context context, #ifdef DEBUG_ASN1 print_buffer_bin(dh_data.data, dh_data.length, "/tmp/client_dh_key"); #endif - if ((retval = decode_krb5_kdc_dh_key_info(&dh_data, + if ((retval = k5int_decode_krb5_kdc_dh_key_info(&dh_data, &kdc_dh)) != 0) { pkiDebug("failed to decode kdc_dh_key_info\n"); goto cleanup; @@ -841,14 +841,14 @@ pkinit_as_rep_parse(krb5_context context, #ifdef DEBUG_ASN1 print_buffer_bin(dh_data.data, dh_data.length, "/tmp/client_key_pack"); #endif - if ((retval = decode_krb5_reply_key_pack(&dh_data, + if ((retval = k5int_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, + k5int_decode_krb5_reply_key_pack_draft9(&dh_data, &key_pack9)) != 0) { pkiDebug("failed to decode reply_key_pack_draft9\n"); goto cleanup; @@ -887,10 +887,10 @@ pkinit_as_rep_parse(krb5_context context, 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); + 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 @@ -982,6 +982,7 @@ krb5_error_code pkinit_client_process(krb5_context context, void *plugin_context, void *request_context, + krb5_get_init_creds_opt *opt, preauth_get_client_data_proc get_data_proc, struct _krb5_preauth_client_rock *rock, krb5_kdc_req *request, @@ -1068,7 +1069,7 @@ pkinit_decode_td_dh_params(krb5_data *data, krb5_algorithm_identifier **algId = NULL; int i = 0, ok = 0, free_dh = 1; - retval = decode_krb5_td_dh_parameters(data, &algId); + retval = k5int_decode_krb5_td_dh_parameters(data, &algId); if (retval) { pkiDebug("decode_krb5_td_dh_parameters failed\n"); goto cleanup; @@ -1076,7 +1077,7 @@ pkinit_decode_td_dh_params(krb5_data *data, while (algId[i] != NULL) { DH *dh = NULL; unsigned char *tmp = NULL; - int dh_prime_bits = 0, dh_err = 0; + int dh_prime_bits = 0; if (algId[i]->algorithm.length != dh_oid.length || memcmp(algId[i]->algorithm.data, dh_oid.data, dh_oid.length)) @@ -1143,6 +1144,7 @@ krb5_error_code pkinit_client_tryagain(krb5_context context, void *plugin_context, void *request_context, + krb5_get_init_creds_opt *opt, preauth_get_client_data_proc get_data_proc, struct _krb5_preauth_client_rock *rock, krb5_kdc_req *request, @@ -1171,7 +1173,6 @@ pkinit_client_tryagain(krb5_context context, 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) @@ -1181,7 +1182,7 @@ pkinit_client_tryagain(krb5_context context, #ifdef DEBUG_ASN1 print_buffer_bin(err_reply->e_data.data, err_reply->e_data.length, "/tmp/client_edata"); #endif - retval = decode_krb5_typed_data(&err_reply->e_data, &typed_data); + retval = k5int_decode_krb5_typed_data(&err_reply->e_data, &typed_data); if (retval) { pkiDebug("decode_krb5_typed_data failed\n"); goto cleanup; @@ -1201,7 +1202,7 @@ pkinit_client_tryagain(krb5_context context, pkiDebug("trusted certifiers\n"); else pkiDebug("invalid certificate\n"); - retval = decode_krb5_td_trusted_certifiers(&scratch, &krb5_trusted_certifiers); + retval = k5int_decode_krb5_td_trusted_certifiers(&scratch, &krb5_trusted_certifiers); if (retval) { pkiDebug("failed to decode sequence of trusted certifiers\n"); goto cleanup; @@ -1294,6 +1295,7 @@ cleanup: } #define PKINIT_REQ_CTX_MAGIC 0xdeadbeef + void pkinit_client_req_init(krb5_context context, void *plugin_context, @@ -1308,15 +1310,28 @@ pkinit_client_req_init(krb5_context context, reqctx = (pkinit_req_context *) malloc(sizeof(*reqctx)); if (reqctx == NULL) return; + memset(reqctx, 0, sizeof(*reqctx)); reqctx->magic = PKINIT_REQ_CTX_MAGIC; + reqctx->plugctx = plugin_context; reqctx->dh = NULL; reqctx->dh_size = 1024; reqctx->require_eku = plgctx->require_eku; reqctx->require_san = plgctx->require_san; + reqctx->dh_or_rsa = plgctx->dh_or_rsa; reqctx->require_hostname_match = 0; reqctx->allow_upn = plgctx->allow_upn; reqctx->require_crl_checking = plgctx->require_crl_checking; +#ifndef WITHOUT_PKCS11 + reqctx->p11_module_name = strdup(PKCS11_MODNAME); + reqctx->p11_module = NULL; + reqctx->slotid = 0; + reqctx->session = CK_INVALID_HANDLE; + reqctx->p11 = NULL; + reqctx->pkcs11_method = (getenv("PKCS11") != NULL); +#else + reqctx->pkcs11_method = 0; +#endif *request_context = (void *) reqctx; pkiDebug("%s: returning reqctx at %p\n", __FUNCTION__, reqctx); @@ -1331,43 +1346,39 @@ pkinit_client_req_fini(krb5_context context, pkinit_req_context *reqctx = (pkinit_req_context *)request_context; pkiDebug("%s: received reqctx at %p\n", __FUNCTION__, reqctx); -#ifndef WITHOUT_PKCS11 - pkinit_close_session(); -#endif if (reqctx != NULL) { if (reqctx->magic != PKINIT_REQ_CTX_MAGIC) { pkiDebug("%s: Bad magic value (%x) in req ctx\n", - __FUNCTION__, reqctx->magic); + __FUNCTION__, reqctx->magic); return; } if (reqctx->dh != NULL) { DH_free(reqctx->dh); } +#ifndef WITHOUT_PKCS11 + if (reqctx->p11) { + if (reqctx->session) { + reqctx->p11->C_CloseSession(reqctx->session); + reqctx->session = CK_INVALID_HANDLE; + } + reqctx->p11->C_Finalize(NULL_PTR); + reqctx->p11 = NULL; + } + if (reqctx->p11_module) { + C_UnloadModule(reqctx->p11_module); + reqctx->p11_module = NULL; + } + if (reqctx->p11_module_name) + free(reqctx->p11_module_name); + if (reqctx->cert_id) + free(reqctx->cert_id); +#endif free(reqctx); } return; } -static int -using_pkcs11(void) -{ -#ifdef WITHOUT_PKCS11 - return 0; -#else - return (getenv("PKCS11") != NULL); -#endif -} - #ifndef WITHOUT_PKCS11 - -static char *module_name = "opensc-pkcs11.so"; -static void *module = NULL; -static 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) { @@ -1376,6 +1387,10 @@ C_LoadModule(const char *modname, CK_FUNCTION_LIST_PTR_PTR p11p) pkiDebug("loading module \"%s\"... ", modname); handle = dlopen(modname, RTLD_NOW); + if (handle == NULL) { + pkiDebug("failed\n"); + return NULL; + } getflist = (CK_RV (*)(CK_FUNCTION_LIST_PTR_PTR)) dlsym(handle, "C_GetFunctionList"); if (getflist == NULL || (*getflist)(p11p) != CKR_OK) { dlclose(handle); @@ -1394,105 +1409,138 @@ C_UnloadModule(void *handle) } static krb5_error_code -pkinit_open_session(krb5_context context, - krb5_prompter_fct prompter, - void *prompter_data) +pkinit_login(pkinit_req_context *reqctx, CK_TOKEN_INFO *tip) { - int r; - char *s; - char pin[32]; - char custom_module_name[100]; - krb5_data response_data; + krb5_data rdat; krb5_prompt kprompt; krb5_prompt_type prompt_type; + krb5_context ctx = reqctx->plugctx->context; + int r = 0; - if (module != NULL) - return 0; + if (tip->flags & CKF_PROTECTED_AUTHENTICATION_PATH) { + rdat.data = NULL; + rdat.length = 0; + } else { + rdat.data = malloc(tip->ulMaxPinLen + 2); + rdat.length = tip->ulMaxPinLen + 1; - /* Temporary pending use of krb5.conf */ - if ((s = getenv("PKCS11")) != NULL && strlen(s) > 0) { - if (sscanf(s, "%99[^:]:%d", custom_module_name, &slotid) < 2) - slotid = atoi(s); - else - module_name = custom_module_name; - } + kprompt.prompt = "PIN"; + kprompt.hidden = 1; + kprompt.reply = &rdat; + prompt_type = KRB5_PROMPT_TYPE_PREAUTH; - /* Load module, init, and open session */ - module = C_LoadModule(module_name, &p11); - if (module == NULL) { - pkiDebug("fail C_LoadModule\n"); - return KRB5KDC_ERR_PREAUTH_FAILED; + /* PROMPTER_INVOCATION */ + krb5int_set_prompt_types(ctx, &prompt_type); + r = (*reqctx->prompter)(ctx, reqctx->prompter_data, NULL, NULL, 1, &kprompt); + krb5int_set_prompt_types(ctx, 0); } - if ((r = p11->C_Initialize(NULL)) != CKR_OK) { - pkiDebug("fail C_Initialize %x\n", r); - return KRB5KDC_ERR_PREAUTH_FAILED; + + if (r == 0) { + r = reqctx->p11->C_Login(reqctx->session, CKU_USER, (u_char *) rdat.data, + rdat.length); + if (r != CKR_OK) { + pkiDebug("fail C_Login %x\n", r); + r = KRB5KDC_ERR_PREAUTH_FAILED; + } } - pkiDebug("opening slot %d\n", slotid); - if ((r = p11->C_OpenSession(slotid, CKF_SERIAL_SESSION, NULL, NULL, &session)) != - CKR_OK) { - pkiDebug("fail C_OpenSession %x\n", r); - return KRB5KDC_ERR_PREAUTH_FAILED; + if (rdat.data) + free(rdat.data); + return r; +} + +static krb5_error_code +pkinit_open_session(pkinit_req_context *reqctx) +{ + char *s, *cp, *ep; + int r, i, gotslot = 0; + CK_ULONG count = 0, id; + CK_SLOT_ID_PTR slotlist; + CK_TOKEN_INFO tinfo; + + if (reqctx->p11_module != NULL) + return 0; /* session already open */ + + /* Temporary pending use of command line options and krb5.conf */ + if ((s = getenv("PKCS11")) != NULL && (i = strlen(s)) > 0) { + id = strtol(s, &ep, 10); + if (*ep == '\0') { + /* got just a slotid */ + reqctx->slotid = id; + gotslot = 1; + } else if (strchr(s, ':')) { + /* got a module name and slotid */ + cp = malloc(i); + sscanf(s, "%[^:]:%d", cp, &reqctx->slotid); + free(reqctx->p11_module_name); + reqctx->p11_module_name = cp; + gotslot = 1; + } else { + /* got just a module name */ + free(reqctx->p11_module_name); + reqctx->p11_module_name = strdup(s); + } } - response_data.data = pin; - response_data.length = sizeof(pin); + /* Load module */ + reqctx->p11_module = C_LoadModule(reqctx->p11_module_name, &reqctx->p11); + if (reqctx->p11_module == NULL) + return KRB5KDC_ERR_PREAUTH_FAILED; - kprompt.prompt = "PIN"; - kprompt.hidden = 1; - kprompt.reply = &response_data; - prompt_type = KRB5_PROMPT_TYPE_PREAUTH; + /* Init */ + if ((r = reqctx->p11->C_Initialize(NULL)) != CKR_OK) { + pkiDebug("fail C_Initialize %x\n", r); + return KRB5KDC_ERR_PREAUTH_FAILED; + } - /* PROMPTER_INVOCATION */ - krb5int_set_prompt_types(context, &prompt_type); - if ((r = ((*prompter)(context, prompter_data, NULL, NULL, 1, &kprompt)))) { - krb5int_set_prompt_types(context, 0); - return r; + /* Decide which slot to use */ + if (!gotslot) { + if (reqctx->p11->C_GetSlotList(TRUE, NULL, &count) != CKR_OK) + return KRB5KDC_ERR_PREAUTH_FAILED; + slotlist = (CK_SLOT_ID_PTR) malloc(count * sizeof (CK_SLOT_ID)); + if (reqctx->p11->C_GetSlotList(TRUE, slotlist, &count) != CKR_OK) + return KRB5KDC_ERR_PREAUTH_FAILED; + /* take the first one for now */ + reqctx->slotid = slotlist[0]; + free(slotlist); } - krb5int_set_prompt_types(context, 0); - if ((r = p11->C_Login(session, CKU_USER, (u_char *) pin, response_data.length)) != CKR_OK) { - pkiDebug("fail C_Login %x\n", r); + /* Open session */ + pkiDebug("init and open slotid %d (1 of %d)\n", reqctx->slotid, (int) count); + if ((r = reqctx->p11->C_OpenSession(reqctx->slotid, CKF_SERIAL_SESSION, NULL, NULL, + &reqctx->session)) != CKR_OK) { + pkiDebug("fail C_OpenSession %x\n", r); return KRB5KDC_ERR_PREAUTH_FAILED; } - return 0; -} + /* Login if needed */ + r = reqctx->p11->C_GetTokenInfo(reqctx->slotid, &tinfo); + if (r == CKR_OK && (tinfo.flags & CKF_LOGIN_REQUIRED)) + r = pkinit_login(reqctx, &tinfo); -static void -pkinit_close_session(void) -{ - if (p11) { - if (session) { - p11->C_CloseSession(session); - session = CK_INVALID_HANDLE; - } - p11->C_Finalize(NULL_PTR); - p11 = NULL; - } - if (module) { - C_UnloadModule(module); - module = NULL; - } + return r; } #endif krb5_error_code -pkinit_get_client_cert(const char *principal, +pkinit_get_client_cert(pkinit_req_context *reqctx, + const char *principal, char *filename, X509 ** client_cert) { #ifndef WITHOUT_PKCS11 + CK_MECHANISM_TYPE_PTR mechp; + CK_MECHANISM_INFO info; CK_OBJECT_CLASS cls; - CK_OBJECT_HANDLE objs[MAX_N_OBJS]; + CK_OBJECT_HANDLE obj; CK_ATTRIBUTE attrs[2]; CK_ULONG count; CK_CERTIFICATE_TYPE certtype; - krb5_octet cert[MAX_CERT_SIZE]; + CK_BYTE_PTR cert, cert_id; const unsigned char *cp; - int r; + int i, r; #endif - if (!using_pkcs11()) { + if (!reqctx->pkcs11_method) { if ((*client_cert = get_cert(filename)) == NULL) return KRB5KDC_ERR_PREAUTH_FAILED; else @@ -1504,7 +1552,37 @@ pkinit_get_client_cert(const char *principal, return KRB5_PRINC_NOMATCH; } - pkiDebug("Reading cert for '%s' from card\n", principal); + if (pkinit_open_session(reqctx)) { + pkiDebug("can't open pkcs11 session\n"); + return KRB5KDC_ERR_PREAUTH_FAILED; + } + + if ((r = reqctx->p11->C_GetMechanismList(reqctx->slotid, NULL, &count)) != CKR_OK + || count <= 0) { + pkiDebug("can't find any mechanisms %x\n", r); + return KRB5KDC_ERR_PREAUTH_FAILED; + } + mechp = (CK_MECHANISM_TYPE_PTR) malloc(count * sizeof (CK_MECHANISM_TYPE)); + if (mechp == NULL) + return ENOMEM; + if ((r = reqctx->p11->C_GetMechanismList(reqctx->slotid, mechp, &count)) != CKR_OK) + return KRB5KDC_ERR_PREAUTH_FAILED; + for (i = 0; i < count; i++) { + if ((r = reqctx->p11->C_GetMechanismInfo(reqctx->slotid, mechp[i], &info)) != CKR_OK) + return KRB5KDC_ERR_PREAUTH_FAILED; +#ifdef DEBUG_MECHINFO + pkiDebug("mech %x flags %x\n", (int) mechp[i], (int) info.flags); + if ((info.flags & (CKF_SIGN|CKF_DECRYPT)) == (CKF_SIGN|CKF_DECRYPT)) + pkiDebug(" this mech is good for sign & decrypt\n"); +#endif + if (mechp[i] == CKM_RSA_PKCS) { + /* This seems backwards... */ + reqctx->mech = (info.flags & CKF_SIGN) ? CKM_SHA1_RSA_PKCS : CKM_RSA_PKCS; + } + } + free(mechp); + + pkiDebug("got %d mechs; reading certs for '%s' from card\n", (int) count, principal); cls = CKO_CERTIFICATE; attrs[0].type = CKA_CLASS; @@ -1516,42 +1594,63 @@ pkinit_get_client_cert(const char *principal, attrs[1].pValue = &certtype; attrs[1].ulValueLen = sizeof certtype; - if (p11->C_FindObjectsInit(session, attrs, 2) != CKR_OK) { + if (reqctx->p11->C_FindObjectsInit(reqctx->session, attrs, 2) != CKR_OK) { pkiDebug("fail C_FindObjectsInit\n"); return KRB5KDC_ERR_PREAUTH_FAILED; } - /* Look for x.509 cert */ - if ((r = p11->C_FindObjects(session, 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); + for (i = 0; ; i++) { + /* Look for x.509 cert */ + if ((r = reqctx->p11->C_FindObjects(reqctx->session, &obj, 1, &count)) != CKR_OK + || count <= 0) { + break; + } - /* - * 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; + /* Get cert and id len */ + attrs[0].type = CKA_VALUE; + attrs[0].pValue = NULL; + attrs[0].ulValueLen = 0; - attrs[1].type = CKA_ID; - attrs[1].pValue = cert_id; - attrs[1].ulValueLen = sizeof cert_id; + attrs[1].type = CKA_ID; + attrs[1].pValue = NULL; + attrs[1].ulValueLen = 0; - /* 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; - } + if ((r = reqctx->p11->C_GetAttributeValue(reqctx->session, obj, attrs, 2)) != CKR_OK + && r != CKR_BUFFER_TOO_SMALL) { + pkiDebug("fail C_GetAttributeValue len %x\n", r); + return KRB5KDC_ERR_PREAUTH_FAILED; + } + cert = (CK_BYTE_PTR) malloc((size_t) attrs[0].ulValueLen); + cert_id = (CK_BYTE_PTR) malloc((size_t) attrs[1].ulValueLen); + if (cert == NULL || cert_id == NULL) + return ENOMEM; + + /* Read the cert and id off the card */ + + attrs[0].type = CKA_VALUE; + attrs[0].pValue = cert; + + attrs[1].type = CKA_ID; + attrs[1].pValue = cert_id; - 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 ((r = reqctx->p11->C_GetAttributeValue(reqctx->session, obj, attrs, 2)) != CKR_OK) { + pkiDebug("fail C_GetAttributeValue %x\n", r); + return KRB5KDC_ERR_PREAUTH_FAILED; + } + + pkiDebug("cert %d size %d id %d idlen %d\n", + i, (int) attrs[0].ulValueLen, (int) cert_id[0], (int) attrs[1].ulValueLen); + /* Just take the first one */ + if (i == 0) { + reqctx->cert_id = cert_id; + reqctx->cert_id_len = attrs[1].ulValueLen; + cp = (unsigned char *) cert; + *client_cert = d2i_X509(NULL, &cp, (int) attrs[0].ulValueLen); + } else + free(cert_id); + free(cert); + } + reqctx->p11->C_FindObjectsFinal(reqctx->session); if (*client_cert == NULL) return KRB5KDC_ERR_PREAUTH_FAILED; #endif @@ -1559,28 +1658,36 @@ pkinit_get_client_cert(const char *principal, } #ifndef WITHOUT_PKCS11 + +/* + * Look for a key that's: + * 1. private + * 2. capable of the specified operation (usually signing or decrypting) + * 3. RSA (this may be wrong but it's all we can do for now) + * 4. matches the id of the cert we chose + * + * You must call pkinit_get_client_cert before calling pkinit_find_private_key + * (that's because we need the ID of the private key) + * + * 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. + * + * We should only find one key that fits all the requirements. + * If there are more than one, we just take the first one. + */ + krb5_error_code -pkinit_find_private_key(CK_ATTRIBUTE_TYPE usage, CK_OBJECT_HANDLE *objp) +pkinit_find_private_key(pkinit_req_context *reqctx, + CK_ATTRIBUTE_TYPE usage, + CK_OBJECT_HANDLE *objp) { CK_OBJECT_CLASS cls; - CK_OBJECT_HANDLE objs[MAX_N_OBJS]; CK_ATTRIBUTE attrs[4]; CK_ULONG count; CK_BBOOL bool; CK_KEY_TYPE keytype; int r; - /* - * Look for a key that's: - * 1. private - * 2. capable of the specified operation (usually signing or decrypting) - * 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; @@ -1597,26 +1704,26 @@ pkinit_find_private_key(CK_ATTRIBUTE_TYPE usage, CK_OBJECT_HANDLE *objp) attrs[2].ulValueLen = sizeof keytype; attrs[3].type = CKA_ID; - attrs[3].pValue = cert_id; - attrs[3].ulValueLen = cert_id_len; + attrs[3].pValue = reqctx->cert_id; + attrs[3].ulValueLen = reqctx->cert_id_len; - if (p11->C_FindObjectsInit(session, attrs, 4) != CKR_OK) { + if (reqctx->p11->C_FindObjectsInit(reqctx->session, attrs, 4) != CKR_OK) { pkiDebug("krb5_pkinit_sign_data: fail C_FindObjectsInit\n"); return KRB5KDC_ERR_PREAUTH_FAILED; } - r = p11->C_FindObjects(session, objs, 6, &count); - p11->C_FindObjectsFinal(session); + r = reqctx->p11->C_FindObjects(reqctx->session, objp, 1, &count); + reqctx->p11->C_FindObjectsFinal(reqctx->session); pkiDebug("found %d private keys %x\n", (int) count, (int) r); if (r != CKR_OK || count < 1) return KRB5KDC_ERR_PREAUTH_FAILED; - *objp = objs[0]; return 0; } #endif krb5_error_code -pkinit_decode_data(unsigned char *data, +pkinit_decode_data(pkinit_req_context *reqctx, + unsigned char *data, int data_len, unsigned char **decoded_data, int *decoded_data_len, @@ -1627,11 +1734,11 @@ pkinit_decode_data(unsigned char *data, CK_OBJECT_HANDLE obj; CK_ULONG len; CK_MECHANISM mech; - unsigned char txtbuf[4096], *cp; + unsigned char *cp; int r; #endif - if (!using_pkcs11()) { + if (!reqctx->pkcs11_method) { if (decode_data(decoded_data, decoded_data_len, data, data_len, filename, cert) <= 0) { pkiDebug("failed to decode data\n"); @@ -1641,51 +1748,55 @@ pkinit_decode_data(unsigned char *data, } #ifndef WITHOUT_PKCS11 - pkinit_find_private_key(CKA_DECRYPT, &obj); + if (pkinit_open_session(reqctx)) { + pkiDebug("can't open pkcs11 session\n"); + return KRB5KDC_ERR_PREAUTH_FAILED; + } + + pkinit_find_private_key(reqctx, CKA_DECRYPT, &obj); mech.mechanism = CKM_RSA_PKCS; mech.pParameter = NULL; mech.ulParameterLen = 0; - if ((r = p11->C_DecryptInit(session, &mech, obj)) != CKR_OK) { + if ((r = reqctx->p11->C_DecryptInit(reqctx->session, &mech, obj)) != CKR_OK) { pkiDebug("fail C_DecryptInit %x\n", (int) r); return KRB5KDC_ERR_PREAUTH_FAILED; } - - len = sizeof txtbuf; - pkiDebug("decrypt %d -> %d\n", (int) data_len, (int) len); - if ((r = p11->C_Decrypt(session, (krb5_octet *) data, (u_int) data_len, txtbuf, &len)) != + cp = malloc((size_t) data_len); + if (cp == NULL) + return ENOMEM; + len = data_len; + if ((r = reqctx->p11->C_Decrypt(reqctx->session, data, (CK_ULONG) data_len, cp, &len)) != CKR_OK) { pkiDebug("fail C_Decrypt %x\n", (int) r); return KRB5KDC_ERR_PREAUTH_FAILED; } - pkiDebug("txt size %d\n", (int) len); - cp = malloc(len); - if (cp == NULL) - return ENOMEM; - memcpy(cp, txtbuf, len); + pkiDebug("decrypt %d -> %d\n", (int) data_len, (int) len); *decoded_data_len = len; *decoded_data = cp; - return 0; #endif + + return 0; } krb5_error_code -pkinit_sign_data(unsigned char *data, - int data_len, - unsigned char **sig, - int *sig_len, - char *filename) +pkinit_sign_data(pkinit_req_context *reqctx, + unsigned char *data, + int data_len, + unsigned char **sig, + int *sig_len, + char *filename) { #ifndef WITHOUT_PKCS11 CK_OBJECT_HANDLE obj; CK_ULONG len; CK_MECHANISM mech; - unsigned char sigbuf[1024], *cp; + unsigned char *cp; int r; #endif - if (!using_pkcs11()) { + if (reqctx == NULL || !reqctx->pkcs11_method) { if (create_signature(sig, sig_len, data, data_len, filename) != 0) { pkiDebug("failed to create the signature\n"); return KRB5KDC_ERR_PREAUTH_FAILED; @@ -1694,33 +1805,48 @@ pkinit_sign_data(unsigned char *data, } #ifndef WITHOUT_PKCS11 - pkinit_find_private_key(CKA_SIGN, &obj); + if (pkinit_open_session(reqctx)) { + pkiDebug("can't open pkcs11 session\n"); + return KRB5KDC_ERR_PREAUTH_FAILED; + } + + pkinit_find_private_key(reqctx, CKA_SIGN, &obj); - mech.mechanism = CKM_SHA1_RSA_PKCS; + mech.mechanism = reqctx->mech; mech.pParameter = NULL; mech.ulParameterLen = 0; - if ((r = p11->C_SignInit(session, &mech, obj)) != CKR_OK) { + if ((r = reqctx->p11->C_SignInit(reqctx->session, &mech, obj)) != CKR_OK) { pkiDebug("fail C_SignInit %x\n", (int) r); return KRB5KDC_ERR_PREAUTH_FAILED; } - len = sizeof sigbuf; - pkiDebug("signing %d -> %d\n", (int) data_len, (int) len); - if ((r = p11->C_Sign(session, (krb5_octet *) data, (u_int) data_len, sigbuf, &len)) != - CKR_OK) { + /* + * Key len would give an upper bound on sig size, but there's no way to + * get that. So guess, and if it's too small, re-malloc. + */ + len = PK_SIGLEN_GUESS; + cp = malloc((size_t) len); + if (cp == NULL) + return ENOMEM; + + r = reqctx->p11->C_Sign(reqctx->session, data, (CK_ULONG) data_len, cp, &len); + if (r == CKR_BUFFER_TOO_SMALL || (r == CKR_OK && len >= PK_SIGLEN_GUESS)) { + free(cp); + pkiDebug("C_Sign realloc %d\n", (int) len); + cp = malloc((size_t) len); + r = reqctx->p11->C_Sign(reqctx->session, data, (CK_ULONG) data_len, cp, &len); + } + if (r != CKR_OK) { pkiDebug("fail C_Sign %x\n", (int) r); return KRB5KDC_ERR_PREAUTH_FAILED; } - pkiDebug("sig size %d\n", (int) len); - cp = malloc(len); - if (cp == NULL) - return ENOMEM; - memcpy(cp, sigbuf, len); + pkiDebug("sign %d -> %d\n", (int) data_len, (int) len); *sig_len = len; *sig = cp; - return 0; #endif + + return 0; } static krb5_error_code @@ -2090,6 +2216,162 @@ errout: return retval; } +static krb5_error_code +handle_gic_opt(krb5_context context, + pkinit_context *plgctx, + char *attr, + char *value) +{ + int i, code; + struct stat statbuf; + char *colon, *sep, *residual; +#define KS_FILE 1 +#define KS_PKCS11 2 + int keyset; + + /* + * XXX This is all just a hack right now... XXX + */ + /* + * Would like to call something like "pkinit_set_identity()" or + * pkinit_set_user_identity() here... + */ + if (strcmp(attr, "X509_user_identity") == 0) { + residual = strchr(value, ':'); + if (residual) { + int typelen; + residual++; /* skip past colon */ + typelen = residual - value; + if (strncmp(value, "FILE:", typelen) == 0) { + keyset = KS_FILE; + } else if (strncmp(value, "PKCS11:", typelen) == 0) { + keyset = KS_PKCS11; + } else { + krb5_set_error_message(context, KRB5_PREAUTH_FAILED, + "Unsupported key set type while processing '%s'\n", value); + return KRB5_PREAUTH_FAILED; + } + } else { + keyset = KS_FILE; + residual = value; + } + + switch (keyset) { + int certlen; + char certbuf[256]; + char keybuf[256]; + case KS_FILE: + sep = strchr(residual, ','); + if (sep) { + certlen = sep - residual; + strncpy(certbuf, residual, certlen); + certbuf[certlen] = '\0'; + strncpy(keybuf, ++sep, sizeof keybuf); + keybuf[sizeof(keybuf) - 1] = '\0'; + } else { + strncpy(certbuf, residual, sizeof certbuf); + certbuf[sizeof(certbuf) - 1] = '\0'; + strncpy(keybuf, residual, sizeof keybuf); + keybuf[sizeof(keybuf) - 1] = '\0'; + } + if ((code = stat(certbuf, &statbuf)) != 0) { + krb5_set_error_message(context, KRB5_PREAUTH_FAILED, + "Could not stat certificate file '%s'", certbuf); + return KRB5_PREAUTH_FAILED; + } + if ((code = stat(keybuf, &statbuf)) != 0) { + krb5_set_error_message(context, KRB5_PREAUTH_FAILED, + "Could not stat private key file '%s'", keybuf); + return KRB5_PREAUTH_FAILED; + } + pkiDebug("Setting X509_USER_CERT to '%s'\n", certbuf); + setenv("X509_USER_CERT", certbuf, 1); + pkiDebug("Setting X509_USER_KEY to '%s'\n", keybuf); + setenv("X509_USER_KEY", keybuf, 1); + break; + case KS_PKCS11: + if ((code = stat(residual, &statbuf)) != 0) { + krb5_set_error_message(context, KRB5_PREAUTH_FAILED, + "Could not stat pkcs11 library '%s'", residual); + return KRB5_PREAUTH_FAILED; + } + pkiDebug("Setting PKCS11 to '%s'\n", residual); + setenv("PKCS11", residual, 1); + break; + default: + krb5_set_error_message(context, KRB5_PREAUTH_FAILED, + "Internal error parsing X509_user_identity\n"); + return KRB5_PREAUTH_FAILED; + break; + } + } else if (strcmp(attr, "X509_anchors") == 0) { + /* Would like to call something like "pkinit_add_anchors() here */ + if ((code = stat(value, &statbuf)) != 0) { + krb5_set_error_message(context, KRB5_PREAUTH_FAILED, + "Could not stat X509_anchors directory '%s'", + value); + return KRB5_PREAUTH_FAILED; + } + pkiDebug("Setting X509_CA_DIR to '%s'\n", value); + setenv("X509_CA_DIR", value, 1); + } else if (strcmp(attr, "flag_RSA_PROTOCOL") == 0) { + if (strcmp(value, "yes") == 0) { + pkiDebug("Setting flag to use RSA_PROTOCOL\n"); + plgctx->dh_or_rsa = RSA_PROTOCOL; + } + } +#if 0 + if (strcmp(attr, "client_cert") == 0) { + if ((code = stat(value, &statbuf)) != 0) { + krb5_set_error_message(context, KRB5_PREAUTH_FAILED, + "Could not stat '%s' file '%s'", attr, value); + return KRB5_PREAUTH_FAILED; + } + pkiDebug("Setting X509_USER_CERT to '%s'\n", value); + setenv("X509_USER_CERT", value, 1); + } + if (strcmp(attr, "client_key") == 0) { + if ((code = stat(value, &statbuf)) != 0) { + krb5_set_error_message(context, KRB5_PREAUTH_FAILED, + "Could not stat '%s' file '%s'", attr, value); + return KRB5_PREAUTH_FAILED; + } + pkiDebug("Setting X509_USER_KEY to '%s'\n", value); + setenv("X509_USER_KEY", value, 1); + } + if (strcmp(attr, "client_ca_dir") == 0) { + if ((code = stat(value, &statbuf)) != 0) { + krb5_set_error_message(context, KRB5_PREAUTH_FAILED, + "Could not stat '%s' directory '%s'", + attr, value); + return KRB5_PREAUTH_FAILED; + } + pkiDebug("Setting X509_CA_DIR to '%s'\n", value); + setenv("X509_CA_DIR", value, 1); + } +#endif + return 0; +} + +static krb5_error_code +pkinit_client_gic_opt(krb5_context context, + void *plugin_context, + krb5_get_init_creds_opt *opt, + const char *attr, + const char *value) +{ + int i; + krb5_error_code retval; + pkinit_context *plgctx = (pkinit_context *)plugin_context; + + pkiDebug("(pkinit) received '%s' = '%s'\n", attr, value); + retval = handle_gic_opt(context, plgctx, attr, value); + if (retval) + return retval; + + return 0; +} + static void pkinit_client_plugin_fini(krb5_context context, void *blob) { @@ -2110,4 +2392,5 @@ struct krb5plugin_preauth_client_ftable_v0 preauthentication_client_0 = { pkinit_client_req_fini, /* (*client_req_fini) */ pkinit_client_process, /* (*process) */ pkinit_client_tryagain, /* (*tryagain) */ + pkinit_client_gic_opt /* (*gic_opt) */ }; diff --git a/src/plugins/preauth/pkinit/pkinit_lib.c b/src/plugins/preauth/pkinit/pkinit_lib.c index 90a1eb3..f4d62bf 100644 --- a/src/plugins/preauth/pkinit/pkinit_lib.c +++ b/src/plugins/preauth/pkinit/pkinit_lib.c @@ -198,9 +198,10 @@ static int openssl_callback (int, X509_STORE_CTX *); static int openssl_callback_ignore_crls (int, X509_STORE_CTX *); static int pkcs7_decrypt - (PKCS7 *p7, X509 *cert, BIO *bio, char *filename); + (pkinit_req_context *reqctx, PKCS7 *p7, X509 *cert, BIO *bio, char *filename); -static BIO * pkcs7_dataDecode(PKCS7 *p7, X509 *pcert, char *filename); +static BIO * pkcs7_dataDecode + (pkinit_req_context *reqctx,PKCS7 *p7, X509 *pcert, char *filename); /* This handy macro borrowed from crypto/x509v3/v3_purp.c */ #define ku_reject(x, usage) \ @@ -210,16 +211,23 @@ krb5_error_code pkinit_lib_init(krb5_context context, void **blob) { pkinit_context *plgctx; - krb5_error_code retval = ENOMEM; + krb5_error_code retval; int tmp = 0; + retval = pkinit_accessor_init(); + if (retval) + goto out; + + retval = ENOMEM; plgctx = (pkinit_context *) calloc(1, sizeof(*plgctx)); if (plgctx == NULL) goto out; plgctx->magic = PKINIT_CTX_MAGIC; + plgctx->context = context; plgctx->require_eku = 1; plgctx->require_san = 1; + plgctx->dh_or_rsa = DH_PROTOCOL; plgctx->allow_upn = 0; plgctx->require_crl_checking = 0; @@ -583,7 +591,8 @@ pkcs7_signeddata_create(unsigned char *data, X509 * cert, char *filename, ASN1_OBJECT *oid, - krb5_context context) + krb5_context context, + pkinit_req_context *reqctx) { krb5_error_code retval = ENOMEM; PKCS7 *p7 = NULL, *inner_p7 = NULL; @@ -593,13 +602,19 @@ pkcs7_signeddata_create(unsigned char *data, ASN1_TYPE *pkinit_data = NULL; STACK_OF(X509) * cert_stack = NULL; ASN1_OCTET_STRING *digest_attr = NULL; - EVP_MD_CTX ctx; + EVP_MD_CTX ctx, ctx2; const EVP_MD *md_tmp; - unsigned char md_data[EVP_MAX_MD_SIZE], *abuf = NULL; - unsigned int md_len, alen; + unsigned char md_data[EVP_MAX_MD_SIZE], md_data2[EVP_MAX_MD_SIZE]; + unsigned char *digestInfo_buf = NULL, *abuf = NULL; + unsigned int md_len, md_len2, alen, digestInfo_len; STACK_OF(X509_ATTRIBUTE) * sk; unsigned char *sig = NULL; int sig_len = 0; + char *dirname = NULL; + X509_ALGOR *alg = NULL; + ASN1_OCTET_STRING *digest = NULL; + int z = 0, alg_len = 0, digest_len = 0; + unsigned char *y = NULL, *alg_buf = NULL, *digest_buf = NULL; /* start creating PKCS7 data */ if ((p7 = PKCS7_new()) == NULL) @@ -612,11 +627,46 @@ pkcs7_signeddata_create(unsigned char *data, if (!ASN1_INTEGER_set(p7s->version, 1)) goto cleanup; - /* create a cert chain */ + /* create a cert chain that has at least the signer's certificate */ if ((cert_stack = sk_X509_new_null()) == NULL) goto cleanup; + + if (get_filename(&dirname, "X509_CA_DIR", 1) != 0) { + pkiDebug("only including signer's certificate\n"); + sk_X509_push(cert_stack, X509_dup(cert)); + } else { + /* create a cert chain */ + X509_STORE *certstore = NULL; + X509_STORE_CTX certctx; + STACK_OF(X509) *certstack = NULL; + char buf[256]; + int i = 0, size = 0; + + if ((certstore = X509_STORE_new()) == NULL) + goto cleanup; + if(!X509_STORE_load_locations(certstore, NULL, dirname)) + goto cleanup; + pkiDebug("building certificate chain from dir = %s\n", dirname); + X509_STORE_set_verify_cb_func(certstore, openssl_callback); + X509_STORE_CTX_init(&certctx, certstore, cert, NULL); + if (!X509_verify_cert(&certctx)) + goto cleanup; + certstack = X509_STORE_CTX_get1_chain(&certctx); + size = sk_X509_num(certstack); + pkiDebug("size of certificate chain = %d\n", size); + for(i = 0; i < size - 1; i++) { + X509 *x = sk_X509_value(certstack, i); + X509_NAME_oneline(X509_get_subject_name(x), buf, 256); + pkiDebug("cert #%d: %s\n", i, buf); + sk_X509_push(cert_stack, X509_dup(x)); + } + X509_STORE_CTX_cleanup(&certctx); + X509_STORE_free(certstore); + sk_X509_free(certstack); + } + if (dirname != NULL) + free(dirname); p7s->cert = cert_stack; - sk_X509_push(cert_stack, X509_dup(cert)); /* fill-in PKCS7_SIGNER_INFO */ if ((p7si = PKCS7_SIGNER_INFO_new()) == NULL) @@ -647,7 +697,7 @@ pkcs7_signeddata_create(unsigned char *data, /* Set sig algs */ if (p7si->digest_enc_alg->parameter != NULL) ASN1_TYPE_free(p7si->digest_enc_alg->parameter); - p7si->digest_enc_alg->algorithm = OBJ_nid2obj(NID_rsaEncryption); + p7si->digest_enc_alg->algorithm = OBJ_nid2obj(NID_sha1WithRSAEncryption); if (!(p7si->digest_enc_alg->parameter = ASN1_TYPE_new())) goto cleanup; p7si->digest_enc_alg->parameter->type = V_ASN1_NULL; @@ -675,12 +725,69 @@ pkcs7_signeddata_create(unsigned char *data, alen = ASN1_item_i2d((ASN1_VALUE *) sk, &abuf, ASN1_ITEM_rptr(PKCS7_ATTR_SIGN)); if (abuf == NULL) - goto cleanup; - - retval = pkinit_sign_data(abuf, alen, &sig, &sig_len, filename); + goto cleanup2; + + /* ActiveCard can only do RSAEncryption */ + /* to compute sha1WithRSAEncryption, encode the algorithm ID for the hash + * function and the hash value into an ASN.1 value of type DigestInfo + * DigestInfo::=SEQUENCE{ + * digestAlgorithm AlgorithmIdentifier, + * digest OCTET STRING } + */ + if (reqctx && reqctx->pkcs11_method && + reqctx->mech == CKM_RSA_PKCS) { + pkiDebug("mech = CKM_RSA_PKCS\n"); + EVP_MD_CTX_init(&ctx2); + EVP_DigestInit_ex(&ctx2, md_tmp, NULL); + EVP_DigestUpdate(&ctx2, abuf, alen); + EVP_DigestFinal_ex(&ctx2, md_data2, &md_len2); + + alg = X509_ALGOR_new(); + if (alg == NULL) + goto cleanup2; + alg->algorithm = OBJ_nid2obj(NID_sha1); + alg->parameter = NULL; + alg_len = i2d_X509_ALGOR(alg, NULL); + alg_buf = malloc(alg_buf); + if (alg_buf == NULL) + goto cleanup2; + + digest = ASN1_OCTET_STRING_new(); + if (digest == NULL) + goto cleanup2; + ASN1_OCTET_STRING_set(digest, md_data2, md_len2); + digest_len = i2d_ASN1_OCTET_STRING(digest, NULL); + digest_buf = malloc(digest_len); + if (digest_buf == NULL) + goto cleanup2; + + digestInfo_len = ASN1_object_size(1, alg_len + digest_len, + V_ASN1_SEQUENCE); + y = digestInfo_buf = malloc(digestInfo_len); + if (digestInfo_buf == NULL) + goto cleanup2; + ASN1_put_object(&y, 1, alg_len + digest_len, V_ASN1_SEQUENCE, + V_ASN1_UNIVERSAL); + i2d_X509_ALGOR(alg, &y); + i2d_ASN1_OCTET_STRING(digest, &y); +#ifdef DEBUG_SIG + pkiDebug("signing buffer\n"); + print_buffer(digestInfo_buf, digestInfo_len); + print_buffer_bin(digestInfo_buf, digestInfo_len, "/tmp/client_tosign"); +#endif + retval = pkinit_sign_data(reqctx, digestInfo_buf, digestInfo_len, + &sig, &sig_len, filename); + } else { + if (reqctx != NULL) + pkiDebug("mech = %s\n", reqctx->pkcs11_method?"CKM_SHA1_RSA_PKCS":"FS"); + retval = pkinit_sign_data(reqctx, abuf, alen, &sig, &sig_len, filename); + } +#ifdef DEBUG_SIG + print_buffer(sig, sig_len); +#endif free(abuf); if (retval) - goto cleanup; + goto cleanup2; /* Add signature */ if (!ASN1_STRING_set(p7si->enc_digest, (unsigned char *) sig, sig_len)) { @@ -689,20 +796,20 @@ pkcs7_signeddata_create(unsigned char *data, krb5_set_error_message(context, retval, "%s\n", ERR_error_string(err, NULL)); pkiDebug("failed to add a signed digest attribute\n"); - goto cleanup; + goto cleanup2; } /* adder signer_info to pkcs7 signed */ if (!PKCS7_add_signer(p7, p7si)) - goto cleanup; + goto cleanup2; /* start on adding data to the pkcs7 signed */ if ((inner_p7 = PKCS7_new()) == NULL) - goto cleanup; + goto cleanup2; if ((pkinit_data = ASN1_TYPE_new()) == NULL) - goto cleanup; + goto cleanup2; pkinit_data->type = V_ASN1_OCTET_STRING; if ((pkinit_data->value.octet_string = ASN1_OCTET_STRING_new()) == NULL) - goto cleanup; + goto cleanup2; if (!ASN1_OCTET_STRING_set(pkinit_data->value.octet_string, data, data_len)) { unsigned long err = ERR_peek_error(); @@ -710,11 +817,11 @@ pkcs7_signeddata_create(unsigned char *data, krb5_set_error_message(context, retval, "%s\n", ERR_error_string(err, NULL)); pkiDebug("failed to add pkcs7 data\n"); - goto cleanup; + goto cleanup2; } if (!PKCS7_set0_type_other(inner_p7, OBJ_obj2nid(oid), pkinit_data)) - goto cleanup; + goto cleanup2; if (p7s->contents != NULL) PKCS7_free(p7s->contents); p7s->contents = inner_p7; @@ -726,11 +833,11 @@ pkcs7_signeddata_create(unsigned char *data, krb5_set_error_message(context, retval, "%s\n", ERR_error_string(err, NULL)); pkiDebug("failed to der encode pkcs7\n"); - goto cleanup; + goto cleanup2; } if ((p = *signed_data = (unsigned char *) malloc(*signed_data_len)) == NULL) - goto cleanup; + goto cleanup2; /* DER encode PKCS7 data */ retval = i2d_PKCS7(p7, &p); @@ -740,14 +847,27 @@ pkcs7_signeddata_create(unsigned char *data, krb5_set_error_message(context, retval, "%s\n", ERR_error_string(err, NULL)); pkiDebug("failed to der encode pkcs7\n"); - goto cleanup; + goto cleanup2; } retval = 0; + cleanup2: + EVP_MD_CTX_cleanup(&ctx); + if (reqctx && reqctx->pkcs11_method && reqctx->mech == CKM_RSA_PKCS) + EVP_MD_CTX_cleanup(&ctx2); + if (alg != NULL) + X509_ALGOR_free(alg); + if (digest != NULL) + ASN1_OCTET_STRING_free(digest); + if (alg_buf != NULL) + free(alg_buf); + if (digest_buf != NULL) + free(digest_buf); + if (digestInfo_buf != NULL) + free(digestInfo_buf); cleanup: if (p7 != NULL) PKCS7_free(p7); - EVP_MD_CTX_cleanup(&ctx); if (sig != NULL) free(sig); @@ -782,6 +902,9 @@ pkcs7_signeddata_verify(unsigned char *signed_data, pkiDebug("failed to get the name of the directory for trusted CAs\n"); return KRB5KDC_ERR_PREAUTH_FAILED; } +#ifdef DEBUG_ASN1 + print_buffer_bin(p, signed_data_len, "/tmp/kdc_signeddata"); +#endif if ((p7 = d2i_PKCS7(NULL, &p, signed_data_len)) == NULL) { unsigned long err = ERR_peek_error(); @@ -825,9 +948,24 @@ pkcs7_signeddata_verify(unsigned char *signed_data, goto cleanup; if (!X509_STORE_CTX_init(&cert_ctx, store, x, p7->d.sign->cert)) goto cleanup; + if (p7->d.sign->cert != NULL) { + int sk_size = sk_X509_num(p7->d.sign->cert); + int i; + char buf[256]; + pkiDebug("received cert chain of size %d\n", sk_size); + for (i = 0; i < sk_size; i++) { + X509 *tmp_cert = sk_X509_value(p7->d.sign->cert, i); + X509_NAME_oneline(X509_get_subject_name(tmp_cert), buf, 256); + pkiDebug("cert #%d: %s\n", i, buf); + } + } i = X509_verify_cert(&cert_ctx); if (i <= 0) { int j = X509_STORE_CTX_get_error(&cert_ctx); + int sk_size = sk_X509_num(p7->d.sign->cert); + int i; + char buf[256]; + *cert = X509_dup(cert_ctx.current_cert); switch(j) { case X509_V_ERR_CERT_REVOKED: @@ -840,9 +978,17 @@ pkcs7_signeddata_verify(unsigned char *signed_data, default: retval = KRB5KDC_ERR_INVALID_CERTIFICATE; } + X509_NAME_oneline(X509_get_subject_name(*cert), buf, 256); + pkiDebug("problem with cert DN = %s (error=%d)\n", buf, j); pkiDebug("%s\n", X509_verify_cert_error_string(j)); krb5_set_error_message(context, retval, "%s\n", X509_verify_cert_error_string(j)); + pkiDebug("received cert chain of size %d\n", sk_size); + for (i = 0; i < sk_size; i++) { + X509 *tmp_cert = sk_X509_value(p7->d.sign->cert, i); + X509_NAME_oneline(X509_get_subject_name(tmp_cert), buf, 256); + pkiDebug("cert #%d: %s\n", i, buf); + } } X509_STORE_CTX_cleanup(&cert_ctx); if (i <= 0) @@ -937,15 +1083,16 @@ pkcs7_envelopeddata_verify(unsigned char *enveloped_data, X509 *client_cert, char *key_filename, krb5_preauthtype pa_type, - pkinit_context *plgctx, X509 ** cert, - krb5_context context) + pkinit_req_context *reqctx) { krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED; PKCS7 *p7 = NULL; BIO *out = NULL; int i = 0, size = 0; const unsigned char *p = enveloped_data; + krb5_context context = reqctx->plugctx->context; + pkinit_context *plgctx = reqctx->plugctx; int tmp_buf_len = 0, tmp_buf2_len = 0; unsigned char *tmp_buf = NULL, *tmp_buf2 = NULL; @@ -966,7 +1113,7 @@ pkcs7_envelopeddata_verify(unsigned char *enveloped_data, } out = BIO_new(BIO_s_mem()); - if (pkcs7_decrypt(p7, client_cert, out, key_filename)) { + if (pkcs7_decrypt(reqctx, p7, client_cert, out, key_filename)) { pkiDebug("PKCS7 decryption successful\n"); } else { unsigned long err = ERR_peek_error(); @@ -1094,7 +1241,7 @@ pkcs7_envelopeddata_create(unsigned char *key_pack, unsigned char *p = NULL; retval = pkcs7_signeddata_create(key_pack, key_pack_len, &signed_data, - &signed_data_len, kdc_cert, filename, oid, context); + &signed_data_len, kdc_cert, filename, oid, context, NULL); if (retval) { pkiDebug("failed to create pkcs7 signed data\n"); goto cleanup; @@ -1191,7 +1338,6 @@ verify_id_pkinit_san(X509 *x, pkiDebug("unable to retrieve subject alt name ext\n"); goto cleanup; } - pkiDebug("found %d subject alt name extension(s)\n", sk_GENERAL_NAME_num(ialt)); @@ -1208,7 +1354,7 @@ verify_id_pkinit_san(X509 *x, #ifdef DEBUG_ASN1 print_buffer_bin(name.data, name.length, "/tmp/pkinit_san"); #endif - ret = decode_krb5_principal_name(&name, out); + ret = k5int_decode_krb5_principal_name(&name, out); } else if (plgctx->allow_upn && !OBJ_cmp(plgctx->id_pkinit_san9, gen->d.otherName->type_id)) { @@ -1807,7 +1953,7 @@ create_krb5_trustedCertifiers(STACK_OF(X509) * sk, } static int -pkcs7_decrypt(PKCS7 *p7, X509 *cert, BIO *data, char *filename) +pkcs7_decrypt(pkinit_req_context *reqctx, PKCS7 *p7, X509 *cert, BIO *data, char *filename) { int flags = PKCS7_BINARY; BIO *tmpmem = NULL; @@ -1822,7 +1968,7 @@ pkcs7_decrypt(PKCS7 *p7, X509 *cert, BIO *data, char *filename) return 0; } - if(!(tmpmem = pkcs7_dataDecode(p7, cert, filename))) { + if(!(tmpmem = pkcs7_dataDecode(reqctx, p7, cert, filename))) { pkiDebug("unable to decrypt pkcs7 object\n"); return 0; } @@ -1838,7 +1984,7 @@ pkcs7_decrypt(PKCS7 *p7, X509 *cert, BIO *data, char *filename) } static BIO * -pkcs7_dataDecode(PKCS7 *p7, X509 *pcert, char *filename) +pkcs7_dataDecode(pkinit_req_context *reqctx, PKCS7 *p7, X509 *pcert, char *filename) { int i = 0, jj= 0, jj2 = 0, tmp_len = 0; BIO *out=NULL,*etmp=NULL,*bio=NULL; @@ -1904,7 +2050,7 @@ pkcs7_dataDecode(PKCS7 *p7, X509 *pcert, char *filename) if (pcert == NULL) { for (i=0; i<sk_PKCS7_RECIP_INFO_num(rsk); i++) { ri=sk_PKCS7_RECIP_INFO_value(rsk,i); - jj = pkinit_decode_data(M_ASN1_STRING_data(ri->enc_key), + jj = pkinit_decode_data(reqctx, M_ASN1_STRING_data(ri->enc_key), M_ASN1_STRING_length(ri->enc_key), &tmp, &tmp_len, filename, pcert); if (jj) { @@ -1927,7 +2073,7 @@ pkcs7_dataDecode(PKCS7 *p7, X509 *pcert, char *filename) } } else { - jj = pkinit_decode_data(M_ASN1_STRING_data(ri->enc_key), + jj = pkinit_decode_data(reqctx, M_ASN1_STRING_data(ri->enc_key), M_ASN1_STRING_length(ri->enc_key), &tmp, &tmp_len, filename, pcert); if (jj || tmp_len <= 0) { diff --git a/src/plugins/preauth/pkinit/pkinit_srv.c b/src/plugins/preauth/pkinit/pkinit_srv.c index 145ddc4..77ce478 100644 --- a/src/plugins/preauth/pkinit/pkinit_srv.c +++ b/src/plugins/preauth/pkinit/pkinit_srv.c @@ -43,6 +43,10 @@ #include <openssl/asn1_mac.h> #include <openssl/sha.h> +#ifndef WITHOUT_PKCS11 +#include <opensc/pkcs11.h> +#endif + #include "pkinit.h" #ifdef DEBUG @@ -51,30 +55,40 @@ #define pkiDebug(args...) #endif -static krb5_error_code pkinit_server_get_edata - (krb5_context context, krb5_kdc_req * request, - struct _krb5_db_entry_new * client, - struct _krb5_db_entry_new * server, - preauth_get_entry_data_proc server_get_entry_data, - void *pa_plugin_context, - krb5_pa_data * data); - -static krb5_error_code pkinit_server_verify_padata - (krb5_context context, struct _krb5_db_entry_new * client, - krb5_data *req_pkt, krb5_kdc_req * request, - krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data * data, - preauth_get_entry_data_proc server_get_entry_data, - void *pa_plugin_context, void **pa_request_context, - krb5_data **e_data); - -static krb5_error_code pkinit_server_return_padata - (krb5_context context, krb5_pa_data * padata, - struct _krb5_db_entry_new * client, krb5_data *req_pkt, - krb5_kdc_req * request, krb5_kdc_rep * reply, - struct _krb5_key_data * client_key, - krb5_keyblock * encrypting_key, krb5_pa_data ** send_pa, - preauth_get_entry_data_proc server_get_entry_data, - void *pa_plugin_context, void **pa_request_context); +static krb5_error_code +pkinit_server_get_edata(krb5_context context, + krb5_kdc_req * request, + struct _krb5_db_entry_new * client, + struct _krb5_db_entry_new * server, + preauth_get_entry_data_proc server_get_entry_data, + void *pa_plugin_context, + krb5_pa_data * data); + +static krb5_error_code +pkinit_server_verify_padata(krb5_context context, + struct _krb5_db_entry_new * client, + krb5_data *req_pkt, + krb5_kdc_req * request, + krb5_enc_tkt_part * enc_tkt_reply, + krb5_pa_data * data, + preauth_get_entry_data_proc server_get_entry_data, + void *pa_plugin_context, + void **pa_request_context, + krb5_data **e_data); + +static krb5_error_code +pkinit_server_return_padata(krb5_context context, + krb5_pa_data * padata, + struct _krb5_db_entry_new * client, + krb5_data *req_pkt, + krb5_kdc_req * request, + krb5_kdc_rep * reply, + struct _krb5_key_data * client_key, + krb5_keyblock * encrypting_key, + krb5_pa_data ** send_pa, + preauth_get_entry_data_proc server_get_entry_data, + void *pa_plugin_context, + void **pa_request_context); static int pkinit_server_get_flags (krb5_context kcontext, krb5_preauthtype patype); @@ -233,7 +247,7 @@ pkinit_create_sequence_of_principal_identifiers(STACK_OF(X509) *sk, int type, kr pkiDebug("create_krb5_trustedCertifiers failed\n"); goto cleanup; } - retval = encode_krb5_td_trusted_certifiers(krb5_trusted_certifiers, &td_certifiers); + retval = k5int_encode_krb5_td_trusted_certifiers(krb5_trusted_certifiers, &td_certifiers); if (retval) { pkiDebug("encode_krb5_td_trusted_certifiers failed\n"); goto cleanup; @@ -248,14 +262,14 @@ pkinit_create_sequence_of_principal_identifiers(STACK_OF(X509) *sk, int type, kr } typed_data[1] = NULL; init_krb5_typed_data(&typed_data[0]); - if (typed_data == NULL) { + if (typed_data[0] == 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); + retval = k5int_encode_krb5_typed_data(typed_data, &data); if (retval) { pkiDebug("encode_krb5_typed_data failed\n"); goto cleanup; @@ -439,7 +453,7 @@ pkinit_create_td_dh_parameters(pkinit_context *plgctx, krb5_data **out_data) algId[0]->algorithm = dh_oid; } - retval = encode_krb5_td_dh_parameters(algId, &encoded_algId); + retval = k5int_encode_krb5_td_dh_parameters(algId, &encoded_algId); if (retval) goto cleanup; #ifdef DEBUG_ASN1 @@ -459,7 +473,7 @@ pkinit_create_td_dh_parameters(pkinit_context *plgctx, krb5_data **out_data) 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); + retval = k5int_encode_krb5_typed_data(typed_data, &data); if (retval) { pkiDebug("encode_krb5_typed_data failed\n"); goto cleanup; @@ -576,6 +590,8 @@ pkinit_server_verify_padata(krb5_context context, pkinit_context *plgctx = (pkinit_context *)pa_plugin_context; krb5_preauthtype pa_type; krb5_principal tmp_client; + krb5_checksum cksum = {0, 0, 0, NULL}; + krb5_data *der_req = NULL; pkiDebug("pkinit_verify_padata: entered!\n"); @@ -596,7 +612,7 @@ pkinit_server_verify_padata(krb5_context context, case KRB5_PADATA_PK_AS_REQ: pkiDebug("processing KRB5_PADATA_PK_AS_REQ\n"); pa_type = (int)data->pa_type; - retval = decode_krb5_pa_pk_as_req(&scratch, &reqp); + retval = k5int_decode_krb5_pa_pk_as_req(&scratch, &reqp); scratch.data = NULL; scratch.length = 0; if (retval) { @@ -612,7 +628,7 @@ pkinit_server_verify_padata(krb5_context context, case KRB5_PADATA_PK_AS_REQ_OLD: pkiDebug("processing KRB5_PADATA_PK_AS_REQ_OLD\n"); pa_type = KRB5_PADATA_PK_AS_REQ_OLD; - retval = decode_krb5_pa_pk_as_req_draft9(&scratch, &reqp9); + retval = k5int_decode_krb5_pa_pk_as_req_draft9(&scratch, &reqp9); scratch.data = NULL; scratch.length = 0; if (retval) { @@ -668,12 +684,13 @@ pkinit_server_verify_padata(krb5_context context, #endif switch ((int)data->pa_type) { case KRB5_PADATA_PK_AS_REQ: - retval = decode_krb5_auth_pack(&scratch, &auth_pack); + retval = k5int_decode_krb5_auth_pack(&scratch, &auth_pack); if (retval) { pkiDebug("failed to decode krb5_auth_pack\n"); goto cleanup; } + /* check dh parameters */ if (auth_pack->clientPublicValue != NULL) { retval = server_check_dh(plgctx, &auth_pack->clientPublicValue->algorithm.parameters, @@ -684,6 +701,36 @@ pkinit_server_verify_padata(krb5_context context, goto cleanup; } } + /* check the checksum */ + retval = encode_krb5_kdc_req_body(request, &der_req); + if (retval) { + pkiDebug("encode_krb5_kdc_req_body returned %d\n", (int) retval); + goto cleanup; + } + retval = krb5_c_make_checksum(context, CKSUMTYPE_NIST_SHA, NULL, + 0, der_req, &cksum); + if (retval) { + pkiDebug("unable to calculate AS REQ checksum\n"); + goto cleanup; + } + if (cksum.length != auth_pack->pkAuthenticator.paChecksum.length || + memcmp(cksum.contents, + auth_pack->pkAuthenticator.paChecksum.contents, + cksum.length)) { + pkiDebug("failed to match the checksum\n"); +#ifdef DEBUG_CKSUM + pkiDebug("calculating checksum on buf size (%d)\n", req_pkt->length); + print_buffer(req_pkt->data, req_pkt->length); + pkiDebug("received checksum type=%d size=%d ", auth_pack->pkAuthenticator.paChecksum.checksum_type, auth_pack->pkAuthenticator.paChecksum.length); + print_buffer(auth_pack->pkAuthenticator.paChecksum.contents, auth_pack->pkAuthenticator.paChecksum.length); + pkiDebug("expected checksum type=%d size=%d ", cksum.checksum_type, cksum.length); + print_buffer(cksum.contents, cksum.length); +#endif + + retval = KRB5KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED; + goto cleanup; + } + /* check if kdcPkId present and match KDC's subjectIdentifier */ if (reqp->kdcPkId.data != NULL) { PKCS7_ISSUER_AND_SERIAL *is = NULL; @@ -716,7 +763,7 @@ pkinit_server_verify_padata(krb5_context context, break; case KRB5_PADATA_PK_AS_REP_OLD: case KRB5_PADATA_PK_AS_REQ_OLD: - retval = decode_krb5_auth_pack_draft9(&scratch, &auth_pack9); + retval = k5int_decode_krb5_auth_pack_draft9(&scratch, &auth_pack9); if (retval) { pkiDebug("failed to decode krb5_auth_pack_draft9\n"); goto cleanup; @@ -751,6 +798,10 @@ pkinit_server_verify_padata(krb5_context context, auth_pack->clientPublicValue->algorithm.algorithm.data != NULL) free(auth_pack->clientPublicValue->algorithm.algorithm.data); free_krb5_auth_pack(&auth_pack); + if (cksum.contents != NULL) + free(cksum.contents); + if (der_req != NULL) + krb5_free_data(context, der_req); break; case KRB5_PADATA_PK_AS_REP_OLD: case KRB5_PADATA_PK_AS_REQ_OLD: @@ -825,7 +876,7 @@ pkinit_server_return_padata(krb5_context context, switch ((int)padata->pa_type) { case KRB5_PADATA_PK_AS_REQ: pkiDebug("processing KRB5_PADATA_PK_AS_REQ\n"); - retval = decode_krb5_pa_pk_as_req(&scratch, &reqp); + retval = k5int_decode_krb5_pa_pk_as_req(&scratch, &reqp); if (retval) { pkiDebug("decode_krb5_pa_pk_as_req failed"); goto cleanup; @@ -840,7 +891,7 @@ pkinit_server_return_padata(krb5_context context, pkiDebug("pkcs7_signeddata_verify failed\n"); goto cleanup; } - retval = decode_krb5_auth_pack(&scratch, &auth_pack); + retval = k5int_decode_krb5_auth_pack(&scratch, &auth_pack); if (retval) { pkiDebug("failed to decode krb5_auth_pack\n"); goto cleanup; @@ -854,7 +905,7 @@ pkinit_server_return_padata(krb5_context context, case KRB5_PADATA_PK_AS_REP_OLD: case KRB5_PADATA_PK_AS_REQ_OLD: pkiDebug("processing KRB5_PADATA_PK_AS_REQ_OLD %d\n", padata->pa_type); - retval = decode_krb5_pa_pk_as_req_draft9(&scratch, &reqp9); + retval = k5int_decode_krb5_pa_pk_as_req_draft9(&scratch, &reqp9); if (retval) { pkiDebug("decode_krb5_pa_pk_as_req_draft9 failed"); goto cleanup; @@ -869,7 +920,7 @@ pkinit_server_return_padata(krb5_context context, pkiDebug("pkcs7_signeddata_verify failed"); goto cleanup; } - retval = decode_krb5_auth_pack_draft9(&scratch, &auth_pack9); + retval = k5int_decode_krb5_auth_pack_draft9(&scratch, &auth_pack9); if (retval) { pkiDebug("failed to decode krb5_auth_pack_draft9\n"); goto cleanup; @@ -978,7 +1029,7 @@ pkinit_server_return_padata(krb5_context context, dhkey_info.nonce = request->nonce; dhkey_info.dhKeyExpiration = 0; - retval = encode_krb5_kdc_dh_key_info(&dhkey_info, &encoded_dhkey_info); + retval = k5int_encode_krb5_kdc_dh_key_info(&dhkey_info, &encoded_dhkey_info); if (retval) { pkiDebug("encode_krb5_kdc_dh_key_info failed\n"); goto cleanup; @@ -993,8 +1044,8 @@ pkinit_server_return_padata(krb5_context context, encoded_dhkey_info->length, &rep->u.dh_Info.dhSignedData.data, &rep->u.dh_Info.dhSignedData.length, - kdc_cert, filename, - plgctx->id_pkinit_DHKeyData, context); + kdc_cert, filename, plgctx->id_pkinit_DHKeyData, + context, NULL); if (retval) { pkiDebug("failed to create pkcs7 signed data\n"); goto cleanup; @@ -1005,7 +1056,7 @@ pkinit_server_return_padata(krb5_context context, retval = pkcs7_signeddata_create(encoded_dhkey_info->data, encoded_dhkey_info->length, &rep9->u.dhSignedData.data, &rep9->u.dhSignedData.length, kdc_cert, filename, - plgctx->id_pkinit_authData9, context); + plgctx->id_pkinit_authData9, context, NULL); if (retval) { pkiDebug("failed to create pkcs7 signed data\n"); goto cleanup; @@ -1057,7 +1108,7 @@ pkinit_server_return_padata(krb5_context context, krb5_copy_keyblock_contents(context, encrypting_key, &key_pack->replyKey); - retval = encode_krb5_reply_key_pack(key_pack, + retval = k5int_encode_krb5_reply_key_pack(key_pack, &encoded_key_pack); if (retval) { pkiDebug("failed to encode reply_key_pack\n"); @@ -1087,7 +1138,7 @@ pkinit_server_return_padata(krb5_context context, krb5_copy_keyblock_contents(context, encrypting_key, &key_pack9->replyKey); - retval = encode_krb5_reply_key_pack_draft9(key_pack9, + retval = k5int_encode_krb5_reply_key_pack_draft9(key_pack9, &encoded_key_pack); if (retval) { pkiDebug("failed to encode reply_key_pack\n"); @@ -1132,11 +1183,11 @@ pkinit_server_return_padata(krb5_context context, switch ((int)padata->pa_type) { case KRB5_PADATA_PK_AS_REQ: - retval = encode_krb5_pa_pk_as_rep(rep, &out_data); + retval = k5int_encode_krb5_pa_pk_as_rep(rep, &out_data); break; case KRB5_PADATA_PK_AS_REP_OLD: case KRB5_PADATA_PK_AS_REQ_OLD: - retval = encode_krb5_pa_pk_as_rep_draft9(rep9, &out_data); + retval = k5int_encode_krb5_pa_pk_as_rep_draft9(rep9, &out_data); break; } if (retval) { @@ -1144,7 +1195,8 @@ pkinit_server_return_padata(krb5_context context, goto cleanup; } #ifdef DEBUG_ASN1 - print_buffer_bin(out_data->data, out_data->length, "/tmp/kdc_as_rep"); + if (out_data != NULL) + print_buffer_bin(out_data->data, out_data->length, "/tmp/kdc_as_rep"); #endif *send_pa = (krb5_pa_data *) malloc(sizeof(krb5_pa_data)); diff --git a/src/plugins/preauth/wpse/wpse_main.c b/src/plugins/preauth/wpse/wpse_main.c index 46ea662..f858063 100644 --- a/src/plugins/preauth/wpse/wpse_main.c +++ b/src/plugins/preauth/wpse/wpse_main.c @@ -90,6 +90,7 @@ static krb5_error_code client_process(krb5_context kcontext, void *plugin_context, void *request_context, + krb5_get_init_creds_opt *opt, preauth_get_client_data_proc client_get_data_proc, struct _krb5_preauth_client_rock *rock, krb5_kdc_req *request, @@ -208,6 +209,21 @@ client_req_cleanup(krb5_context kcontext, void *plugin_context, void *req_contex return; } +static krb5_error_code +client_gic_opt(krb5_context kcontext, + void *plugin_context, + krb5_get_init_creds_opt *opt, + const char *attr, + const char *value) +{ +#ifdef DEBUG + fprintf(stderr, "(wpse) client_gic_opt: received '%s' = '%s'\n", + attr, value); +#endif + return 0; +} + + /* Free state. */ static krb5_error_code server_free_pa_request_context(krb5_context kcontext, void *plugin_context, @@ -378,6 +394,7 @@ struct krb5plugin_preauth_client_ftable_v0 preauthentication_client_0 = { client_req_cleanup, /* request fini function */ client_process, /* process function */ NULL, /* try_again function */ + client_gic_opt /* get init creds opts function */ }; struct krb5plugin_preauth_server_ftable_v0 preauthentication_server_0 = { |