From ec49e6e673ab229462ef18aa2986167eaa643643 Mon Sep 17 00:00:00 2001 From: Sam Hartman Date: Mon, 28 Dec 2009 17:15:30 +0000 Subject: [PATCH] Anonymous support for Kerberos This ticket implements Project/Anonymous pkinit from k5wiki. Provides support for completely anonymous principals and untested client support for realm-exposed anonymous authentication. * Introduce kinit -n * Introduce kadmin -n * krb5_get_init_creds_opt_set_out_ccache aliases the supplied ccache * No longer generate ad-initial-verified-cas in pkinit * Fix pkinit interactions with non-TGT authentication Merge remote branch 'anonymous' into trunk Conflicts: src/lib/krb5/krb/gic_opt.c ticket: 6607 Tags: enhancement git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@23527 dc483132-0cff-0310-8789-dd5450dbe970 --- src/clients/kinit/kinit.c | 85 +++++--- src/include/k5-int-pkinit.h | 11 + src/include/k5-int.h | 13 +- src/include/krb5/krb5.hin | 35 +++- src/kadmin/cli/kadmin.c | 19 +- src/kdc/do_as_req.c | 18 ++ src/kdc/do_tgs_req.c | 3 +- src/kdc/kdc_authdata.c | 8 +- src/kdc/kdc_preauth.c | 9 +- src/lib/gssapi/krb5/disp_name.c | 9 +- src/lib/gssapi/krb5/import_name.c | 11 +- src/lib/kadm5/admin.h | 7 + src/lib/kadm5/clnt/client_init.c | 60 ++++-- src/lib/kadm5/clnt/libkadm5clnt.exports | 1 + src/lib/kadm5/srv/libkadm5srv.exports | 1 + src/lib/kadm5/srv/server_init.c | 13 ++ src/lib/krb5/krb/bld_princ.c | 30 +++ src/lib/krb5/krb/chk_trans.c | 6 + src/lib/krb5/krb/get_in_tkt.c | 101 ++++++++- src/lib/krb5/krb/gic_opt.c | 20 +- src/lib/krb5/libkrb5.exports | 3 + src/plugins/preauth/fast_factor.h | 8 +- src/plugins/preauth/pkinit/pkinit_clnt.c | 37 +++- src/plugins/preauth/pkinit/pkinit_crypto.h | 15 +- .../preauth/pkinit/pkinit_crypto_openssl.c | 191 ++++++++++++++---- src/plugins/preauth/pkinit/pkinit_identity.c | 108 +++++----- src/plugins/preauth/pkinit/pkinit_lib.c | 4 +- src/plugins/preauth/pkinit/pkinit_srv.c | 189 +++++++++-------- 28 files changed, 731 insertions(+), 284 deletions(-) diff --git a/src/clients/kinit/kinit.c b/src/clients/kinit/kinit.c index fdfae8828..79ac65418 100644 --- a/src/clients/kinit/kinit.c +++ b/src/clients/kinit/kinit.c @@ -106,6 +106,7 @@ struct k_opts int forwardable; int proxiable; + int anonymous; int addresses; int not_forwardable; @@ -189,6 +190,7 @@ usage() USAGE_BREAK_LONG "[-p | -P" USAGE_LONG_PROXIABLE "] " USAGE_BREAK_LONG + "-n" "[-a | -A" USAGE_LONG_ADDRESSES "] " USAGE_BREAK_LONG "[-C" USAGE_LONG_CANONICALIZE "] " @@ -214,6 +216,7 @@ usage() fprintf(stderr, "\t-F not forwardable\n"); fprintf(stderr, "\t-p proxiable\n"); fprintf(stderr, "\t-P not proxiable\n"); + fprintf(stderr, "\t -n anonymous\n"); fprintf(stderr, "\t-a include addresses\n"); fprintf(stderr, "\t-A do not include addresses\n"); fprintf(stderr, "\t-v validate\n"); @@ -282,7 +285,7 @@ parse_options(argc, argv, opts) int errflg = 0; int i; - while ((i = GETOPT(argc, argv, "r:fpFP54aAVl:s:c:kt:T:RS:vX:CE")) + while ((i = GETOPT(argc, argv, "r:fpFPn54aAVl:s:c:kt:T:RS:vX:CE")) != -1) { switch (i) { case 'V': @@ -316,6 +319,9 @@ parse_options(argc, argv, opts) case 'P': opts->not_proxiable = 1; break; + case 'n': + opts->anonymous = 1; + break; case 'a': opts->addresses = 1; break; @@ -472,44 +478,63 @@ k5_begin(opts, k5) else { /* No principal name specified */ - if (opts->action == INIT_KT) { - /* Use the default host/service name */ - code = krb5_sname_to_principal(k5->ctx, NULL, NULL, - KRB5_NT_SRV_HST, &k5->me); + if (opts->anonymous) { + char *defrealm; + code = krb5_get_default_realm(k5->ctx, &defrealm); if (code) { - com_err(progname, code, - "when creating default server principal name"); + com_err(progname, code, "while getting default realm"); return 0; } - if (k5->me->realm.data[0] == 0) { - code = krb5_unparse_name(k5->ctx, k5->me, &k5->name); - if (code == 0) - com_err(progname, KRB5_ERR_HOST_REALM_UNKNOWN, - "(principal %s)", k5->name); - else - com_err(progname, KRB5_ERR_HOST_REALM_UNKNOWN, - "for local services"); + code = krb5_build_principal_ext(k5->ctx, &k5->me, + strlen(defrealm), defrealm, + strlen(KRB5_WELLKNOWN_NAMESTR), KRB5_WELLKNOWN_NAMESTR, + strlen(KRB5_ANONYMOUS_PRINCSTR), KRB5_ANONYMOUS_PRINCSTR, + 0); + krb5_free_default_realm( k5->ctx, defrealm); + if (code) { + com_err(progname, code, "while building principal"); return 0; } } else { - /* Get default principal from cache if one exists */ - code = krb5_cc_get_principal(k5->ctx, k5->cc, - &k5->me); - if (code) - { - char *name = get_name_from_os(); - if (!name) - { - fprintf(stderr, "Unable to identify user\n"); + if (opts->action == INIT_KT) { + /* Use the default host/service name */ + code = krb5_sname_to_principal(k5->ctx, NULL, NULL, + KRB5_NT_SRV_HST, &k5->me); + if (code) { + com_err(progname, code, + "when creating default server principal name"); return 0; } - if ((code = krb5_parse_name_flags(k5->ctx, name, - flags, &k5->me))) - { - com_err(progname, code, "when parsing name %s", - name); + if (k5->me->realm.data[0] == 0) { + code = krb5_unparse_name(k5->ctx, k5->me, &k5->name); + if (code == 0) + com_err(progname, KRB5_ERR_HOST_REALM_UNKNOWN, + "(principal %s)", k5->name); + else + com_err(progname, KRB5_ERR_HOST_REALM_UNKNOWN, + "for local services"); return 0; } + } else { + /* Get default principal from cache if one exists */ + code = krb5_cc_get_principal(k5->ctx, k5->cc, + &k5->me); + if (code) + { + char *name = get_name_from_os(); + if (!name) + { + fprintf(stderr, "Unable to identify user\n"); + return 0; + } + if ((code = krb5_parse_name_flags(k5->ctx, name, + flags, &k5->me))) + { + com_err(progname, code, "when parsing name %s", + name); + return 0; + } + } } } } @@ -593,6 +618,8 @@ k5_kinit(opts, k5) krb5_get_init_creds_opt_set_proxiable(options, 0); if (opts->canonicalize) krb5_get_init_creds_opt_set_canonicalize(options, 1); + if (opts->anonymous) + krb5_get_init_creds_opt_set_anonymous(options, 1); if (opts->addresses) { krb5_address **addresses = NULL; diff --git a/src/include/k5-int-pkinit.h b/src/include/k5-int-pkinit.h index 77bd260ff..8dcbd5d4f 100644 --- a/src/include/k5-int-pkinit.h +++ b/src/include/k5-int-pkinit.h @@ -283,4 +283,15 @@ decode_krb5_td_dh_parameters(const krb5_data *, krb5_algorithm_identifier ***); void krb5_free_typed_data(krb5_context, krb5_typed_data **); +krb5_error_code +encode_krb5_enc_data(const krb5_enc_data *, krb5_data **); + +krb5_error_code +encode_krb5_encryption_key(const krb5_keyblock *rep, krb5_data **code); + +krb5_error_code +krb5_encrypt_helper(krb5_context context, const krb5_keyblock *key, + krb5_keyusage keyusage, const krb5_data *plain, + krb5_enc_data *cipher); + #endif /* _KRB5_INT_PKINIT_H */ diff --git a/src/include/k5-int.h b/src/include/k5-int.h index f1b48c0af..a70eae9c5 100644 --- a/src/include/k5-int.h +++ b/src/include/k5-int.h @@ -806,11 +806,6 @@ krb5_error_code krb5_crypto_us_timeofday(krb5_int32 *, krb5_int32 *); /* this helper fct is in libkrb5, but it makes sense declared here. */ -krb5_error_code -krb5_encrypt_helper(krb5_context context, const krb5_keyblock *key, - krb5_keyusage keyusage, const krb5_data *plain, - krb5_enc_data *cipher); - krb5_error_code krb5_encrypt_keyhelper(krb5_context context, krb5_key key, krb5_keyusage keyusage, const krb5_data *plain, @@ -1532,7 +1527,7 @@ void KRB5_CALLCONV krb5_free_priv_enc_part(krb5_context, krb5_priv_enc_part *); /* allow either constructed or primitive encoding, so check for bit 6 set or reset */ #define krb5int_is_app_tag(dat,tag) \ - ((dat) && (dat)->length && \ + ((dat != NULL) && (dat)->length && \ ((((dat)->data[0] & ~0x20) == ((tag) | 0x40)))) #define krb5_is_krb_ticket(dat) krb5int_is_app_tag(dat, 1) #define krb5_is_krb_authenticator(dat) krb5int_is_app_tag(dat, 2) @@ -1571,9 +1566,6 @@ encode_krb5_authenticator(const krb5_authenticator *rep, krb5_data **code); krb5_error_code encode_krb5_ticket(const krb5_ticket *rep, krb5_data **code); -krb5_error_code -encode_krb5_encryption_key(const krb5_keyblock *rep, krb5_data **code); - krb5_error_code encode_krb5_enc_tkt_part(const krb5_enc_tkt_part *rep, krb5_data **code); @@ -1659,9 +1651,6 @@ encode_krb5_etype_info(krb5_etype_info_entry *const *, krb5_data **code); krb5_error_code encode_krb5_etype_info2(krb5_etype_info_entry *const *, krb5_data **code); -krb5_error_code -encode_krb5_enc_data(const krb5_enc_data *, krb5_data **); - krb5_error_code encode_krb5_pa_enc_ts(const krb5_pa_enc_ts *, krb5_data **); diff --git a/src/include/krb5/krb5.hin b/src/include/krb5/krb5.hin index 7f7b56dd8..470ca4d50 100644 --- a/src/include/krb5/krb5.hin +++ b/src/include/krb5/krb5.hin @@ -261,6 +261,8 @@ typedef krb5_principal_data * krb5_principal; #define KRB5_NT_SMTP_NAME 7 /* Windows 2000 UPN */ #define KRB5_NT_ENTERPRISE_PRINCIPAL 10 +#define KRB5_NT_WELLKNOWN 11 +#define KRB5_WELLKNOWN_NAMESTR "WELLKNOWN" /*first component of NT_WELLKNOWN principals*/ /* Windows 2000 UPN and SID */ #define KRB5_NT_MS_PRINCIPAL -128 /* NT 4 style name */ @@ -293,6 +295,14 @@ typedef const krb5_principal_data *krb5_const_principal; */ krb5_boolean KRB5_CALLCONV krb5_is_referral_realm(const krb5_data *); +/*Both these functions return constant storage that must not be freed*/ + +const krb5_data *KRB5_CALLCONV +krb5_anonymous_realm(void); +krb5_const_principal KRB5_CALLCONV +krb5_anonymous_principal(void); +#define KRB5_ANONYMOUS_REALMSTR "WELLKNOWN:ANONYMOUS" +#define KRB5_ANONYMOUS_PRINCSTR "ANONYMOUS" /*wellknown name type*/ /* * end "base-defs.h" */ @@ -628,7 +638,7 @@ krb5_c_keyed_checksum_types(krb5_context context, krb5_enctype enctype, #define KRB5_KEYUSAGE_PA_S4U_X509_USER_REPLY 27 /* XXX note conflict with above */ #define KRB5_KEYUSAGE_AD_SIGNEDPATH -21 - +#define KRB5_KEYUSAGE_PA_PKINIT_KX 44 /* define in draft-ietf-krb-wg-preauth-framework*/ #define KRB5_KEYUSAGE_FAST_REQ_CHKSUM 50 #define KRB5_KEYUSAGE_FAST_ENC 51 @@ -838,10 +848,9 @@ krb5_verify_checksum(krb5_context context, krb5_cksumtype ctype, /* #define KDC_OPT_RESERVED 0x00100000 */ /* #define KDC_OPT_RESERVED 0x00080000 */ /* #define KDC_OPT_RESERVED 0x00040000 */ -#define KDC_OPT_REQUEST_ANONYMOUS 0x00020000 #define KDC_OPT_CNAME_IN_ADDL_TKT 0x00020000 #define KDC_OPT_CANONICALIZE 0x00010000 -/* #define KDC_OPT_RESERVED 0x00008000 */ +#define KDC_OPT_REQUEST_ANONYMOUS 0x00008000 /* #define KDC_OPT_RESERVED 0x00004000 */ /* #define KDC_OPT_RESERVED 0x00002000 */ /* #define KDC_OPT_RESERVED 0x00001000 */ @@ -929,9 +938,8 @@ krb5_verify_checksum(krb5_context context, krb5_cksumtype ctype, #define TKT_FLG_HW_AUTH 0x00100000 #define TKT_FLG_TRANSIT_POLICY_CHECKED 0x00080000 #define TKT_FLG_OK_AS_DELEGATE 0x00040000 -#define TKT_FLG_ANONYMOUS 0x00020000 #define TKT_FLG_ENC_PA_REP 0x00010000 -/* #define TKT_FLG_RESERVED 0x00008000 */ +#define TKT_FLG_ANONYMOUS 0x00008000 /* #define TKT_FLG_RESERVED 0x00004000 */ /* #define TKT_FLG_RESERVED 0x00002000 */ /* #define TKT_FLG_RESERVED 0x00001000 */ @@ -1033,6 +1041,7 @@ krb5_verify_checksum(krb5_context context, krb5_cksumtype ctype, #define KRB5_PADATA_FX_FAST 136 #define KRB5_PADATA_FX_ERROR 137 #define KRB5_PADATA_ENCRYPTED_CHALLENGE 138 +#define KRB5_PADATA_PKINIT_KX 147 #define KRB5_ENCPADATA_REQ_ENC_PA_REP 149 #define KRB5_SAM_USE_SAD_AS_KEY 0x80000000 @@ -2196,6 +2205,7 @@ typedef struct _krb5_get_init_creds_opt { #define KRB5_GET_INIT_CREDS_OPT_SALT 0x0080 #define KRB5_GET_INIT_CREDS_OPT_CHG_PWD_PRMPT 0x0100 #define KRB5_GET_INIT_CREDS_OPT_CANONICALIZE 0x0200 +#define KRB5_GET_INIT_CREDS_OPT_ANONYMOUS 0x0400 krb5_error_code KRB5_CALLCONV @@ -2229,6 +2239,21 @@ void KRB5_CALLCONV krb5_get_init_creds_opt_set_canonicalize(krb5_get_init_creds_opt *opt, int canonicalize); +/** + * Request anonymous credentials from the KDC. If the client name looks like + * "@REALM" (an empty principal name), then fully anonymous credentials are + * requested. If the client name looks like "name@REALM," then credentials + * tied to a specific realm are requested. + * + * Credentials tied to a specific realm are not supported in this version. + * + * Note that anonymous credentials are only a request; clients must verify that + * credentials are anonymous if that is a requirement. + */ +void KRB5_CALLCONV +krb5_get_init_creds_opt_set_anonymous(krb5_get_init_creds_opt *opt, + int anonymous); + void KRB5_CALLCONV krb5_get_init_creds_opt_set_etype_list(krb5_get_init_creds_opt *opt, krb5_enctype *etype_list, diff --git a/src/kadmin/cli/kadmin.c b/src/kadmin/cli/kadmin.c index c8cb3fb58..1bcf891ed 100644 --- a/src/kadmin/cli/kadmin.c +++ b/src/kadmin/cli/kadmin.c @@ -119,7 +119,7 @@ usage() { fprintf(stderr, "Usage: %s [-r realm] [-p principal] [-q query] [clnt|local args]\n" - "\tclnt args: [-s admin_server[:port]] [[-c ccache]|[-k [-t keytab]]]\n" + "\tclnt args: [-s admin_server[:port]] [[-c ccache]|[-k [-t keytab]]]|[-n]\n" "\tlocal args: [-x db_args]* [-d dbname] [-e \"enc:salt ...\"] [-m]\n" "where,\n\t[-x db_args]* - any number of database specific arguments.\n" "\t\t\tLook at each database documentation for supported arguments\n", @@ -238,7 +238,7 @@ kadmin_startup(int argc, char *argv[]) char *princstr = NULL, *keytab_name = NULL, *query = NULL; char *password = NULL; char *luser, *canon, *cp; - int optchar, freeprinc = 0, use_keytab = 0; + int optchar, freeprinc = 0, use_keytab = 0, use_anonymous = 0; struct passwd *pw; kadm5_ret_t retval; krb5_ccache cc; @@ -270,7 +270,7 @@ kadmin_startup(int argc, char *argv[]) exit(1); } - while ((optchar = getopt(argc, argv, "x:r:p:kq:w:d:s:mc:t:e:ON")) != EOF) { + while ((optchar = getopt(argc, argv, "x:r:p:knq:w:d:s:mc:t:e:ON")) != EOF) { switch (optchar) { case 'x': db_args_size++; @@ -296,6 +296,9 @@ kadmin_startup(int argc, char *argv[]) case 'k': use_keytab++; break; + case 'n': + use_anonymous++; + break; case 't': keytab_name = optarg; break; @@ -349,7 +352,9 @@ kadmin_startup(int argc, char *argv[]) } } if ((ccache_name && use_keytab) || - (keytab_name && !use_keytab)) + (keytab_name && !use_keytab) + || (ccache_name && use_anonymous) + || (use_anonymous &&use_keytab)) usage(); if (def_realm == NULL && krb5_get_default_realm(context, &def_realm)) { @@ -487,6 +492,12 @@ kadmin_startup(int argc, char *argv[]) retval = kadm5_init_with_creds(context, princstr, cc, svcname, ¶ms, KADM5_STRUCT_VERSION, KADM5_API_VERSION_3, db_args, &handle); + } else if ( use_anonymous) { + printf("Authenticating as principal %s with password; anonymous requested.\n", + princstr); + retval = kadm5_init_anonymous(context, princstr, svcname, ¶ms, + KADM5_STRUCT_VERSION, + KADM5_API_VERSION_3, db_args, &handle); } else if (use_keytab) { if (keytab_name) printf("Authenticating as principal %s with keytab %s.\n", diff --git a/src/kdc/do_as_req.c b/src/kdc/do_as_req.c index 23f1ddcb8..58da726cb 100644 --- a/src/kdc/do_as_req.c +++ b/src/kdc/do_as_req.c @@ -389,6 +389,24 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt, enc_tkt_reply.caddrs = request->addresses; enc_tkt_reply.authorization_data = 0; + /* If anonymous requests are being used, adjust the realm of the client principal*/ + if (isflagset(request->kdc_options, KDC_OPT_REQUEST_ANONYMOUS)) { + if (!krb5_principal_compare_any_realm(kdc_context, request->client, + krb5_anonymous_principal())) { + errcode = KRB5KDC_ERR_BADOPTION; + status = "Anonymous requested but anonymous principal not used."; + goto errout; + } + setflag(enc_tkt_reply.flags, TKT_FLG_ANONYMOUS); + krb5_free_principal(kdc_context, request->client); + errcode = krb5_copy_principal(kdc_context, krb5_anonymous_principal(), + &request->client); + if (errcode) { + status = "Copying anonymous principal"; + goto errout; + } + enc_tkt_reply.client = request->client; + } /* * Check the preauthentication if it is there. */ diff --git a/src/kdc/do_tgs_req.c b/src/kdc/do_tgs_req.c index 75d413250..4a778f412 100644 --- a/src/kdc/do_tgs_req.c +++ b/src/kdc/do_tgs_req.c @@ -593,7 +593,8 @@ tgt_again: } else { enc_tkt_reply.times.renew_till = 0; } - + if (isflagset(header_enc_tkt->flags, TKT_FLG_ANONYMOUS)) + setflag(enc_tkt_reply.flags, TKT_FLG_ANONYMOUS); /* * Set authtime to be the same as header or evidence ticket's */ diff --git a/src/kdc/kdc_authdata.c b/src/kdc/kdc_authdata.c index 03bfe29c4..882167443 100644 --- a/src/kdc/kdc_authdata.c +++ b/src/kdc/kdc_authdata.c @@ -128,6 +128,7 @@ typedef struct _krb5_authdata_systems { int type; #define AUTHDATA_FLAG_CRITICAL 0x1 #define AUTHDATA_FLAG_PRE_PLUGIN 0x2 +#define AUTHDATA_FLAG_ANONYMOUS 0x4 /*Use this plugin even for anonymous tickets*/ int flags; void *plugin_context; init_proc init; @@ -143,7 +144,7 @@ static krb5_authdata_systems static_authdata_systems[] = { /* Propagate client-submitted authdata */ "tgs_req", AUTHDATA_SYSTEM_V2, - AUTHDATA_FLAG_CRITICAL | AUTHDATA_FLAG_PRE_PLUGIN, + AUTHDATA_FLAG_CRITICAL | AUTHDATA_FLAG_PRE_PLUGIN|AUTHDATA_FLAG_ANONYMOUS, NULL, NULL, NULL, @@ -153,7 +154,7 @@ static krb5_authdata_systems static_authdata_systems[] = { /* Propagate TGT authdata */ "tgt", AUTHDATA_SYSTEM_V2, - AUTHDATA_FLAG_CRITICAL, + AUTHDATA_FLAG_CRITICAL|AUTHDATA_FLAG_ANONYMOUS, NULL, NULL, NULL, @@ -765,6 +766,9 @@ handle_authdata (krb5_context context, for (i = 0; i < n_authdata_systems; i++) { const krb5_authdata_systems *asys = &authdata_systems[i]; + if (isflagset(enc_tkt_reply->flags, TKT_FLG_ANONYMOUS) && + !isflagset(asys->flags, AUTHDATA_FLAG_ANONYMOUS)) + continue; switch (asys->type) { case AUTHDATA_SYSTEM_V0: diff --git a/src/kdc/kdc_preauth.c b/src/kdc/kdc_preauth.c index 2262c8956..d14b18333 100644 --- a/src/kdc/kdc_preauth.c +++ b/src/kdc/kdc_preauth.c @@ -1275,6 +1275,7 @@ return_padata(krb5_context context, krb5_db_entry *client, krb5_data *req_pkt, krb5_pa_data ** send_pa_list; krb5_pa_data ** send_pa; krb5_pa_data * pa = 0; + krb5_pa_data null_item; krb5_preauth_systems * ap; int * pa_order; int * pa_type; @@ -1308,7 +1309,8 @@ return_padata(krb5_context context, krb5_db_entry *client, krb5_data *req_pkt, return retval; } key_modified = FALSE; - + null_item.contents = NULL; + null_item.length = NULL; send_pa = send_pa_list; *send_pa = 0; @@ -1330,7 +1332,8 @@ return_padata(krb5_context context, krb5_db_entry *client, krb5_data *req_pkt, continue; if (find_pa_context(ap, *padata_context, &pa_context)) continue; - pa = 0; + pa = &null_item; + null_item.pa_type = ap->type; if (request->padata) { for (padata = request->padata; *padata; padata++) { if ((*padata)->pa_type == ap->type) { @@ -1900,7 +1903,7 @@ return_sam_data(krb5_context context, krb5_pa_data *in_padata, krb5_sam_response *sr = 0; krb5_predicted_sam_response *psr = 0; - if (in_padata == 0) + if (in_padata->contents == 0) return 0; /* diff --git a/src/lib/gssapi/krb5/disp_name.c b/src/lib/gssapi/krb5/disp_name.c index ac576f5b4..79b14f1a9 100644 --- a/src/lib/gssapi/krb5/disp_name.c +++ b/src/lib/gssapi/krb5/disp_name.c @@ -34,6 +34,8 @@ krb5_gss_display_name(minor_status, input_name, output_name_buffer, krb5_context context; krb5_error_code code; char *str; + krb5_gss_name_t k5name = (krb5_gss_name_t) input_name; + gss_OID nametype = (gss_OID) gss_nt_krb5_name; code = krb5_gss_init_context(&context); if (code) { @@ -49,6 +51,11 @@ krb5_gss_display_name(minor_status, input_name, output_name_buffer, krb5_free_context(context); return(GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME); } + if (krb5_princ_type(context, k5name->princ) == KRB5_NT_WELLKNOWN) { + if (krb5_principal_compare(context, k5name->princ, + krb5_anonymous_principal())) + nametype = GSS_C_NT_ANONYMOUS; + } if ((code = krb5_unparse_name(context, ((krb5_gss_name_t) input_name)->princ, @@ -72,6 +79,6 @@ krb5_gss_display_name(minor_status, input_name, output_name_buffer, *minor_status = 0; if (output_name_type) - *output_name_type = (gss_OID) gss_nt_krb5_name; + *output_name_type = (gss_OID) nametype; return(GSS_S_COMPLETE); } diff --git a/src/lib/gssapi/krb5/import_name.c b/src/lib/gssapi/krb5/import_name.c index cd2748b56..cfb75fb22 100644 --- a/src/lib/gssapi/krb5/import_name.c +++ b/src/lib/gssapi/krb5/import_name.c @@ -154,7 +154,16 @@ krb5_gss_import_name(minor_status, input_name_buffer, krb5_free_context(context); return(GSS_S_FAILURE); } - } else { + } else if ((input_name_type != NULL) && + g_OID_equal(input_name_type, GSS_C_NT_ANONYMOUS)) { + code = krb5_copy_principal(context, krb5_anonymous_principal(), &princ); + if (code != 0) { + krb5_free_context(context); + *minor_status = code; + return GSS_S_FAILURE; + } + } + else { #ifndef NO_PASSWORD uid_t uid; struct passwd pwx; diff --git a/src/lib/kadm5/admin.h b/src/lib/kadm5/admin.h index 4196a19e2..8fad11177 100644 --- a/src/lib/kadm5/admin.h +++ b/src/lib/kadm5/admin.h @@ -338,6 +338,13 @@ kadm5_ret_t kadm5_init(krb5_context context, char *client_name, krb5_ui_4 api_version, char **db_args, void **server_handle); +kadm5_ret_t kadm5_init_anonymous(krb5_context context, char *client_name, + char *service_name, + kadm5_config_params *params, + krb5_ui_4 struct_version, + krb5_ui_4 api_version, + char **db_args, + void **server_handle); kadm5_ret_t kadm5_init_with_password(krb5_context context, char *client_name, char *pass, diff --git a/src/lib/kadm5/clnt/client_init.c b/src/lib/kadm5/clnt/client_init.c index 95c4954ef..82033e9fd 100644 --- a/src/lib/kadm5/clnt/client_init.c +++ b/src/lib/kadm5/clnt/client_init.c @@ -59,7 +59,7 @@ #define ADM_CCACHE "/tmp/ovsec_adm.XXXXXX" -enum init_type { INIT_PASS, INIT_SKEY, INIT_CREDS }; +enum init_type { INIT_PASS, INIT_SKEY, INIT_CREDS , INIT_ANONYMOUS}; static kadm5_ret_t _kadm5_init_any(krb5_context context, char *client_name, @@ -129,6 +129,19 @@ kadm5_ret_t kadm5_init_with_password(krb5_context context, char *client_name, api_version, db_args, server_handle); } +kadm5_ret_t kadm5_init_anonymous(krb5_context context, char *client_name, + char *service_name, + kadm5_config_params *params, + krb5_ui_4 struct_version, + krb5_ui_4 api_version, + char **db_args, + void **server_handle) +{ + return _kadm5_init_any(context, client_name, INIT_ANONYMOUS, NULL, NULL, + service_name, params, struct_version, + api_version, db_args, server_handle); +} + kadm5_ret_t kadm5_init(krb5_context context, char *client_name, char *pass, char *service_name, kadm5_config_params *params, @@ -343,7 +356,8 @@ static kadm5_ret_t _kadm5_init_any(krb5_context context, char *client_name, * The RPC connection is open; establish the GSS-API * authentication context. */ - code = kadm5_setup_gss(handle, params_in, client_name, full_svcname); + code = kadm5_setup_gss(handle, params_in, (init_type == INIT_CREDS)?client_name:NULL, + full_svcname); if (code) goto error; @@ -490,7 +504,7 @@ kadm5_get_init_creds(kadm5_server_handle_t handle, full_svcname, full_svcname_len); if ((code == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN || code == KRB5_CC_NOTFOUND) && svcname_in == NULL) { - /* Retry with old host-independent service princpal. */ + /* Retry with old host-independent service principal. */ code = kadm5_gic_iter(handle, init_type, ccache, client, pass, KADM5_ADMIN_SERVICE, realm, @@ -525,7 +539,7 @@ kadm5_gic_iter(kadm5_server_handle_t handle, kadm5_ret_t code; krb5_context ctx; krb5_keytab kt; - krb5_get_init_creds_opt opt; + krb5_get_init_creds_opt *opt = NULL; krb5_creds mcreds, outcreds; int n; @@ -540,29 +554,32 @@ kadm5_gic_iter(kadm5_server_handle_t handle, if (realm) { n = snprintf(full_svcname, full_svcname_len, "%s@%s", svcname, realm); - if (n < 0 || n >= full_svcname_len) + if (n < 0 || n >= (int) full_svcname_len) goto error; } else { /* krb5_princ_realm(client) is not null terminated */ n = snprintf(full_svcname, full_svcname_len, "%s@%.*s", svcname, krb5_princ_realm(ctx, client)->length, krb5_princ_realm(ctx, client)->data); - if (n < 0 || n >= full_svcname_len) + if (n < 0 || n >= (int) full_svcname_len) goto error; } /* Credentials for kadmin don't need to be forwardable or proxiable. */ if (init_type != INIT_CREDS) { - krb5_get_init_creds_opt_init(&opt); - krb5_get_init_creds_opt_set_forwardable(&opt, 0); - krb5_get_init_creds_opt_set_proxiable(&opt, 0); + code = krb5_get_init_creds_opt_alloc(ctx, &opt); + krb5_get_init_creds_opt_set_forwardable(opt, 0); + krb5_get_init_creds_opt_set_proxiable(opt, 0); + krb5_get_init_creds_opt_set_out_ccache(ctx, opt, ccache); + if (init_type == INIT_ANONYMOUS) + krb5_get_init_creds_opt_set_anonymous(opt, 1); } - if (init_type == INIT_PASS) { + if (init_type == INIT_PASS || init_type == INIT_ANONYMOUS) { code = krb5_get_init_creds_password(ctx, &outcreds, client, pass, krb5_prompter_posix, NULL, 0, - full_svcname, &opt); + full_svcname, opt); if (code) goto error; } else if (init_type == INIT_SKEY) { @@ -572,7 +589,7 @@ kadm5_gic_iter(kadm5_server_handle_t handle, goto error; } code = krb5_get_init_creds_keytab(ctx, &outcreds, client, kt, - 0, full_svcname, &opt); + 0, full_svcname, opt); if (pass) krb5_kt_close(ctx, kt); if (code) @@ -588,14 +605,10 @@ kadm5_gic_iter(kadm5_server_handle_t handle, if (code) goto error; } - if (init_type != INIT_CREDS) { - /* Caller has initialized ccache. */ - code = krb5_cc_store_cred(ctx, ccache, &outcreds); - if (code) - goto error; - } error: krb5_free_cred_contents(ctx, &outcreds); + if (opt) + krb5_get_init_creds_opt_free(ctx, opt); return code; } @@ -644,10 +657,13 @@ kadm5_setup_gss(kadm5_server_handle_t handle, goto error; } - buf.value = client_name; - buf.length = strlen((char *)buf.value) + 1; - gssstat = gss_import_name(&minor_stat, &buf, - (gss_OID) gss_nt_krb5_name, &gss_client); + if (client_name) { + buf.value = client_name; + buf.length = strlen((char *)buf.value) + 1; + gssstat = gss_import_name(&minor_stat, &buf, + (gss_OID) gss_nt_krb5_name, &gss_client); + } else gss_client = GSS_C_NO_NAME; + if (gssstat != GSS_S_COMPLETE) { code = KADM5_GSS_ERROR; goto error; diff --git a/src/lib/kadm5/clnt/libkadm5clnt.exports b/src/lib/kadm5/clnt/libkadm5clnt.exports index 617484778..5e81580b1 100644 --- a/src/lib/kadm5/clnt/libkadm5clnt.exports +++ b/src/lib/kadm5/clnt/libkadm5clnt.exports @@ -24,6 +24,7 @@ kadm5_get_principal kadm5_get_principals kadm5_get_privs kadm5_init +kadm5_init_anonymous kadm5_init_krb5_context kadm5_init_with_creds kadm5_init_with_password diff --git a/src/lib/kadm5/srv/libkadm5srv.exports b/src/lib/kadm5/srv/libkadm5srv.exports index 35745be88..d8d3b2283 100644 --- a/src/lib/kadm5/srv/libkadm5srv.exports +++ b/src/lib/kadm5/srv/libkadm5srv.exports @@ -40,6 +40,7 @@ kadm5_get_principal_keys kadm5_get_principals kadm5_get_privs kadm5_init +kadm5_init_anonymous kadm5_init_krb5_context kadm5_init_with_creds kadm5_init_with_password diff --git a/src/lib/kadm5/srv/server_init.c b/src/lib/kadm5/srv/server_init.c index ed71cbf96..557ef0ad4 100644 --- a/src/lib/kadm5/srv/server_init.c +++ b/src/lib/kadm5/srv/server_init.c @@ -104,6 +104,19 @@ kadm5_ret_t kadm5_init_with_password(krb5_context context, char *client_name, server_handle); } +kadm5_ret_t kadm5_init_anonymous(krb5_context context, char *client_name, + char *service_name, + kadm5_config_params *params, + krb5_ui_4 struct_version, + krb5_ui_4 api_version, + char **db_args, + void **server_handle) +{ + return kadm5_init(context, client_name, NULL, service_name, params, + struct_version, api_version, db_args, + server_handle); +} + kadm5_ret_t kadm5_init_with_creds(krb5_context context, char *client_name, krb5_ccache ccache, diff --git a/src/lib/krb5/krb/bld_princ.c b/src/lib/krb5/krb/bld_princ.c index ac2c92a9e..8378599d3 100644 --- a/src/lib/krb5/krb/bld_princ.c +++ b/src/lib/krb5/krb/bld_princ.c @@ -187,3 +187,33 @@ krb5_build_principal(krb5_context context, return retval; } + +/*Anonymous and well known principals*/ +static const char anon_realm_str[] += KRB5_ANONYMOUS_REALMSTR; +static const krb5_data anon_realm_data = { + KV5M_DATA, sizeof(anon_realm_str)-1, + (char *) anon_realm_str}; +static const char wellknown_str[] = KRB5_WELLKNOWN_NAMESTR; +static const char anon_str[] = KRB5_ANONYMOUS_PRINCSTR; +static const krb5_data anon_princ_data[] = { + {KV5M_DATA, sizeof(wellknown_str)-1, (char *) wellknown_str}, + {KV5M_DATA, sizeof(anon_str)-1, (char *)anon_str} +}; + +const krb5_principal_data anon_princ = { + KV5M_PRINCIPAL, + {KV5M_DATA, sizeof(anon_realm_str)-1, (char *) anon_realm_str}, + (krb5_data *) anon_princ_data, 2, KRB5_NT_WELLKNOWN +}; + +const krb5_data * KRB5_CALLCONV +krb5_anonymous_realm() +{ + return &anon_realm_data; +} +krb5_const_principal KRB5_CALLCONV +krb5_anonymous_principal() +{ + return &anon_princ; +} diff --git a/src/lib/krb5/krb/chk_trans.c b/src/lib/krb5/krb/chk_trans.c index 3c014817c..def50885c 100644 --- a/src/lib/krb5/krb/chk_trans.c +++ b/src/lib/krb5/krb/chk_trans.c @@ -315,6 +315,7 @@ krb5_check_transited_list (krb5_context ctx, const krb5_data *trans_in, krb5_data trans; struct check_data cdata; krb5_error_code r; + const krb5_data *anonymous; trans.length = trans_in->length; trans.data = (char *) trans_in->data; @@ -327,6 +328,11 @@ krb5_check_transited_list (krb5_context ctx, const krb5_data *trans_in, (int) srealm->length, srealm->data)); if (trans.length == 0) return 0; + anonymous = krb5_anonymous_realm(); + if (crealm->length == anonymous->length + && (memcmp(crealm->data, anonymous->data, anonymous->length) == 0)) + return 0; /*Nothing to check for anonymous*/ + r = krb5_walk_realm_tree (ctx, crealm, srealm, &cdata.tgs, KRB5_REALM_BRANCH_CHAR); if (r) { diff --git a/src/lib/krb5/krb/get_in_tkt.c b/src/lib/krb5/krb/get_in_tkt.c index 06b3c3874..315bdc943 100644 --- a/src/lib/krb5/krb/get_in_tkt.c +++ b/src/lib/krb5/krb/get_in_tkt.c @@ -283,6 +283,71 @@ cleanup: return (retval); } +/** + * Fully anonymous replies include a pa_pkinit_kx padata type including the KDC + * contribution key. This routine confirms that the session key is of the + * right form for fully anonymous requests. It is here rather than in the + * preauth code because the session key cannot be verified until the AS reply + * is decrypted and the preauth code all runs before the AS reply is decrypted. + */ +static krb5_error_code +verify_anonymous( krb5_context context, krb5_kdc_req *request, + krb5_kdc_rep *reply, krb5_keyblock *as_key) +{ + krb5_error_code ret = 0; + krb5_pa_data *pa; + krb5_data scratch; + krb5_keyblock *kdc_key = NULL, *expected = NULL; + krb5_enc_data *enc = NULL; + krb5_keyblock *session = reply->enc_part2->session; + if (!krb5_principal_compare_any_realm(context, request->client, + krb5_anonymous_principal())) + return 0; /*Only applies to fully anonymous*/ + pa = krb5int_find_pa_data(context, reply->padata, KRB5_PADATA_PKINIT_KX); + if (pa == NULL) + goto verification_error; + scratch.length = pa->length; + scratch.data = (char *) pa->contents; + ret = decode_krb5_enc_data( &scratch, &enc); + if (ret) + goto cleanup; + scratch.data = k5alloc(enc->ciphertext.length, &ret); + if (ret) + goto cleanup; + scratch.length = enc->ciphertext.length; + ret = krb5_c_decrypt(context, as_key, KRB5_KEYUSAGE_PA_PKINIT_KX, + NULL /*cipherstate*/, enc, &scratch); + if (ret) { + free( scratch.data); + goto cleanup; + } + ret = decode_krb5_encryption_key( &scratch, &kdc_key); + zap(scratch.data, scratch.length); + free(scratch.data); + if (ret) + goto cleanup; + ret = krb5_c_fx_cf2_simple( context, kdc_key, "PKINIT", + as_key, "KEYEXCHANGE", &expected); + if (ret) + goto cleanup; + if ((expected->enctype != session->enctype) + || (expected->length != session->length) + || (memcmp(expected->contents, session->contents, expected->length) != 0)) + goto verification_error; +cleanup: + if (kdc_key) + krb5_free_keyblock(context, kdc_key); + if (expected) + krb5_free_keyblock(context, expected); + if (enc) + krb5_free_enc_data(context, enc); + return ret; +verification_error: + ret = KRB5_KDCREP_MODIFIED; + krb5_set_error_message(context, ret, "Reply has wrong form of session key for anonymous request"); + goto cleanup; +} + static krb5_error_code verify_as_reply(krb5_context context, krb5_timestamp time_now, @@ -304,10 +369,14 @@ verify_as_reply(krb5_context context, * principal) and we requested (and received) a TGT. */ canon_req = ((request->kdc_options & KDC_OPT_CANONICALIZE) != 0) || - (krb5_princ_type(context, request->client) == KRB5_NT_ENTERPRISE_PRINCIPAL); + (krb5_princ_type(context, request->client) == KRB5_NT_ENTERPRISE_PRINCIPAL) + || (request->kdc_options & KDC_OPT_REQUEST_ANONYMOUS); if (canon_req) { canon_ok = IS_TGS_PRINC(context, request->server) && IS_TGS_PRINC(context, as_reply->enc_part2->server); + if ((!canon_ok ) && (request->kdc_options &KDC_OPT_REQUEST_ANONYMOUS)) + canon_ok = krb5_principal_compare_any_realm(context, as_reply->client, + krb5_anonymous_principal()); } else canon_ok = 0; @@ -1394,6 +1463,32 @@ krb5_init_creds_init(krb5_context context, ctx->salt.data = NULL; } + /*Anonymous*/ + if(opte->flags & KRB5_GET_INIT_CREDS_OPT_ANONYMOUS) { + ctx->request->kdc_options |= KDC_OPT_REQUEST_ANONYMOUS; + /*Remap @REALM to WELLKNOWN/ANONYMOUS@REALM*/ + if (client->length == 1 && client->data[0].length ==0) { + krb5_principal new_client; + code = krb5_build_principal_ext(context, &new_client, client->realm.length, + client->realm.data, + strlen(KRB5_WELLKNOWN_NAMESTR), + KRB5_WELLKNOWN_NAMESTR, + strlen(KRB5_ANONYMOUS_PRINCSTR), + KRB5_ANONYMOUS_PRINCSTR, + 0); + if (code) + goto cleanup; + krb5_free_principal(context, ctx->request->client); + ctx->request->client = new_client; + krb5_princ_type(context, ctx->request->client) = KRB5_NT_WELLKNOWN; + } + } + /*We will also handle anonymous if the input principal is the anonymous principal*/ + if (krb5_principal_compare_any_realm(context, ctx->request->client, + krb5_anonymous_principal())) { + ctx->request->kdc_options |= KDC_OPT_REQUEST_ANONYMOUS; + krb5_princ_type(context, ctx->request->client) = KRB5_NT_WELLKNOWN; + } code = restart_init_creds_loop(context, ctx, NULL); *pctx = ctx; @@ -1829,6 +1924,10 @@ init_creds_step_reply(krb5_context context, ctx->request, ctx->reply); if (code != 0) goto cleanup; + code = verify_anonymous( context, ctx->request, ctx->reply, + &encrypting_key); + if (code) + goto cleanup; code = stash_as_reply(context, ctx->request_time, ctx->request, ctx->reply, &ctx->cred, NULL); diff --git a/src/lib/krb5/krb/gic_opt.c b/src/lib/krb5/krb/gic_opt.c index d326ac570..c94ee3487 100644 --- a/src/lib/krb5/krb/gic_opt.c +++ b/src/lib/krb5/krb/gic_opt.c @@ -52,6 +52,15 @@ krb5_get_init_creds_opt_set_canonicalize(krb5_get_init_creds_opt *opt, int canon opt->flags &= ~(KRB5_GET_INIT_CREDS_OPT_CANONICALIZE); } +void KRB5_CALLCONV +krb5_get_init_creds_opt_set_anonymous (krb5_get_init_creds_opt *opt, + int anonymous) +{ + if (anonymous) + opt->flags |= KRB5_GET_INIT_CREDS_OPT_ANONYMOUS; + else opt->flags &= ~KRB5_GET_INIT_CREDS_OPT_ANONYMOUS; +} + void KRB5_CALLCONV krb5_get_init_creds_opt_set_etype_list(krb5_get_init_creds_opt *opt, krb5_enctype *etype_list, int etype_list_length) { @@ -149,8 +158,6 @@ krb5int_gic_opte_private_free(krb5_context context, krb5_gic_opt_ext *opte) free_gic_opt_ext_preauth_data(context, opte); if (opte->opt_private->fast_ccache_name) free(opte->opt_private->fast_ccache_name); - if (opte->opt_private->out_ccache) - krb5_cc_close(context, opte->opt_private->out_ccache); free(opte->opt_private); opte->opt_private = NULL; return 0; @@ -504,13 +511,8 @@ krb5_get_init_creds_opt_set_out_ccache(krb5_context context, "krb5_get_init_creds_opt_set_out_ccache"); if (retval) return retval; - if (opte->opt_private->out_ccache) { - krb5_cc_close(context, opte->opt_private->out_ccache); - opte->opt_private->out_ccache = NULL; - } - retval = krb5_cc_resolve(context, krb5_cc_get_name(context, ccache), - &opte->opt_private->out_ccache); - return retval; + opte->opt_private->out_ccache = ccache; + return 0; } krb5_error_code KRB5_CALLCONV diff --git a/src/lib/krb5/libkrb5.exports b/src/lib/krb5/libkrb5.exports index 1c35c4592..e7c191b63 100644 --- a/src/lib/krb5/libkrb5.exports +++ b/src/lib/krb5/libkrb5.exports @@ -109,6 +109,8 @@ krb5_address_compare krb5_address_order krb5_address_search krb5_aname_to_localname +krb5_anonymous_principal +krb5_anonymous_realm krb5_appdefault_boolean krb5_appdefault_string krb5_auth_con_free @@ -337,6 +339,7 @@ krb5_get_init_creds_opt_get_fast_flags 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_anonymous krb5_get_init_creds_opt_set_canonicalize krb5_get_init_creds_opt_set_change_password_prompt krb5_get_init_creds_opt_set_etype_list diff --git a/src/plugins/preauth/fast_factor.h b/src/plugins/preauth/fast_factor.h index 0789c1ad1..52f4fa2e8 100644 --- a/src/plugins/preauth/fast_factor.h +++ b/src/plugins/preauth/fast_factor.h @@ -4,7 +4,7 @@ * Returns success with a null armor_key if FAST is available but not in use. * Returns failure if the client library does not support FAST. */ -static krb5_error_code +static inline krb5_error_code fast_get_armor_key(krb5_context context, preauth_get_client_data_proc get_data, struct _krb5_preauth_client_rock *rock, krb5_keyblock **armor_key) @@ -21,7 +21,7 @@ fast_get_armor_key(krb5_context context, preauth_get_client_data_proc get_data, return retval; } -static krb5_error_code +static inline krb5_error_code fast_kdc_get_armor_key(krb5_context context, preauth_get_entry_data_proc get_entry, krb5_kdc_req *request, @@ -43,7 +43,7 @@ fast_kdc_get_armor_key(krb5_context context, -static krb5_error_code +static inline krb5_error_code fast_kdc_replace_reply_key(krb5_context context, preauth_get_entry_data_proc get_data, krb5_kdc_req *request) @@ -51,7 +51,7 @@ fast_kdc_replace_reply_key(krb5_context context, return 0; } -static krb5_error_code +static inline krb5_error_code fast_set_kdc_verified(krb5_context context, preauth_get_client_data_proc get_data, struct _krb5_preauth_client_rock *rock) diff --git a/src/plugins/preauth/pkinit/pkinit_clnt.c b/src/plugins/preauth/pkinit/pkinit_clnt.c index ba1e4344f..8f17f7e4a 100644 --- a/src/plugins/preauth/pkinit/pkinit_clnt.c +++ b/src/plugins/preauth/pkinit/pkinit_clnt.c @@ -61,7 +61,8 @@ static krb5_error_code pkinit_as_req_create(krb5_context context, pkinit_context plgctx, pkinit_req_context reqctx, krb5_timestamp ctsec, krb5_int32 cusec, krb5_ui_4 nonce, - const krb5_checksum *cksum, krb5_principal server, + const krb5_checksum *cksum, + krb5_principal client, krb5_principal server, krb5_data **as_req); static krb5_error_code @@ -139,7 +140,7 @@ pa_pkinit_gen_req(krb5_context context, nonce = request->nonce; retval = pkinit_as_req_create(context, plgctx, reqctx, ctsec, cusec, - nonce, &cksum, request->server, &out_data); + nonce, &cksum, request->client, request->server, &out_data); if (retval || !out_data->length) { pkiDebug("error %d on pkinit_as_req_create; aborting PKINIT\n", (int) retval); @@ -218,6 +219,7 @@ pkinit_as_req_create(krb5_context context, krb5_int32 cusec, krb5_ui_4 nonce, const krb5_checksum * cksum, + krb5_principal client, krb5_principal server, krb5_data ** as_req) { @@ -344,10 +346,17 @@ pkinit_as_req_create(krb5_context context, retval = ENOMEM; goto cleanup; } - retval = cms_signeddata_create(context, plgctx->cryptoctx, - reqctx->cryptoctx, reqctx->idctx, CMS_SIGN_CLIENT, 1, - (unsigned char *)coded_auth_pack->data, coded_auth_pack->length, - &req->signedAuthPack.data, &req->signedAuthPack.length); + /*For the new protocol, we support anonymous*/ + if (krb5_principal_compare_any_realm(context, client, + krb5_anonymous_principal())) + retval = cms_contentinfo_create(context, plgctx->cryptoctx, + reqctx->cryptoctx, reqctx->idctx, CMS_SIGN_CLIENT, + (unsigned char *)coded_auth_pack->data, coded_auth_pack->length, + &req->signedAuthPack.data, &req->signedAuthPack.length); + else retval = cms_signeddata_create(context, plgctx->cryptoctx, + reqctx->cryptoctx, reqctx->idctx, CMS_SIGN_CLIENT, 1, + (unsigned char *)coded_auth_pack->data, coded_auth_pack->length, + &req->signedAuthPack.data, &req->signedAuthPack.length); #ifdef DEBUG_ASN1 print_buffer_bin((unsigned char *)req->signedAuthPack.data, req->signedAuthPack.length, @@ -640,6 +649,7 @@ pkinit_as_rep_parse(krb5_context context, krb5_data *encoded_request) { krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED; + krb5_principal kdc_princ = NULL; krb5_pa_pk_as_rep *kdc_reply = NULL; krb5_kdc_dh_key_info *kdc_dh = NULL; krb5_reply_key_pack *key_pack = NULL; @@ -677,7 +687,7 @@ pkinit_as_rep_parse(krb5_context context, reqctx->opts->require_crl_checking, kdc_reply->u.dh_Info.dhSignedData.data, kdc_reply->u.dh_Info.dhSignedData.length, - &dh_data.data, &dh_data.length, NULL, NULL)) != 0) { + &dh_data.data, &dh_data.length, NULL, NULL, NULL)) != 0) { pkiDebug("failed to verify pkcs7 signed data\n"); goto cleanup; } @@ -700,8 +710,16 @@ pkinit_as_rep_parse(krb5_context context, retval = -1; goto cleanup; } - - retval = verify_kdc_san(context, plgctx, reqctx, request->server, + retval = krb5_build_principal_ext(context, &kdc_princ, + request->server->realm.length, + request->server->realm.data, + strlen(KRB5_TGS_NAME), KRB5_TGS_NAME, + request->server->realm.length, + request->server->realm.data, + 0); + if (retval) + goto cleanup; + retval = verify_kdc_san(context, plgctx, reqctx, kdc_princ, &valid_san, &need_eku_checking); if (retval) goto cleanup; @@ -850,6 +868,7 @@ pkinit_as_rep_parse(krb5_context context, cleanup: free(dh_data.data); + krb5_free_principal(context, kdc_princ); free(client_key); free_krb5_kdc_dh_key_info(&kdc_dh); free_krb5_pa_pk_as_rep(&kdc_reply); diff --git a/src/plugins/preauth/pkinit/pkinit_crypto.h b/src/plugins/preauth/pkinit/pkinit_crypto.h index 83d2f1e19..dedd8f945 100644 --- a/src/plugins/preauth/pkinit/pkinit_crypto.h +++ b/src/plugins/preauth/pkinit/pkinit_crypto.h @@ -113,6 +113,16 @@ void pkinit_fini_req_crypto(pkinit_req_crypto_context); krb5_error_code pkinit_init_identity_crypto(pkinit_identity_crypto_context *); void pkinit_fini_identity_crypto(pkinit_identity_crypto_context); +/**Create a pkinit ContentInfo*/ +krb5_error_code cms_contentinfo_create + (krb5_context context, /* IN */ + pkinit_plg_crypto_context plg_cryptoctx, /* IN */ + pkinit_req_crypto_context req_cryptoctx, /* IN */ + pkinit_identity_crypto_context id_cryptoctx, /* IN */ + int cms_msg_type, + unsigned char *in_data, unsigned int in_length, + unsigned char **out_data, unsigned int *out_data_len); + /* * this function creates a CMS message where eContentType is SignedData @@ -171,8 +181,9 @@ krb5_error_code cms_signeddata_verify receives required authorization data that contains the verified certificate chain (only used by the KDC) */ - unsigned int *authz_data_len); /* OUT - receives length of authz_data */ + unsigned int *authz_data_len, /* OUT + receives length of authz_data */ + int *is_signed /*out: is message signed*/); /* * this function creates a CMS message where eContentType is EnvelopedData diff --git a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c index 443b0f98c..887ec0627 100644 --- a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c +++ b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c @@ -691,6 +691,102 @@ pkinit_identity_set_prompter(pkinit_identity_crypto_context id_cryptoctx, return 0; } +/*helper function for creating pkinit ContentInfo*/ +static krb5_error_code create_contentinfo +(krb5_context context, pkinit_plg_crypto_context plg_crypto_context, + ASN1_OBJECT *oid, + unsigned char *data, size_t data_len, + PKCS7 **out_p7) +{ + krb5_error_code retval = EINVAL; + PKCS7 *inner_p7; + ASN1_TYPE *pkinit_data = NULL; + *out_p7 = NULL; + if ((inner_p7 = PKCS7_new()) == NULL) + goto cleanup; + if ((pkinit_data = ASN1_TYPE_new()) == NULL) + goto cleanup; + pkinit_data->type = V_ASN1_OCTET_STRING; + if ((pkinit_data->value.octet_string = ASN1_OCTET_STRING_new()) == NULL) + goto cleanup; + if (!ASN1_OCTET_STRING_set(pkinit_data->value.octet_string, (unsigned char *) data, + data_len)) { + unsigned long err = ERR_peek_error(); + retval = KRB5KDC_ERR_PREAUTH_FAILED; + krb5_set_error_message(context, retval, "%s\n", + ERR_error_string(err, NULL)); + pkiDebug("failed to add pkcs7 data\n"); + goto cleanup; + } + if (!PKCS7_set0_type_other(inner_p7, OBJ_obj2nid(oid), pkinit_data)) + goto cleanup; + retval = 0; + *out_p7 = inner_p7; + inner_p7 = NULL; + pkinit_data = NULL; +cleanup: + if (inner_p7) + PKCS7_free(inner_p7); + if (pkinit_data) + ASN1_TYPE_free(pkinit_data); + return retval; +} + +krb5_error_code cms_contentinfo_create +(krb5_context context, /* IN */ + pkinit_plg_crypto_context plg_cryptoctx, /* IN */ + pkinit_req_crypto_context req_cryptoctx, /* IN */ + pkinit_identity_crypto_context id_cryptoctx, /* IN */ + int cms_msg_type, + unsigned char *data, unsigned int data_len, + unsigned char **out_data, unsigned int *out_data_len) +{ + krb5_error_code retval = ENOMEM; + ASN1_OBJECT *oid = NULL; + PKCS7 *p7 = NULL; + unsigned char *p; + /* pick the correct oid for the eContentInfo */ + oid = pkinit_pkcs7type2oid(plg_cryptoctx, cms_msg_type); + if (oid == NULL) + goto cleanup; + retval = create_contentinfo(context, plg_cryptoctx, oid, + data, data_len, &p7); + if (retval != 0) + goto cleanup; + *out_data_len = i2d_PKCS7(p7, NULL); + if (!(*out_data_len)) { + unsigned long err = ERR_peek_error(); + retval = KRB5KDC_ERR_PREAUTH_FAILED; + krb5_set_error_message(context, retval, "%s\n", + ERR_error_string(err, NULL)); + pkiDebug("failed to der encode pkcs7\n"); + goto cleanup; + } + retval = ENOMEM; + if ((p = *out_data = malloc(*out_data_len)) == NULL) + goto cleanup; + + /* DER encode PKCS7 data */ + retval = i2d_PKCS7(p7, &p); + if (!retval) { + unsigned long err = ERR_peek_error(); + retval = KRB5KDC_ERR_PREAUTH_FAILED; + krb5_set_error_message(context, retval, "%s\n", + ERR_error_string(err, NULL)); + pkiDebug("failed to der encode pkcs7\n"); + goto cleanup; + } + retval = 0; +cleanup: + if (p7) + PKCS7_free(p7); + if (oid) + ASN1_OBJECT_free(oid); + return retval; +} + + + krb5_error_code cms_signeddata_create(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, @@ -708,7 +804,6 @@ cms_signeddata_create(krb5_context context, PKCS7_SIGNED *p7s = NULL; PKCS7_SIGNER_INFO *p7si = NULL; unsigned char *p; - ASN1_TYPE *pkinit_data = NULL; STACK_OF(X509) * cert_stack = NULL; ASN1_OCTET_STRING *digest_attr = NULL; EVP_MD_CTX ctx, ctx2; @@ -726,7 +821,11 @@ cms_signeddata_create(krb5_context context, X509 *cert = NULL; ASN1_OBJECT *oid = NULL; - /* start creating PKCS7 data */ + if (id_cryptoctx->my_certs == NULL) { + krb5_set_error_message(context, EINVAL, "cms_signdata_create called with no certificates"); + return EINVAL; + } +/* start creating PKCS7 data */ if ((p7 = PKCS7_new()) == NULL) goto cleanup; p7->type = OBJ_nid2obj(NID_pkcs7_signed); @@ -751,7 +850,7 @@ cms_signeddata_create(krb5_context context, X509_STORE_CTX certctx; STACK_OF(X509) *certstack = NULL; char buf[DN_BUF_LEN]; - int i = 0, size = 0; + unsigned int i = 0, size = 0; if ((certstore = X509_STORE_new()) == NULL) goto cleanup; @@ -939,26 +1038,8 @@ cms_signeddata_create(krb5_context context, goto cleanup2; /* start on adding data to the pkcs7 signed */ - if ((inner_p7 = PKCS7_new()) == NULL) - goto cleanup2; - if ((pkinit_data = ASN1_TYPE_new()) == NULL) - goto cleanup2; - pkinit_data->type = V_ASN1_OCTET_STRING; - if ((pkinit_data->value.octet_string = ASN1_OCTET_STRING_new()) == NULL) - goto cleanup2; - if (!ASN1_OCTET_STRING_set(pkinit_data->value.octet_string, data, - (int)data_len)) { - unsigned long err = ERR_peek_error(); - retval = KRB5KDC_ERR_PREAUTH_FAILED; - krb5_set_error_message(context, retval, "%s\n", - ERR_error_string(err, NULL)); - pkiDebug("failed to add pkcs7 data\n"); - goto cleanup2; - } - - if (!PKCS7_set0_type_other(inner_p7, OBJ_obj2nid(oid), pkinit_data)) - goto cleanup2; - + retval = create_contentinfo(context, plg_cryptoctx, oid, + data, data_len, &inner_p7); if (p7s->contents != NULL) PKCS7_free(p7s->contents); p7s->contents = inner_p7; @@ -972,6 +1053,7 @@ cms_signeddata_create(krb5_context context, pkiDebug("failed to der encode pkcs7\n"); goto cleanup2; } + retval = ENOMEM; if ((p = *signed_data = malloc(*signed_data_len)) == NULL) goto cleanup2; @@ -1038,12 +1120,14 @@ cms_signeddata_verify(krb5_context context, unsigned char **data, unsigned int *data_len, unsigned char **authz_data, - unsigned int *authz_data_len) + unsigned int *authz_data_len, + int *is_signed) { krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED; PKCS7 *p7 = NULL; BIO *out = NULL; - int flags = PKCS7_NOVERIFY, i = 0; + int flags = PKCS7_NOVERIFY; + unsigned int i = 0; unsigned int vflags = 0, size = 0; const unsigned char *p = signed_data; STACK_OF(PKCS7_SIGNER_INFO) *si_sk = NULL; @@ -1063,7 +1147,8 @@ cms_signeddata_verify(krb5_context context, print_buffer_bin(signed_data, signed_data_len, "/tmp/client_received_pkcs7_signeddata"); #endif - + if (is_signed) + *is_signed = 1; /* Do this early enough to create the shadow OID for pkcs7-data if needed */ oid = pkinit_pkcs7type2oid(plgctx, cms_msg_type); if (oid == NULL) @@ -1079,13 +1164,33 @@ cms_signeddata_verify(krb5_context context, goto cleanup; } - /* verify that the received message is PKCS7 SignedData message */ - if (OBJ_obj2nid(p7->type) != NID_pkcs7_signed) { - pkiDebug("Expected id-signedData PKCS7 msg (received type = %d)\n", - OBJ_obj2nid(p7->type)); - krb5_set_error_message(context, retval, "wrong oid\n"); - goto cleanup; - } +/*Handle the case in pkinit anonymous where we get unsigned data.*/ + if (is_signed && !OBJ_cmp( p7->type, oid)) { + unsigned char *d; + *is_signed = 0; + if (p7->d.other->type != V_ASN1_OCTET_STRING) { + retval = KRB5KDC_ERR_PREAUTH_FAILED; + krb5_set_error_message(context, KRB5KDC_ERR_PREAUTH_FAILED, "Invalid pkinit packet: octet string expected"); + goto cleanup; + } + *data_len = ASN1_STRING_length(p7->d.other->value.octet_string); + d = malloc(*data_len); + if (d == NULL) { + retval = ENOMEM; + goto cleanup; + } + memcpy(d, ASN1_STRING_data(p7->d.other->value.octet_string), + *data_len); + *data = d; + goto out; + } else /* verify that the received message is PKCS7 SignedData message */ + if (OBJ_obj2nid(p7->type) != NID_pkcs7_signed) { + + pkiDebug("Expected id-signedData PKCS7 msg (received type = %d)\n", + OBJ_obj2nid(p7->type)); + krb5_set_error_message(context, retval, "wrong oid\n"); + goto cleanup; + } /* setup to verify X509 certificate used to sign PKCS7 message */ if (!(store = X509_STORE_new())) @@ -1285,13 +1390,15 @@ cms_signeddata_verify(krb5_context context, /* transfer the data from PKCS7 message into return buffer */ for (size = 0;;) { + int remain; + retval = ENOMEM; if ((*data = realloc(*data, size + 1024 * 10)) == NULL) goto cleanup; - i = BIO_read(out, &((*data)[size]), 1024 * 10); - if (i <= 0) + remain = BIO_read(out, &((*data)[size]), 1024 * 10); + if (remain <= 0) break; else - size += i; + size += remain; } *data_len = size; @@ -1617,7 +1724,7 @@ cms_envelopeddata_verify(krb5_context context, id_cryptoctx, msg_type, require_crl_checking, vfy_buf, vfy_buf_len, - data, data_len, NULL, NULL); + data, data_len, NULL, NULL, NULL); if (!retval) pkiDebug("PKCS7 Verification Success\n"); @@ -1655,7 +1762,7 @@ crypto_retrieve_X509_sans(krb5_context context, krb5_principal *princs = NULL; krb5_principal *upns = NULL; unsigned char **dnss = NULL; - int i, num_found = 0; + unsigned int i, num_found = 0; if (princs_ret == NULL && upn_ret == NULL && dns_ret == NULL) { pkiDebug("%s: nowhere to return any values!\n", __FUNCTION__); @@ -3220,7 +3327,7 @@ static krb5_error_code pkinit_open_session(krb5_context context, pkinit_identity_crypto_context cctx) { - int i, r; + CK_ULONG i, r; unsigned char *cp; CK_ULONG count = 0; CK_SLOT_ID_PTR slotlist; @@ -3414,7 +3521,7 @@ pkinit_C_Decrypt(pkinit_identity_crypto_context id_cryptoctx, rv = id_cryptoctx->p11->C_Decrypt(id_cryptoctx->session, pEncryptedData, ulEncryptedDataLen, pData, pulDataLen); if (rv == CKR_OK) { - pkiDebug("pData %x *pulDataLen %d\n", (int) pData, (int) *pulDataLen); + pkiDebug("pData %x *pulDataLen %d\n", (unsigned int) pData, (int) *pulDataLen); } return rv; } @@ -4451,7 +4558,7 @@ X509_NAME_oneline_ex(X509_NAME * a, out = BIO_new(BIO_s_mem ()); if (X509_NAME_print_ex(out, a, 0, flag) > 0) { - if (buf != NULL && *size > (int) BIO_number_written(out)) { + if (buf != NULL && (int)(*size) > BIO_number_written(out)) { memset(buf, 0, *size); BIO_read(out, buf, (int) BIO_number_written(out)); } @@ -5459,7 +5566,7 @@ pkcs7_dataDecode(krb5_context context, if (EVP_CIPHER_asn1_to_param(evp_ctx,enc_alg->parameter) < 0) goto cleanup; - if (jj != EVP_CIPHER_CTX_key_length(evp_ctx)) { + if ((unsigned) jj != EVP_CIPHER_CTX_key_length(evp_ctx)) { /* Some S/MIME clients don't use the same key * and effective key length. The key length is * determined by the size of the decrypted RSA key. diff --git a/src/plugins/preauth/pkinit/pkinit_identity.c b/src/plugins/preauth/pkinit/pkinit_identity.c index aef039361..aecea2c78 100644 --- a/src/plugins/preauth/pkinit/pkinit_identity.c +++ b/src/plugins/preauth/pkinit/pkinit_identity.c @@ -505,65 +505,67 @@ pkinit_identity_initialize(krb5_context context, int i; pkiDebug("%s: %p %p %p\n", __FUNCTION__, context, idopts, id_cryptoctx); - if (idopts == NULL || id_cryptoctx == NULL) - goto errout; - - /* - * If identity was specified, use that. (For the kdc, this - * is specified as pkinit_identity in the kdc.conf. For users, - * this is specified on the command line via X509_user_identity.) - * If a user did not specify identity on the command line, - * then we will try alternatives which may have been specified - * in the config file. - */ - if (idopts->identity != NULL) { - retval = process_option_identity(context, plg_cryptoctx, req_cryptoctx, - idopts, id_cryptoctx, - idopts->identity); - } else if (idopts->identity_alt != NULL) { - for (i = 0; retval != 0 && idopts->identity_alt[i] != NULL; i++) - retval = process_option_identity(context, plg_cryptoctx, - req_cryptoctx, idopts, - id_cryptoctx, - idopts->identity_alt[i]); - } else { - pkiDebug("%s: no user identity options specified\n", __FUNCTION__); - goto errout; - } - if (retval) - goto errout; - - retval = crypto_load_certs(context, plg_cryptoctx, req_cryptoctx, - idopts, id_cryptoctx, princ); - if (retval) - goto errout; + if (!(princ && krb5_principal_compare_any_realm (context, princ, krb5_anonymous_principal()))) { + if (idopts == NULL || id_cryptoctx == NULL) + goto errout; - if (do_matching) { - retval = pkinit_cert_matching(context, plg_cryptoctx, req_cryptoctx, - id_cryptoctx, princ); - if (retval) { - pkiDebug("%s: No matching certificate found\n", __FUNCTION__); - crypto_free_cert_info(context, plg_cryptoctx, req_cryptoctx, - id_cryptoctx); + /* + * If identity was specified, use that. (For the kdc, this + * is specified as pkinit_identity in the kdc.conf. For users, + * this is specified on the command line via X509_user_identity.) + * If a user did not specify identity on the command line, + * then we will try alternatives which may have been specified + * in the config file. + */ + if (idopts->identity != NULL) { + retval = process_option_identity(context, plg_cryptoctx, req_cryptoctx, + idopts, id_cryptoctx, + idopts->identity); + } else if (idopts->identity_alt != NULL) { + for (i = 0; retval != 0 && idopts->identity_alt[i] != NULL; i++) + retval = process_option_identity(context, plg_cryptoctx, + req_cryptoctx, idopts, + id_cryptoctx, + idopts->identity_alt[i]); + } else { + pkiDebug("%s: no user identity options specified\n", __FUNCTION__); goto errout; } - } else { - /* Tell crypto code to use the "default" */ - retval = crypto_cert_select_default(context, plg_cryptoctx, - req_cryptoctx, id_cryptoctx); - if (retval) { - pkiDebug("%s: Failed while selecting default certificate\n", - __FUNCTION__); - crypto_free_cert_info(context, plg_cryptoctx, req_cryptoctx, - id_cryptoctx); + if (retval) + goto errout; + + retval = crypto_load_certs(context, plg_cryptoctx, req_cryptoctx, + idopts, id_cryptoctx, princ); + if (retval) goto errout; + + if (do_matching) { + retval = pkinit_cert_matching(context, plg_cryptoctx, req_cryptoctx, + id_cryptoctx, princ); + if (retval) { + pkiDebug("%s: No matching certificate found\n", __FUNCTION__); + crypto_free_cert_info(context, plg_cryptoctx, req_cryptoctx, + id_cryptoctx); + goto errout; + } + } else { + /* Tell crypto code to use the "default" */ + retval = crypto_cert_select_default(context, plg_cryptoctx, + req_cryptoctx, id_cryptoctx); + if (retval) { + pkiDebug("%s: Failed while selecting default certificate\n", + __FUNCTION__); + crypto_free_cert_info(context, plg_cryptoctx, req_cryptoctx, + id_cryptoctx); + goto errout; + } } - } - retval = crypto_free_cert_info(context, plg_cryptoctx, req_cryptoctx, - id_cryptoctx); - if (retval) - goto errout; + retval = crypto_free_cert_info(context, plg_cryptoctx, req_cryptoctx, + id_cryptoctx); + if (retval) + goto errout; + } /*not anonymous principal*/ for (i = 0; idopts->anchors != NULL && idopts->anchors[i] != NULL; i++) { retval = process_option_ca_crl(context, plg_cryptoctx, req_cryptoctx, diff --git a/src/plugins/preauth/pkinit/pkinit_lib.c b/src/plugins/preauth/pkinit/pkinit_lib.c index e665b6cb7..a6d7762fb 100644 --- a/src/plugins/preauth/pkinit/pkinit_lib.c +++ b/src/plugins/preauth/pkinit/pkinit_lib.c @@ -424,7 +424,7 @@ pkinit_copy_krb5_octet_data(krb5_octet_data *dst, const krb5_octet_data *src) void print_buffer(unsigned char *buf, unsigned int len) { - int i = 0; + unsigned i = 0; if (len <= 0) return; @@ -437,7 +437,7 @@ void print_buffer_bin(unsigned char *buf, unsigned int len, char *filename) { FILE *f = NULL; - int i = 0; + unsigned int i = 0; if (len <= 0 || filename == NULL) return; diff --git a/src/plugins/preauth/pkinit/pkinit_srv.c b/src/plugins/preauth/pkinit/pkinit_srv.c index adfcb9593..34700ad18 100644 --- a/src/plugins/preauth/pkinit/pkinit_srv.c +++ b/src/plugins/preauth/pkinit/pkinit_srv.c @@ -300,7 +300,6 @@ pkinit_server_verify_padata(krb5_context context, { krb5_error_code retval = 0; krb5_octet_data authp_data = {0, 0, NULL}, krb5_authz = {0, 0, NULL}; - krb5_data *encoded_pkinit_authz_data = NULL; krb5_pa_pk_as_req *reqp = NULL; krb5_pa_pk_as_req_draft9 *reqp9 = NULL; krb5_auth_pack *auth_pack = NULL; @@ -311,9 +310,9 @@ pkinit_server_verify_padata(krb5_context context, krb5_checksum cksum = {0, 0, 0, NULL}; krb5_data *der_req = NULL; int valid_eku = 0, valid_san = 0; - krb5_authdata **my_authz_data = NULL, *pkinit_authz_data = NULL; krb5_kdc_req *tmp_as_req = NULL; krb5_data k5data; + int is_signed = 1; krb5_keyblock *armor_key; pkiDebug("pkinit_verify_padata: entered!\n"); @@ -367,7 +366,7 @@ pkinit_server_verify_padata(krb5_context context, plgctx->opts->require_crl_checking, reqp->signedAuthPack.data, reqp->signedAuthPack.length, &authp_data.data, &authp_data.length, &krb5_authz.data, - &krb5_authz.length); + &krb5_authz.length, &is_signed); break; case KRB5_PADATA_PK_AS_REP_OLD: case KRB5_PADATA_PK_AS_REQ_OLD: @@ -389,7 +388,7 @@ pkinit_server_verify_padata(krb5_context context, plgctx->opts->require_crl_checking, reqp9->signedAuthPack.data, reqp9->signedAuthPack.length, &authp_data.data, &authp_data.length, &krb5_authz.data, - &krb5_authz.length); + &krb5_authz.length, NULL); break; default: pkiDebug("unrecognized pa_type = %d\n", data->pa_type); @@ -400,28 +399,35 @@ pkinit_server_verify_padata(krb5_context context, pkiDebug("pkcs7_signeddata_verify failed\n"); goto cleanup; } + if (is_signed) { - retval = verify_client_san(context, plgctx, reqctx, request->client, - &valid_san); - if (retval) - goto cleanup; - if (!valid_san) { - pkiDebug("%s: did not find an acceptable SAN in user certificate\n", - __FUNCTION__); - retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH; - goto cleanup; - } - retval = verify_client_eku(context, plgctx, reqctx, &valid_eku); - if (retval) - goto cleanup; + retval = verify_client_san(context, plgctx, reqctx, request->client, + &valid_san); + if (retval) + goto cleanup; + if (!valid_san) { + pkiDebug("%s: did not find an acceptable SAN in user certificate\n", + __FUNCTION__); + retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH; + goto cleanup; + } + retval = verify_client_eku(context, plgctx, reqctx, &valid_eku); + if (retval) + goto cleanup; - if (!valid_eku) { - pkiDebug("%s: did not find an acceptable EKU in user certificate\n", - __FUNCTION__); - retval = KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE; - goto cleanup; + if (!valid_eku) { + pkiDebug("%s: did not find an acceptable EKU in user certificate\n", + __FUNCTION__); + retval = KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE; + goto cleanup; + } + } else { /*!is_signed*/ + if (!krb5_principal_compare( context, request->client, krb5_anonymous_principal())) { + retval = KRB5KDC_ERR_PREAUTH_FAILED; + krb5_set_error_message(context, retval, "Pkinit request not signed, but client not anonymous."); + goto cleanup; + } } - #ifdef DEBUG_ASN1 print_buffer_bin(authp_data.data, authp_data.length, "/tmp/kdc_auth_pack"); #endif @@ -446,6 +452,11 @@ pkinit_server_verify_padata(krb5_context context, pkiDebug("bad dh parameters\n"); goto cleanup; } + } else if (!is_signed) { + /*Anonymous pkinit requires DH*/ + retval = KRB5KDC_ERR_PREAUTH_FAILED; + krb5_set_error_message(context, retval, "Anonymous pkinit without DH public value not supported."); + goto cleanup; } /* * The KDC may have modified the request after decoding it. @@ -536,64 +547,11 @@ pkinit_server_verify_padata(krb5_context context, /* return authorization data to be included in the ticket */ switch ((int)data->pa_type) { - case KRB5_PADATA_PK_AS_REQ: - my_authz_data = malloc(2 * sizeof(*my_authz_data)); - if (my_authz_data == NULL) { - retval = ENOMEM; - pkiDebug("Couldn't allocate krb5_authdata ptr array\n"); - goto cleanup; - } - my_authz_data[1] = NULL; - my_authz_data[0] = malloc(sizeof(krb5_authdata)); - if (my_authz_data[0] == NULL) { - retval = ENOMEM; - pkiDebug("Couldn't allocate krb5_authdata\n"); - free(my_authz_data); - goto cleanup; - } - /* AD-INITIAL-VERIFIED-CAS must be wrapped in AD-IF-RELEVANT */ - my_authz_data[0]->magic = KV5M_AUTHDATA; - my_authz_data[0]->ad_type = KRB5_AUTHDATA_IF_RELEVANT; - - /* create an internal AD-INITIAL-VERIFIED-CAS data */ - pkinit_authz_data = malloc(sizeof(krb5_authdata)); - if (pkinit_authz_data == NULL) { - retval = ENOMEM; - pkiDebug("Couldn't allocate krb5_authdata\n"); - free(my_authz_data[0]); - free(my_authz_data); - goto cleanup; - } - pkinit_authz_data->ad_type = KRB5_AUTHDATA_INITIAL_VERIFIED_CAS; - /* content of this ad-type contains the certification - path with which the client certificate was validated - */ - pkinit_authz_data->contents = krb5_authz.data; - pkinit_authz_data->length = krb5_authz.length; - retval = k5int_encode_krb5_authdata_elt(pkinit_authz_data, - &encoded_pkinit_authz_data); -#ifdef DEBUG_ASN1 - print_buffer_bin((unsigned char *)encoded_pkinit_authz_data->data, - encoded_pkinit_authz_data->length, - "/tmp/kdc_pkinit_authz_data"); -#endif - free(pkinit_authz_data); - if (retval) { - pkiDebug("k5int_encode_krb5_authdata_elt failed\n"); - free(my_authz_data[0]); - free(my_authz_data); - goto cleanup; - } - - my_authz_data[0]->contents = - (krb5_octet *) encoded_pkinit_authz_data->data; - my_authz_data[0]->length = encoded_pkinit_authz_data->length; - *authz_data = my_authz_data; - pkiDebug("Returning %d bytes of authorization data\n", - krb5_authz.length); - encoded_pkinit_authz_data->data = NULL; /* Don't free during cleanup*/ - free(encoded_pkinit_authz_data); - break; +/* + * This code used to generate ad-initial-verified-cas authorization data. + * However that has been removed until the ad-kdc-issued discussion can happen + * in the working group. Dec 2009 + */ default: *authz_data = NULL; } @@ -634,6 +592,67 @@ cleanup: return retval; } +static krb5_error_code +return_pkinit_kx( krb5_context context, krb5_kdc_req *request, krb5_kdc_rep *reply, + krb5_keyblock *encrypting_key, + krb5_pa_data **out_padata) +{ + krb5_error_code ret = 0; + krb5_keyblock *session = reply->ticket->enc_part2->session; + krb5_keyblock *new_session = NULL; + krb5_pa_data *pa = NULL; + krb5_enc_data enc; + krb5_data *scratch = NULL; + *out_padata = NULL; + enc.ciphertext.data = NULL; + if (!krb5_principal_compare(context, request->client, + krb5_anonymous_principal())) + return 0; + /* + *The KDC contribution key needs to be a fresh key of an + *enctype supported by the client and server. The existing + *session key meets these requirements so we use itt. + */ + ret = krb5_c_fx_cf2_simple(context, session, "PKINIT", + encrypting_key, "KEYEXCHANGE", + &new_session); + if (ret) + goto cleanup; + ret = encode_krb5_encryption_key( session, &scratch); + if (ret) + goto cleanup; + ret = krb5_encrypt_helper( context, encrypting_key, KRB5_KEYUSAGE_PA_PKINIT_KX, + scratch, &enc); + if (ret) + goto cleanup; + memset(scratch->data, 0, scratch->length); + krb5_free_data(context, scratch); + scratch = NULL; + ret = encode_krb5_enc_data(&enc, &scratch); + if (ret) + goto cleanup; + pa = malloc(sizeof(krb5_pa_data)); + if (pa == NULL) { + ret = ENOMEM; + goto cleanup; + } + if (ret) + goto cleanup; + pa->pa_type = KRB5_PADATA_PKINIT_KX; + pa->length = scratch->length; + pa->contents = (krb5_octet *) scratch->data; + *out_padata = pa; + scratch->data = NULL; + memset(session->contents, 0, session->length); + krb5_free_keyblock_contents(context, session); + *session = *new_session; + new_session->contents = NULL; +cleanup: + krb5_free_data_contents(context, &enc.ciphertext); + krb5_free_keyblock(context, new_session); + krb5_free_data(context, scratch); + return ret; +} static krb5_error_code pkinit_server_return_padata(krb5_context context, @@ -680,6 +699,9 @@ pkinit_server_return_padata(krb5_context context, int fixed_keypack = 0; *send_pa = NULL; + if (padata->pa_type == KRB5_PADATA_PKINIT_KX) + return return_pkinit_kx(context, request, reply, + encrypting_key, send_pa); if (padata == NULL || padata->length <= 0 || padata->contents == NULL) return 0; @@ -1037,6 +1059,8 @@ cleanup: static int pkinit_server_get_flags(krb5_context kcontext, krb5_preauthtype patype) { + if (patype == KRB5_PADATA_PKINIT_KX) + return PA_PSEUDO; return PA_SUFFICIENT | PA_REPLACES_KEY; } @@ -1044,6 +1068,7 @@ static krb5_preauthtype supported_server_pa_types[] = { KRB5_PADATA_PK_AS_REQ, KRB5_PADATA_PK_AS_REQ_OLD, KRB5_PADATA_PK_AS_REP_OLD, + KRB5_PADATA_PKINIT_KX, 0 }; @@ -1238,7 +1263,7 @@ pkinit_server_plugin_init(krb5_context context, void **blob, { krb5_error_code retval = ENOMEM; pkinit_kdc_context plgctx, *realm_contexts = NULL; - int i, j; + size_t i, j; size_t numrealms; retval = pkinit_accessor_init(); -- 2.26.2