From: Greg Hudson Date: Fri, 30 Apr 2010 21:22:48 +0000 (+0000) Subject: Add IAKERB mechanism and gss_acquire_cred_with_password X-Git-Tag: krb5-1.9-beta1~257 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=baea9a7a27d781581505f0bb6d0ac4e4f24053aa;p=krb5.git Add IAKERB mechanism and gss_acquire_cred_with_password Merge branches/iakerb to trunk. Includes the following: * New IAKERB mechanism. * New gss_acquire_cred_with_password mechglue function. * ASN.1 encoders and decoders for IAKERB structures (with tests). * New shortcuts in gss-sample client and server. * Tests to exercise SPNEGO and IAKERB using gss-sample application. ticket: 6712 git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@23960 dc483132-0cff-0310-8789-dd5450dbe970 --- diff --git a/src/appl/gss-sample/gss-client.c b/src/appl/gss-sample/gss-client.c index ad314c270..d922cc3bd 100644 --- a/src/appl/gss-sample/gss-client.c +++ b/src/appl/gss-sample/gss-client.c @@ -64,6 +64,8 @@ #endif #include +#include +#include #include "gss-misc.h" static int verbose = 1; @@ -72,7 +74,7 @@ static void usage() { fprintf(stderr, "Usage: gss-client [-port port] [-mech mechanism] [-d]\n"); - fprintf(stderr, " [-seq] [-noreplay] [-nomutual]"); + fprintf(stderr, " [-seq] [-noreplay] [-nomutual] [-user user] [-pass pw]"); #ifdef _WIN32 fprintf(stderr, " [-threads num]"); #endif @@ -162,6 +164,7 @@ connect_to_server(char *host, u_short port) static int client_establish_context(int s, char *service_name, OM_uint32 gss_flags, int auth_flag, int v1_format, gss_OID oid, + char *username, char *password, gss_ctx_id_t *gss_context, OM_uint32 *ret_flags) { if (auth_flag) { @@ -169,6 +172,53 @@ client_establish_context(int s, char *service_name, OM_uint32 gss_flags, gss_name_t target_name; OM_uint32 maj_stat, min_stat, init_sec_min_stat; int token_flags; + gss_cred_id_t cred = GSS_C_NO_CREDENTIAL; + gss_name_t gss_username = GSS_C_NO_NAME; + gss_OID_set_desc mechs, *mechsp = GSS_C_NO_OID_SET; + + if (oid != GSS_C_NO_OID) { + mechs.elements = oid; + mechs.count = 1; + mechsp = &mechs; + } + + if (username != NULL) { + send_tok.value = username; + send_tok.length = strlen(username); + + maj_stat = gss_import_name(&min_stat, &send_tok, + (gss_OID) gss_nt_user_name, + &gss_username); + if (maj_stat != GSS_S_COMPLETE) { + display_status("parsing client name", maj_stat, min_stat); + return -1; + } + } + + if (password != NULL) { + gss_buffer_desc pwbuf; + + pwbuf.value = password; + pwbuf.length = strlen(password); + + maj_stat = gss_acquire_cred_with_password(&min_stat, + gss_username, + &pwbuf, 0, + mechsp, GSS_C_INITIATE, + &cred, NULL, NULL); + } else if (gss_username != GSS_C_NO_NAME) { + maj_stat = gss_acquire_cred(&min_stat, + gss_username, 0, + mechsp, GSS_C_INITIATE, + &cred, NULL, NULL); + } else + maj_stat = GSS_S_COMPLETE; + if (maj_stat != GSS_S_COMPLETE) { + display_status("acquiring creds", maj_stat, min_stat); + gss_release_name(&min_stat, &gss_username); + return -1; + } + gss_release_name(&min_stat, &gss_username); /* * Import the name into target_name. Use send_tok to save @@ -213,7 +263,7 @@ client_establish_context(int s, char *service_name, OM_uint32 gss_flags, do { maj_stat = gss_init_sec_context(&init_sec_min_stat, - GSS_C_NO_CREDENTIAL, gss_context, + cred, gss_context, target_name, oid, gss_flags, 0, NULL, /* channel bindings */ token_ptr, NULL, /* mech type */ @@ -260,6 +310,7 @@ client_establish_context(int s, char *service_name, OM_uint32 gss_flags, printf("\n"); } while (maj_stat == GSS_S_CONTINUE_NEEDED); + (void) gss_release_cred(&min_stat, &cred); (void) gss_release_name(&min_stat, &target_name); } else { if (send_token(s, TOKEN_NOOP, empty_token) < 0) @@ -344,7 +395,7 @@ read_file(file_name, in_buf) static int call_server(host, port, oid, service_name, gss_flags, auth_flag, wrap_flag, encrypt_flag, mic_flag, v1_format, msg, use_file, - mcount) + mcount, username, password) char *host; u_short port; gss_OID oid; @@ -355,6 +406,8 @@ call_server(host, port, oid, service_name, gss_flags, auth_flag, char *msg; int use_file; int mcount; + char *username; + char *password; { gss_ctx_id_t context; gss_buffer_desc in_buf, out_buf; @@ -380,7 +433,8 @@ call_server(host, port, oid, service_name, gss_flags, auth_flag, /* Establish context */ if (client_establish_context(s, service_name, gss_flags, auth_flag, - v1_format, oid, &context, &ret_flags) < 0) { + v1_format, oid, username, password, + &context, &ret_flags) < 0) { (void) close(s); return -1; } @@ -663,13 +717,15 @@ static OM_uint32 min_stat; static gss_OID oid = GSS_C_NULL_OID; static int mcount = 1, ccount = 1; static int auth_flag, wrap_flag, encrypt_flag, mic_flag, v1_format; +static char *username = NULL; +static char *password = NULL; static void worker_bee(void *unused) { if (call_server(server_host, port, oid, service_name, gss_flags, auth_flag, wrap_flag, encrypt_flag, mic_flag, - v1_format, msg, use_file, mcount) < 0) + v1_format, msg, use_file, mcount, username, password) < 0) exit(1); #ifdef _WIN32 @@ -705,17 +761,33 @@ main(argc, argv) if (!argc) usage(); mechanism = *argv; - } + } else if (strcmp(*argv, "-user") == 0) { + argc--; + argv++; + if (!argc) + usage(); + username = *argv; + } else if (strcmp(*argv, "-pass") == 0) { + argc--; + argv++; + if (!argc) + usage(); + password = *argv; + } else if (strcmp(*argv, "-iakerb") == 0) { + mechanism = "{ 1 3 6 1 5 2 5 }"; + } else if (strcmp(*argv, "-spnego") == 0) { + mechanism = "{ 1 3 6 1 5 5 2 }"; + } else if (strcmp(*argv, "-krb5") == 0) { + mechanism = "{ 1 3 5 1 5 2 }"; #ifdef _WIN32 - else if (strcmp(*argv, "-threads") == 0) { + } else if (strcmp(*argv, "-threads") == 0) { argc--; argv++; if (!argc) usage(); max_threads = atoi(*argv); - } #endif - else if (strcmp(*argv, "-d") == 0) { + } else if (strcmp(*argv, "-d") == 0) { gss_flags |= GSS_C_DELEG_FLAG; } else if (strcmp(*argv, "-seq") == 0) { gss_flags |= GSS_C_SEQUENCE_FLAG; diff --git a/src/appl/gss-sample/gss-server.c b/src/appl/gss-sample/gss-server.c index 8b59eb276..0ddfaeee8 100644 --- a/src/appl/gss-sample/gss-server.c +++ b/src/appl/gss-sample/gss-server.c @@ -58,6 +58,7 @@ #include #include +#include #include "gss-misc.h" #ifdef HAVE_STRING_H @@ -75,7 +76,8 @@ usage() #endif fprintf(stderr, "\n"); fprintf(stderr, - " [-inetd] [-export] [-logfile file] service_name\n"); + " [-inetd] [-export] [-logfile file] [-keytab keytab]\n" + " service_name\n"); exit(1); } @@ -690,6 +692,15 @@ main(int argc, char **argv) exit(1); } } + } else if (strcmp(*argv, "-keytab") == 0) { + argc--; + argv++; + if (!argc) + usage(); + if (krb5_gss_register_acceptor_identity(*argv)) { + fprintf(stderr, "failed to register keytab\n"); + exit(1); + } } else break; argc--; diff --git a/src/appl/gss-sample/t_gss_sample.py b/src/appl/gss-sample/t_gss_sample.py index 8a09b2123..09d0803ce 100644 --- a/src/appl/gss-sample/t_gss_sample.py +++ b/src/appl/gss-sample/t_gss_sample.py @@ -27,14 +27,54 @@ appdir = os.path.join(buildtop, 'appl', 'gss-sample') gss_client = os.path.join(appdir, 'gss-client') gss_server = os.path.join(appdir, 'gss-server') -for realm in multipass_realms(): +# Run a gss-server process and a gss-client process, with additional +# gss-client flags given by options. Verify that gss-client displayed +# the expected output for a successful negotiation, and that we +# obtained credentials for the host service. +def server_client_test(realm, options): portstr = str(realm.server_port()) server = realm.start_server([gss_server, '-port', portstr, 'host'], 'starting...') - output = realm.run_as_client([gss_client, '-port', portstr, - hostname, 'host', 'testmsg']) + output = realm.run_as_client([gss_client, '-port', portstr] + options + + [hostname, 'host', 'testmsg']) if 'Signature verified.' not in output: fail('Expected message not seen in gss-client output') stop_daemon(server) + realm.klist(realm.user_princ, realm.host_princ) + +# Make up a filename to hold user's initial credentials. +def ccache_savefile(realm): + return os.path.join(realm.testdir, 'ccache.copy') + +# Move user's initial credentials into the save file. +def ccache_save(realm): + os.rename(realm.ccache, ccache_savefile(realm)) + +# Copy user's initial credentials from the save file into the ccache. +def ccache_restore(realm): + shutil.copyfile(ccache_savefile(realm), realm.ccache) + +# Perform a regular (TGS path) test of the server and client. +def tgs_test(realm, options): + ccache_restore(realm) + server_client_test(realm, options) + +# Perform a test of the server and client with initial credentials +# obtained through gss_acquire_cred_with_password(). +def as_test(realm, options): + os.remove(realm.ccache) + server_client_test(realm, options + ['-user', realm.user_princ, + '-pass', password('user')]) + +for realm in multipass_realms(): + ccache_save(realm) + + tgs_test(realm, ['-krb5']) + tgs_test(realm, ['-spnego']) + tgs_test(realm, ['-iakerb']) + + as_test(realm, ['-krb5']) + as_test(realm, ['-spnego']) + as_test(realm, ['-iakerb']) success('GSS sample application') diff --git a/src/include/k5-int.h b/src/include/k5-int.h index fde8e6660..9a23a7e68 100644 --- a/src/include/k5-int.h +++ b/src/include/k5-int.h @@ -358,6 +358,10 @@ typedef INT64_TYPE krb5_int64; #define KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED 79 /* missing paChecksum in PA-PK-AS-REQ */ #define KDC_ERR_DIGEST_IN_SIGNED_DATA_NOT_ACCEPTED 80 /* bad digest algorithm in SignedData */ #define KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED 81 +#define KRB_AP_ERR_IAKERB_KDC_NOT_FOUND 85 /* The IAKERB proxy could +not find a KDC */ +#define KRB_AP_ERR_IAKERB_KDC_NO_RESPONSE 86 /* The KDC did not respond +to the IAKERB proxy */ /* * This structure is returned in the e-data field of the KRB-ERROR @@ -1032,6 +1036,15 @@ typedef struct _krb5_ad_signedpath { krb5_pa_data **method_data; } krb5_ad_signedpath; +typedef struct _krb5_iakerb_header { + krb5_data target_realm; + krb5_data *cookie; +} krb5_iakerb_header; + +typedef struct _krb5_iakerb_finished { + krb5_checksum checksum; +} krb5_iakerb_finished; + typedef krb5_error_code (*krb5_preauth_obtain_proc)(krb5_context, krb5_pa_data *, krb5_etype_info, krb5_keyblock *, @@ -1329,6 +1342,9 @@ void KRB5_CALLCONV krb5_free_fast_finished(krb5_context, krb5_fast_finished *); void KRB5_CALLCONV krb5_free_fast_response(krb5_context, krb5_fast_response *); void KRB5_CALLCONV krb5_free_ad_kdcissued(krb5_context, krb5_ad_kdcissued *); void KRB5_CALLCONV krb5_free_ad_signedpath(krb5_context, krb5_ad_signedpath *); +void KRB5_CALLCONV krb5_free_iakerb_header(krb5_context, krb5_iakerb_header *); +void KRB5_CALLCONV krb5_free_iakerb_finished(krb5_context, + krb5_iakerb_finished *); /* #include "krb5/wordsize.h" -- comes in through base-defs.h. */ #include "com_err.h" @@ -1741,6 +1757,12 @@ encode_krb5_fast_req(const krb5_fast_req *, krb5_data **); krb5_error_code encode_krb5_pa_fx_fast_reply(const krb5_enc_data *, krb5_data **); +krb5_error_code +encode_krb5_iakerb_header(const krb5_iakerb_header *, krb5_data **); + +krb5_error_code +encode_krb5_iakerb_finished(const krb5_iakerb_finished *, krb5_data **); + krb5_error_code encode_krb5_fast_response(const krb5_fast_response *, krb5_data **); @@ -1939,6 +1961,12 @@ decode_krb5_ad_kdcissued(const krb5_data *, krb5_ad_kdcissued **); krb5_error_code decode_krb5_ad_signedpath(const krb5_data *, krb5_ad_signedpath **); +krb5_error_code +decode_krb5_iakerb_header(const krb5_data *, krb5_iakerb_header **); + +krb5_error_code +decode_krb5_iakerb_finished(const krb5_data *, krb5_iakerb_finished **); + struct _krb5_key_data; /* kdb.h */ struct ldap_seqof_key_data { diff --git a/src/include/krb5/krb5.hin b/src/include/krb5/krb5.hin index 98ffb70b8..1c89f9e92 100644 --- a/src/include/krb5/krb5.hin +++ b/src/include/krb5/krb5.hin @@ -637,6 +637,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_IAKERB_FINISHED 42 #define KRB5_KEYUSAGE_PA_PKINIT_KX 44 /* define in draft-ietf-krb-wg-preauth-framework*/ #define KRB5_KEYUSAGE_FAST_REQ_CHKSUM 50 diff --git a/src/lib/gssapi/generic/gssapi_ext.h b/src/lib/gssapi/generic/gssapi_ext.h index 12216775a..8e5c7c9f7 100644 --- a/src/lib/gssapi/generic/gssapi_ext.h +++ b/src/lib/gssapi/generic/gssapi_ext.h @@ -47,6 +47,33 @@ int KRB5_CALLCONV __gss_userok const char * /*username*/); #endif +OM_uint32 KRB5_CALLCONV +gss_acquire_cred_with_password( + OM_uint32 *, /* minor_status */ + const gss_name_t, /* desired_name */ + const gss_buffer_t, /* password */ + OM_uint32, /* time_req */ + const gss_OID_set, /* desired_mechs */ + gss_cred_usage_t, /* cred_usage */ + gss_cred_id_t *, /* output_cred_handle */ + gss_OID_set *, /* actual_mechs */ + OM_uint32 *); /* time_rec */ + +OM_uint32 KRB5_CALLCONV +gss_add_cred_with_password( + OM_uint32 *, /* minor_status */ + const gss_cred_id_t,/* input_cred_handle */ + const gss_name_t, /* desired_name */ + const gss_OID, /* desired_mech */ + const gss_buffer_t, /* password */ + gss_cred_usage_t, /* cred_usage */ + OM_uint32, /* initiator_time_req */ + OM_uint32, /* acceptor_time_req */ + gss_cred_id_t *, /* output_cred_handle */ + gss_OID_set *, /* actual_mechs */ + OM_uint32 *, /* initiator_time_rec */ + OM_uint32 *); /* acceptor_time_rec */ + /* * GGF extensions */ diff --git a/src/lib/gssapi/krb5/Makefile.in b/src/lib/gssapi/krb5/Makefile.in index 01591b2c0..2f4e96976 100644 --- a/src/lib/gssapi/krb5/Makefile.in +++ b/src/lib/gssapi/krb5/Makefile.in @@ -52,6 +52,7 @@ SRCS = \ $(srcdir)/export_sec_context.c \ $(srcdir)/get_tkt_flags.c \ $(srcdir)/gssapi_krb5.c \ + $(srcdir)/iakerb.c \ $(srcdir)/import_name.c \ $(srcdir)/import_sec_context.c \ $(srcdir)/indicate_mechs.c \ @@ -106,6 +107,7 @@ OBJS = \ $(OUTPRE)export_sec_context.$(OBJEXT) \ $(OUTPRE)get_tkt_flags.$(OBJEXT) \ $(OUTPRE)gssapi_krb5.$(OBJEXT) \ + $(OUTPRE)iakerb.$(OBJEXT) \ $(OUTPRE)import_name.$(OBJEXT) \ $(OUTPRE)import_sec_context.$(OBJEXT) \ $(OUTPRE)indicate_mechs.$(OBJEXT) \ @@ -163,6 +165,7 @@ STLIBOBJS = \ export_sec_context.o \ get_tkt_flags.o \ gssapi_krb5.o \ + iakerb.o \ import_name.o \ import_sec_context.o \ indicate_mechs.o \ diff --git a/src/lib/gssapi/krb5/accept_sec_context.c b/src/lib/gssapi/krb5/accept_sec_context.c index ce3075fad..368ef21be 100644 --- a/src/lib/gssapi/krb5/accept_sec_context.c +++ b/src/lib/gssapi/krb5/accept_sec_context.c @@ -105,7 +105,6 @@ #endif #include - #ifdef CFX_EXERCISE #define CFX_ACCEPTOR_SUBKEY (time(0) & 1) #else @@ -384,12 +383,49 @@ fail: return major_status; } +static krb5_error_code +kg_process_extension(krb5_context context, + krb5_auth_context auth_context, + int ext_type, + krb5_data *ext_data, + krb5_gss_ctx_ext_t exts) +{ + krb5_error_code code = 0; + + assert(exts != NULL); + + switch (ext_type) { + case KRB5_GSS_EXTS_IAKERB_FINISHED: + if (exts->iakerb.conv == NULL) { + code = KRB5KRB_AP_ERR_MSG_TYPE; /* XXX */ + } else { + krb5_key key; + + code = krb5_auth_con_getrecvsubkey_k(context, auth_context, &key); + if (code != 0) + break; + + code = iakerb_verify_finished(context, key, exts->iakerb.conv, + ext_data); + if (code == 0) + exts->iakerb.verified = 1; + + krb5_k_free_key(context, key); + } + break; + default: + break; + } + + return code; +} + static OM_uint32 kg_accept_krb5(minor_status, context_handle, verifier_cred_handle, input_token, input_chan_bindings, src_name, mech_type, output_token, ret_flags, time_rec, - delegated_cred_handle) + delegated_cred_handle, exts) OM_uint32 *minor_status; gss_ctx_id_t *context_handle; gss_cred_id_t verifier_cred_handle; @@ -401,6 +437,7 @@ kg_accept_krb5(minor_status, context_handle, OM_uint32 *ret_flags; OM_uint32 *time_rec; gss_cred_id_t *delegated_cred_handle; + krb5_gss_ctx_ext_t exts; { krb5_context context; unsigned char *ptr, *ptr2; @@ -741,16 +778,11 @@ kg_accept_krb5(minor_status, context_handle, /* if the checksum length > 24, there are options to process */ - if(authdat->checksum->length > 24 && (gss_flags & GSS_C_DELEG_FLAG)) { - - i = authdat->checksum->length - 24; - + i = authdat->checksum->length - 24; + if (i && (gss_flags & GSS_C_DELEG_FLAG)) { if (i >= 4) { - TREAD_INT16(ptr, option_id, bigend); - TREAD_INT16(ptr, option.length, bigend); - i -= 4; if (i < option.length || option.length < 0) { @@ -784,37 +816,41 @@ kg_accept_krb5(minor_status, context_handle, } /* if i >= 4 */ /* ignore any additional trailing data, for now */ -#ifdef CFX_EXERCISE - { - FILE *f = fopen("/tmp/gsslog", "a"); - if (f) { - fprintf(f, - "initial context token with delegation, %d extra bytes\n", - i); - fclose(f); - } + } + while (i > 0) { + /* Process Type-Length-Data options */ + if (i < 8) { + code = KG_BAD_LENGTH; + major_status = GSS_S_FAILURE; + goto fail; } -#endif - } else { -#ifdef CFX_EXERCISE - { - FILE *f = fopen("/tmp/gsslog", "a"); - if (f) { - if (gss_flags & GSS_C_DELEG_FLAG) - fprintf(f, - "initial context token, delegation flag but too small\n"); - else - /* no deleg flag, length might still be too big */ - fprintf(f, - "initial context token, %d extra bytes\n", - authdat->checksum->length - 24); - fclose(f); - } + TREAD_INT(ptr, option_id, 1); + TREAD_INT(ptr, option.length, 1); + i -= 8; + if (i < option.length) { + code = KG_BAD_LENGTH; + major_status = GSS_S_FAILURE; + goto fail; + } + TREAD_STR(ptr, ptr2, option.length); + option.data = (char *)ptr2; + + i -= option.length; + + code = kg_process_extension(context, auth_context, + option_id, &option, exts); + if (code != 0) { + major_status = GSS_S_FAILURE; + goto fail; } -#endif } } + if (exts->iakerb.conv && !exts->iakerb.verified) { + major_status = GSS_S_BAD_SIG; + goto fail; + } + /* only DCE_STYLE clients are allowed to send raw AP-REQs */ if (no_encap != ((gss_flags & GSS_C_DCE_STYLE) != 0)) { major_status = GSS_S_DEFECTIVE_TOKEN; @@ -831,6 +867,7 @@ kg_accept_krb5(minor_status, context_handle, } memset(ctx, 0, sizeof(krb5_gss_ctx_id_rec)); + ctx->magic = KG_CONTEXT; ctx->mech_used = (gss_OID) mech_used; ctx->auth_context = auth_context; ctx->initiate = 0; @@ -1248,22 +1285,19 @@ done: #endif /* LEAN_CLIENT */ OM_uint32 -krb5_gss_accept_sec_context(minor_status, context_handle, - verifier_cred_handle, input_token, - input_chan_bindings, src_name, mech_type, - output_token, ret_flags, time_rec, - delegated_cred_handle) - OM_uint32 *minor_status; - gss_ctx_id_t *context_handle; - gss_cred_id_t verifier_cred_handle; - gss_buffer_t input_token; - gss_channel_bindings_t input_chan_bindings; - gss_name_t *src_name; - gss_OID *mech_type; - gss_buffer_t output_token; - OM_uint32 *ret_flags; - OM_uint32 *time_rec; - gss_cred_id_t *delegated_cred_handle; +krb5_gss_accept_sec_context_ext( + OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + gss_cred_id_t verifier_cred_handle, + gss_buffer_t input_token, + gss_channel_bindings_t input_chan_bindings, + gss_name_t *src_name, + gss_OID *mech_type, + gss_buffer_t output_token, + OM_uint32 *ret_flags, + OM_uint32 *time_rec, + gss_cred_id_t *delegated_cred_handle, + krb5_gss_ctx_ext_t exts) { krb5_gss_ctx_id_rec *ctx = (krb5_gss_ctx_id_rec *)*context_handle; @@ -1291,5 +1325,42 @@ krb5_gss_accept_sec_context(minor_status, context_handle, verifier_cred_handle, input_token, input_chan_bindings, src_name, mech_type, output_token, ret_flags, time_rec, - delegated_cred_handle); + delegated_cred_handle, exts); +} + +OM_uint32 +krb5_gss_accept_sec_context(minor_status, context_handle, + verifier_cred_handle, input_token, + input_chan_bindings, src_name, mech_type, + output_token, ret_flags, time_rec, + delegated_cred_handle) + OM_uint32 *minor_status; + gss_ctx_id_t *context_handle; + gss_cred_id_t verifier_cred_handle; + gss_buffer_t input_token; + gss_channel_bindings_t input_chan_bindings; + gss_name_t *src_name; + gss_OID *mech_type; + gss_buffer_t output_token; + OM_uint32 *ret_flags; + OM_uint32 *time_rec; + gss_cred_id_t *delegated_cred_handle; +{ + krb5_gss_ctx_ext_rec exts; + + memset(&exts, 0, sizeof(exts)); + + return krb5_gss_accept_sec_context_ext(minor_status, + context_handle, + verifier_cred_handle, + input_token, + input_chan_bindings, + src_name, + mech_type, + output_token, + ret_flags, + time_rec, + delegated_cred_handle, + &exts); } + diff --git a/src/lib/gssapi/krb5/acquire_cred.c b/src/lib/gssapi/krb5/acquire_cred.c index 2ccf7dbf3..c3e84818d 100644 --- a/src/lib/gssapi/krb5/acquire_cred.c +++ b/src/lib/gssapi/krb5/acquire_cred.c @@ -222,16 +222,17 @@ acquire_init_cred(krb5_context context, OM_uint32 *minor_status, krb5_gss_name_t desired_name, krb5_gss_name_t *output_name, + gss_buffer_t password, krb5_gss_cred_id_rec *cred) { krb5_error_code code; krb5_ccache ccache; - krb5_principal princ, tmp_princ; - krb5_flags flags; + krb5_principal princ = NULL, tmp_princ; krb5_cc_cursor cur; krb5_creds creds; int got_endtime; int caller_provided_ccache_name = 0; + krb5_data password_data; cred->ccache = NULL; @@ -335,24 +336,30 @@ acquire_init_cred(krb5_context context, } /* turn off OPENCLOSE mode while extensive frobbing is going on */ - - flags = 0; /* turns off OPENCLOSE mode */ - if ((code = krb5_cc_set_flags(context, ccache, flags))) { + code = krb5_cc_set_flags(context, ccache, 0); + if (code == KRB5_FCC_NOFILE && + password != GSS_C_NO_BUFFER && desired_name != NULL) { + /* We will get initial creds later. */ + code = krb5_cc_initialize(context, ccache, desired_name->princ); + if (code == 0) + code = krb5_cc_set_flags(context, ccache, 0); + } + if (code != 0) { (void)krb5_cc_close(context, ccache); *minor_status = code; return(GSS_S_CRED_UNAVAIL); } /* get out the principal name and see if it matches */ - - if ((code = krb5_cc_get_principal(context, ccache, &princ))) { + code = krb5_cc_get_principal(context, ccache, &princ); + if (code != 0) { (void)krb5_cc_close(context, ccache); *minor_status = code; return(GSS_S_FAILURE); } - if (desired_name != (krb5_gss_name_t)NULL) { - if (! krb5_principal_compare(context, princ, desired_name->princ)) { + if (desired_name != NULL) { + if (!krb5_principal_compare(context, princ, desired_name->princ)) { (void)krb5_free_principal(context, princ); (void)krb5_cc_close(context, ccache); *minor_status = KG_CCACHE_NOMATCH; @@ -369,6 +376,32 @@ acquire_init_cred(krb5_context context, *minor_status = code; return(GSS_S_FAILURE); } + /* princ is now owned by output_name, it need not be freed here */ + } + + if (password != GSS_C_NO_BUFFER) { + /* stash the password for later */ + password_data.length = password->length; + password_data.data = (char *)password->value; + + code = krb5int_copy_data_contents_add0(context, &password_data, + &cred->password); + if (code != 0) { + (void)krb5_cc_close(context, ccache); + *minor_status = code; + return GSS_S_FAILURE; + } + + /* restore the OPENCLOSE flag */ + code = krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE); + if (code != 0) { + (void)krb5_cc_close(context, ccache); + *minor_status = code; + return GSS_S_FAILURE; + } + + cred->ccache = ccache; + return GSS_S_COMPLETE; } /* iterate over the ccache, find the tgt */ @@ -388,7 +421,7 @@ acquire_init_cred(krb5_context context, code = krb5_build_principal_ext(context, &tmp_princ, krb5_princ_realm(context, princ)->length, krb5_princ_realm(context, princ)->data, - 6, "krbtgt", + KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME, krb5_princ_realm(context, princ)->length, krb5_princ_realm(context, princ)->data, 0); @@ -433,8 +466,7 @@ acquire_init_cred(krb5_context context, *minor_status = code; return(GSS_S_FAILURE); } - flags = KRB5_TC_OPENCLOSE; /* turns on OPENCLOSE mode */ - if ((code = krb5_cc_set_flags(context, ccache, flags))) { + if ((code = krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE))) { (void)krb5_cc_close(context, ccache); *minor_status = code; return(GSS_S_FAILURE); @@ -449,38 +481,36 @@ acquire_init_cred(krb5_context context, } /*ARGSUSED*/ -OM_uint32 -krb5_gss_acquire_cred(minor_status, desired_name, time_req, - desired_mechs, cred_usage, output_cred_handle, - actual_mechs, time_rec) +static OM_uint32 +acquire_cred(minor_status, desired_name, password, time_req, + desired_mechs, cred_usage, output_cred_handle, + actual_mechs, time_rec, req_iakerb) OM_uint32 *minor_status; - gss_name_t desired_name; + const gss_name_t desired_name; + const gss_buffer_t password; OM_uint32 time_req; - gss_OID_set desired_mechs; + const gss_OID_set desired_mechs; gss_cred_usage_t cred_usage; gss_cred_id_t *output_cred_handle; gss_OID_set *actual_mechs; OM_uint32 *time_rec; + int req_iakerb; { - krb5_context context; + krb5_context context = NULL; size_t i; - krb5_gss_cred_id_t cred; - gss_OID_set ret_mechs = NULL; + krb5_gss_cred_id_t cred = NULL; + gss_OID_set ret_mechs = GSS_C_NO_OID_SET; int req_old, req_new; OM_uint32 ret; - krb5_error_code code; + krb5_error_code code = 0; code = gss_krb5int_initialize_library(); - if (code) { - *minor_status = code; - return GSS_S_FAILURE; - } + if (code) + goto krb_error_out; code = krb5_gss_init_context(&context); - if (code) { - *minor_status = code; - return GSS_S_FAILURE; - } + if (code) + goto krb_error_out; /* make sure all outputs are valid */ @@ -495,9 +525,8 @@ krb5_gss_acquire_cred(minor_status, desired_name, time_req, /*SUPPRESS 29*/ if ((desired_name != GSS_C_NO_NAME) && (! kg_validate_name(desired_name))) { - *minor_status = (OM_uint32) G_VALIDATE_FAILED; - krb5_free_context(context); - return(GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME); + code = G_VALIDATE_FAILED; + goto krb_error_out; } /* verify that the requested mechanism set is the default, or @@ -518,26 +547,21 @@ krb5_gss_acquire_cred(minor_status, desired_name, time_req, } if (!req_old && !req_new) { - *minor_status = 0; - krb5_free_context(context); - return(GSS_S_BAD_MECH); + ret = GSS_S_BAD_MECH; + goto error_out; } } /* create the gss cred structure */ - - if ((cred = - (krb5_gss_cred_id_t) xmalloc(sizeof(krb5_gss_cred_id_rec))) == NULL) { - *minor_status = ENOMEM; - krb5_free_context(context); - return(GSS_S_FAILURE); - } - memset(cred, 0, sizeof(krb5_gss_cred_id_rec)); + cred = k5alloc(sizeof(krb5_gss_cred_id_rec), &code); + if (code != 0) + goto krb_error_out; cred->usage = cred_usage; cred->name = NULL; - cred->prerfc_mech = (req_old != 0); - cred->rfc_mech = (req_new != 0); + cred->prerfc_mech = (req_old != 0) && (req_iakerb == 0); + cred->rfc_mech = (req_new != 0) && (req_iakerb == 0); + cred->iakerb_mech = req_iakerb; cred->default_identity = (desired_name == GSS_C_NO_NAME); #ifndef LEAN_CLIENT @@ -546,11 +570,9 @@ krb5_gss_acquire_cred(minor_status, desired_name, time_req, cred->ccache = NULL; code = k5_mutex_init(&cred->lock); - if (code) { - *minor_status = code; - krb5_free_context(context); - return GSS_S_FAILURE; - } + if (code) + goto krb_error_out; + /* Note that we don't need to lock this GSSAPI credential record here, because no other thread can gain access to it until we return it. */ @@ -558,11 +580,8 @@ krb5_gss_acquire_cred(minor_status, desired_name, time_req, if ((cred_usage != GSS_C_INITIATE) && (cred_usage != GSS_C_ACCEPT) && (cred_usage != GSS_C_BOTH)) { - k5_mutex_destroy(&cred->lock); - xfree(cred); *minor_status = (OM_uint32) G_BAD_USAGE; - krb5_free_context(context); - return(GSS_S_FAILURE); + goto error_out; } /* if requested, acquire credentials for accepting */ @@ -574,14 +593,7 @@ krb5_gss_acquire_cred(minor_status, desired_name, time_req, (krb5_gss_name_t)desired_name, &cred->name, cred)) != GSS_S_COMPLETE) { - if (cred->name) - kg_release_name(context, 0, &cred->name); - k5_mutex_destroy(&cred->lock); - xfree(cred); - /* minor_status set by acquire_accept_cred() */ - save_error_info(*minor_status, context); - krb5_free_context(context); - return(ret); + goto error_out; } #endif /* LEAN_CLIENT */ @@ -589,46 +601,24 @@ krb5_gss_acquire_cred(minor_status, desired_name, time_req, /* this will fill in cred->name if it wasn't set above, and the desired_name is not specified */ - if ((cred_usage == GSS_C_INITIATE) || - (cred_usage == GSS_C_BOTH)) - if ((ret = - acquire_init_cred(context, minor_status, - cred->name?cred->name:(krb5_gss_name_t)desired_name, - &cred->name, cred)) - != GSS_S_COMPLETE) { -#ifndef LEAN_CLIENT - if (cred->keytab) - krb5_kt_close(context, cred->keytab); -#endif /* LEAN_CLIENT */ - if (cred->name) - kg_release_name(context, 0, &cred->name); - k5_mutex_destroy(&cred->lock); - xfree(cred); - /* minor_status set by acquire_init_cred() */ - save_error_info(*minor_status, context); - krb5_free_context(context); - return(ret); - } + if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) { + ret = acquire_init_cred(context, minor_status, + cred->name ? + cred->name : (krb5_gss_name_t)desired_name, + &cred->name, password, cred); + if (ret != GSS_S_COMPLETE) + goto error_out; + } /* if the princ wasn't filled in already, fill it in now */ - if (!cred->name && (desired_name != GSS_C_NO_NAME)) - if ((code = kg_duplicate_name(context, - (krb5_gss_name_t)desired_name, - 0, &cred->name))) { - if (cred->ccache) - (void)krb5_cc_close(context, cred->ccache); -#ifndef LEAN_CLIENT - if (cred->keytab) - (void)krb5_kt_close(context, cred->keytab); -#endif /* LEAN_CLIENT */ - k5_mutex_destroy(&cred->lock); - xfree(cred); - *minor_status = code; - save_error_info(*minor_status, context); - krb5_free_context(context); - return(GSS_S_FAILURE); - } + if (!cred->name && (desired_name != GSS_C_NO_NAME)) { + code = kg_duplicate_name(context, + (krb5_gss_name_t)desired_name, + 0, &cred->name); + if (code != 0) + goto krb_error_out; + } /*** at this point, the cred structure has been completely created */ @@ -640,22 +630,9 @@ krb5_gss_acquire_cred(minor_status, desired_name, time_req, } else { krb5_timestamp now; - if ((code = krb5_timeofday(context, &now))) { - if (cred->ccache) - (void)krb5_cc_close(context, cred->ccache); -#ifndef LEAN_CLIENT - if (cred->keytab) - (void)krb5_kt_close(context, cred->keytab); -#endif /* LEAN_CLIENT */ - if (cred->name) - kg_release_name(context, 0, &cred->name); - k5_mutex_destroy(&cred->lock); - xfree(cred); - *minor_status = code; - save_error_info(*minor_status, context); - krb5_free_context(context); - return(GSS_S_FAILURE); - } + code = krb5_timeofday(context, &now); + if (code != 0) + goto krb_error_out; if (time_rec) *time_rec = (cred->tgt_expire > now) ? (cred->tgt_expire - now) : 0; @@ -673,44 +650,20 @@ krb5_gss_acquire_cred(minor_status, desired_name, time_req, (cred->rfc_mech && GSS_ERROR(ret = generic_gss_add_oid_set_member(minor_status, gss_mech_krb5, + &ret_mechs))) || + (cred->iakerb_mech && + GSS_ERROR(ret = generic_gss_add_oid_set_member(minor_status, + gss_mech_iakerb, &ret_mechs)))) { - if (cred->ccache) - (void)krb5_cc_close(context, cred->ccache); -#ifndef LEAN_CLIENT - if (cred->keytab) - (void)krb5_kt_close(context, cred->keytab); -#endif /* LEAN_CLIENT */ - if (cred->name) - kg_release_name(context, 0, &cred->name); - k5_mutex_destroy(&cred->lock); - xfree(cred); - /* *minor_status set above */ - krb5_free_context(context); - return(ret); + goto error_out; } } /* intern the credential handle */ if (! kg_save_cred_id((gss_cred_id_t) cred)) { - if (ret_mechs) { - free(ret_mechs->elements); - free(ret_mechs); - } - if (cred->ccache) - (void)krb5_cc_close(context, cred->ccache); -#ifndef LEAN_CLIENT - if (cred->keytab) - (void)krb5_kt_close(context, cred->keytab); -#endif /* LEAN_CLIENT */ - if (cred->name) - kg_release_name(context, 0, &cred->name); - k5_mutex_destroy(&cred->lock); - xfree(cred); - *minor_status = (OM_uint32) G_VALIDATE_FAILED; - save_error_string(*minor_status, "error saving credentials"); - krb5_free_context(context); - return(GSS_S_FAILURE); + ret = GSS_S_FAILURE; + goto error_out; } /* return success */ @@ -722,6 +675,29 @@ krb5_gss_acquire_cred(minor_status, desired_name, time_req, krb5_free_context(context); return(GSS_S_COMPLETE); + +krb_error_out: + *minor_status = code; + ret = GSS_S_FAILURE; + +error_out: + if (ret_mechs != GSS_C_NO_OID_SET) { + free(ret_mechs->elements); + free(ret_mechs); + } + if (cred->ccache) + (void)krb5_cc_close(context, cred->ccache); +#ifndef LEAN_CLIENT + if (cred->keytab) + (void)krb5_kt_close(context, cred->keytab); +#endif /* LEAN_CLIENT */ + if (cred->name) + kg_release_name(context, 0, &cred->name); + k5_mutex_destroy(&cred->lock); + xfree(cred); + save_error_info(*minor_status, context); + krb5_free_context(context); + return ret; } OM_uint32 @@ -768,3 +744,76 @@ gss_krb5int_set_cred_rcache(OM_uint32 *minor_status, *minor_status = 0; return GSS_S_COMPLETE; } + +OM_uint32 +krb5_gss_acquire_cred(minor_status, desired_name, time_req, + desired_mechs, cred_usage, output_cred_handle, + actual_mechs, time_rec) + OM_uint32 *minor_status; + gss_name_t desired_name; + OM_uint32 time_req; + gss_OID_set desired_mechs; + gss_cred_usage_t cred_usage; + gss_cred_id_t *output_cred_handle; + gss_OID_set *actual_mechs; + OM_uint32 *time_rec; +{ + return acquire_cred(minor_status, desired_name, GSS_C_NO_BUFFER, + time_req, desired_mechs, + cred_usage, output_cred_handle, actual_mechs, + time_rec, 0); +} + +OM_uint32 +iakerb_gss_acquire_cred(minor_status, desired_name, time_req, + desired_mechs, cred_usage, output_cred_handle, + actual_mechs, time_rec) + OM_uint32 *minor_status; + gss_name_t desired_name; + OM_uint32 time_req; + gss_OID_set desired_mechs; + gss_cred_usage_t cred_usage; + gss_cred_id_t *output_cred_handle; + gss_OID_set *actual_mechs; + OM_uint32 *time_rec; +{ + return acquire_cred(minor_status, desired_name, GSS_C_NO_BUFFER, + time_req, desired_mechs, + cred_usage, output_cred_handle, actual_mechs, + time_rec, 1); +} + +OM_uint32 +krb5_gss_acquire_cred_with_password(OM_uint32 *minor_status, + const gss_name_t desired_name, + const gss_buffer_t password, + OM_uint32 time_req, + const gss_OID_set desired_mechs, + int cred_usage, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec) +{ + return acquire_cred(minor_status, desired_name, password, + time_req, desired_mechs, + cred_usage, output_cred_handle, actual_mechs, + time_rec, 0); +} + +OM_uint32 +iakerb_gss_acquire_cred_with_password(OM_uint32 *minor_status, + const gss_name_t desired_name, + const gss_buffer_t password, + OM_uint32 time_req, + const gss_OID_set desired_mechs, + int cred_usage, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec) +{ + return acquire_cred(minor_status, desired_name, password, + time_req, desired_mechs, + cred_usage, output_cred_handle, actual_mechs, + time_rec, 1); +} + diff --git a/src/lib/gssapi/krb5/add_cred.c b/src/lib/gssapi/krb5/add_cred.c index 6f6707c1a..489703655 100644 --- a/src/lib/gssapi/krb5/add_cred.c +++ b/src/lib/gssapi/krb5/add_cred.c @@ -145,7 +145,8 @@ krb5_gss_add_cred(minor_status, input_cred_handle, /* check that desired_mech isn't already in the credential */ if ((g_OID_equal(desired_mech, gss_mech_krb5_old) && cred->prerfc_mech) || - (g_OID_equal(desired_mech, gss_mech_krb5) && cred->rfc_mech)) { + (g_OID_equal(desired_mech, gss_mech_krb5) && cred->rfc_mech) || + (g_OID_equal(desired_mech, gss_mech_iakerb) && cred->iakerb_mech)) { *minor_status = 0; krb5_free_context(context); return(GSS_S_DUPLICATE_ELEMENT); @@ -197,6 +198,7 @@ krb5_gss_add_cred(minor_status, input_cred_handle, new_cred->usage = cred_usage; new_cred->prerfc_mech = cred->prerfc_mech; new_cred->rfc_mech = cred->rfc_mech; + new_cred->iakerb_mech = cred->iakerb_mech; new_cred->tgt_expire = cred->tgt_expire; if (cred->name) @@ -359,6 +361,8 @@ krb5_gss_add_cred(minor_status, input_cred_handle, cred->prerfc_mech = 1; else if (g_OID_equal(desired_mech, gss_mech_krb5)) cred->rfc_mech = 1; + else if (g_OID_equal(desired_mech, gss_mech_iakerb)) + cred->iakerb_mech = 1; /* set the outputs */ diff --git a/src/lib/gssapi/krb5/gssapiP_krb5.h b/src/lib/gssapi/krb5/gssapiP_krb5.h index 2ac4b3ae2..1ec7be33f 100644 --- a/src/lib/gssapi/krb5/gssapiP_krb5.h +++ b/src/lib/gssapi/krb5/gssapiP_krb5.h @@ -86,6 +86,9 @@ #define GSS_MECH_KRB5_WRONG_OID_LENGTH 9 #define GSS_MECH_KRB5_WRONG_OID "\052\206\110\202\367\022\001\002\002" +/* IAKERB variant */ +#define GSS_MECH_IAKERB_OID_LENGTH 6 +#define GSS_MECH_IAKERB_OID "\053\006\001\005\002\005" #define CKSUMTYPE_KG_CB 0x8003 @@ -100,6 +103,7 @@ #define KG2_TOK_MIC_MSG 0x0404 #define KG2_TOK_WRAP_MSG 0x0504 #define KG2_TOK_DEL_CTX 0x0405 +#define IAKERB_TOK_PROXY 0x0501 #define KRB5_GSS_FOR_CREDS_OPTION 1 @@ -170,6 +174,7 @@ typedef struct _krb5_gss_cred_id_rec { unsigned int rfc_mech : 1; unsigned int proxy_cred : 1; unsigned int default_identity : 1; + unsigned int iakerb_mech : 1; /* keytab (accept) data */ krb5_keytab keytab; @@ -179,9 +184,18 @@ typedef struct _krb5_gss_cred_id_rec { krb5_ccache ccache; krb5_timestamp tgt_expire; krb5_enctype *req_enctypes; /* limit negotiated enctypes to this list */ + krb5_data password; } krb5_gss_cred_id_rec, *krb5_gss_cred_id_t; +typedef struct _krb5_gss_ctx_ext_rec { + struct { + krb5_data *conv; + int verified; + } iakerb; +} krb5_gss_ctx_ext_rec, *krb5_gss_ctx_ext_t; + typedef struct _krb5_gss_ctx_id_rec { + krb5_magic magic; unsigned int initiate : 1; /* nonzero if initiating, zero if accepting */ unsigned int established : 1; unsigned int big_endian : 1; @@ -239,7 +253,9 @@ extern k5_mutex_t gssint_krb5_keytab_lock; #define kg_validate_name(name) g_validate_name(&kg_vdb,name) #define kg_validate_cred_id(cred) g_validate_cred_id(&kg_vdb,cred) -#define kg_validate_ctx_id(ctx) g_validate_ctx_id(&kg_vdb,ctx) +#define kg_validate_ctx_id(ctx) (g_validate_ctx_id(&kg_vdb,ctx) && \ + ((krb5_gss_ctx_id_t)ctx)->magic == \ + KG_CONTEXT) #define kg_validate_lucidctx_id(lctx) g_validate_lucidctx_id(&kg_vdb,lctx) #define kg_delete_name(name) g_delete_name(&kg_vdb,name) @@ -492,7 +508,8 @@ kg_new_connection( OM_uint32 *ret_flags, OM_uint32 *time_rec, krb5_context context, - int default_mech); + int default_mech, + krb5_gss_ctx_ext_t exts); /** declarations of internal name mechanism functions **/ @@ -507,6 +524,42 @@ OM_uint32 krb5_gss_acquire_cred OM_uint32* /* time_rec */ ); +OM_uint32 +iakerb_gss_acquire_cred +(OM_uint32*, /* minor_status */ + gss_name_t, /* desired_name */ + OM_uint32, /* time_req */ + gss_OID_set, /* desired_mechs */ + gss_cred_usage_t, /* cred_usage */ + gss_cred_id_t*, /* output_cred_handle */ + gss_OID_set*, /* actual_mechs */ + OM_uint32* /* time_rec */ +); + +OM_uint32 +krb5_gss_acquire_cred_with_password( + OM_uint32 *minor_status, + const gss_name_t desired_name, + const gss_buffer_t password, + OM_uint32 time_req, + const gss_OID_set desired_mechs, + int cred_usage, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec); + +OM_uint32 +iakerb_gss_acquire_cred_with_password( + OM_uint32 *minor_status, + const gss_name_t desired_name, + const gss_buffer_t password, + OM_uint32 time_req, + const gss_OID_set desired_mechs, + int cred_usage, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec); + OM_uint32 krb5_gss_release_cred (OM_uint32*, /* minor_status */ gss_cred_id_t* /* cred_handle */ @@ -529,6 +582,24 @@ OM_uint32 krb5_gss_init_sec_context OM_uint32* /* time_rec */ ); +OM_uint32 krb5_gss_init_sec_context_ext +(OM_uint32*, /* minor_status */ + gss_cred_id_t, /* claimant_cred_handle */ + gss_ctx_id_t*, /* context_handle */ + gss_name_t, /* target_name */ + gss_OID, /* mech_type */ + OM_uint32, /* req_flags */ + OM_uint32, /* time_req */ + gss_channel_bindings_t, + /* input_chan_bindings */ + gss_buffer_t, /* input_token */ + gss_OID*, /* actual_mech_type */ + gss_buffer_t, /* output_token */ + OM_uint32*, /* ret_flags */ + OM_uint32*, /* time_rec */ + krb5_gss_ctx_ext_t /* exts */ +); + #ifndef LEAN_CLIENT OM_uint32 krb5_gss_accept_sec_context (OM_uint32*, /* minor_status */ @@ -544,6 +615,22 @@ OM_uint32 krb5_gss_accept_sec_context OM_uint32*, /* time_rec */ gss_cred_id_t* /* delegated_cred_handle */ ); + +OM_uint32 krb5_gss_accept_sec_context_ext +(OM_uint32*, /* minor_status */ + gss_ctx_id_t*, /* context_handle */ + gss_cred_id_t, /* verifier_cred_handle */ + gss_buffer_t, /* input_token_buffer */ + gss_channel_bindings_t, + /* input_chan_bindings */ + gss_name_t*, /* src_name */ + gss_OID*, /* mech_type */ + gss_buffer_t, /* output_token */ + OM_uint32*, /* ret_flags */ + OM_uint32*, /* time_rec */ + gss_cred_id_t*, /* delegated_cred_handle */ + krb5_gss_ctx_ext_t/*exts */ +); #endif /* LEAN_CLIENT */ OM_uint32 krb5_gss_process_context_token @@ -1084,4 +1171,53 @@ extern void krb5_gss_delete_error_info(void *p); #define GSS_KRB5_SESSION_KEY_ENCTYPE_OID_LENGTH 10 #define GSS_KRB5_SESSION_KEY_ENCTYPE_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x04" +/* IAKERB */ + +OM_uint32 +iakerb_gss_init_sec_context(OM_uint32 *minor_status, + gss_cred_id_t claimant_cred_handle, + gss_ctx_id_t *context_handle, + gss_name_t target_name, + gss_OID mech_type, + OM_uint32 req_flags, + OM_uint32 time_req, + gss_channel_bindings_t input_chan_bindings, + gss_buffer_t input_token, + gss_OID *actual_mech_type, + gss_buffer_t output_token, + OM_uint32 *ret_flags, + OM_uint32 *time_rec); + +OM_uint32 +iakerb_gss_accept_sec_context(OM_uint32 *minor_status, + gss_ctx_id_t *context_handler, + gss_cred_id_t verifier_cred_handle, + gss_buffer_t input_token, + gss_channel_bindings_t input_chan_bindings, + gss_name_t *src_name, + gss_OID *mech_type, + gss_buffer_t output_token, + OM_uint32 *ret_flags, + OM_uint32 *time_rec, + gss_cred_id_t *delegated_cred_handle); + +OM_uint32 +iakerb_gss_delete_sec_context(OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + gss_buffer_t output_token); + +krb5_error_code +iakerb_make_finished(krb5_context context, + krb5_key key, + const krb5_data *conv, + krb5_data **finished); + +krb5_error_code +iakerb_verify_finished(krb5_context context, + krb5_key key, + const krb5_data *conv, + const krb5_data *finished); + +#define KRB5_GSS_EXTS_IAKERB_FINISHED 1 + #endif /* _GSSAPIP_KRB5_H_ */ diff --git a/src/lib/gssapi/krb5/gssapi_err_krb5.et b/src/lib/gssapi/krb5/gssapi_err_krb5.et index 4cfd68a00..6396d5902 100644 --- a/src/lib/gssapi/krb5/gssapi_err_krb5.et +++ b/src/lib/gssapi/krb5/gssapi_err_krb5.et @@ -38,4 +38,5 @@ error_code KG_EMPTY_CCACHE, "Credential cache is empty" error_code KG_NO_CTYPES, "Acceptor and Initiator share no checksum types" error_code KG_LUCID_VERSION, "Requested lucid context version not supported" error_code KG_INPUT_TOO_LONG, "PRF input too long" +error_code KG_IAKERB_CONTEXT, "Bad magic number for iakerb_ctx_id_t" end diff --git a/src/lib/gssapi/krb5/gssapi_krb5.c b/src/lib/gssapi/krb5/gssapi_krb5.c index 9e5ba76d9..c902c3dcc 100644 --- a/src/lib/gssapi/krb5/gssapi_krb5.c +++ b/src/lib/gssapi/krb5/gssapi_krb5.c @@ -127,34 +127,36 @@ const gss_OID_desc krb5_gss_oid_array[] = { {GSS_MECH_KRB5_OLD_OID_LENGTH, GSS_MECH_KRB5_OLD_OID}, /* this is the unofficial, incorrect mech OID emitted by MS */ {GSS_MECH_KRB5_WRONG_OID_LENGTH, GSS_MECH_KRB5_WRONG_OID}, + /* IAKERB OID */ + {GSS_MECH_IAKERB_OID_LENGTH, GSS_MECH_IAKERB_OID}, /* this is the v2 assigned OID */ {9, "\052\206\110\206\367\022\001\002\003"}, /* these two are name type OID's */ - /* 2.1.1. Kerberos Principal Name Form: (rfc 1964) * This name form shall be represented by the Object Identifier {iso(1) * member-body(2) United States(840) mit(113554) infosys(1) gssapi(2) * krb5(2) krb5_name(1)}. The recommended symbolic name for this type * is "GSS_KRB5_NT_PRINCIPAL_NAME". */ {10, "\052\206\110\206\367\022\001\002\002\001"}, - /* gss_nt_krb5_principal. Object identifier for a krb5_principal. Do not use. */ {10, "\052\206\110\206\367\022\001\002\002\002"}, - { 0, 0 } }; const gss_OID_desc * const gss_mech_krb5 = krb5_gss_oid_array+0; const gss_OID_desc * const gss_mech_krb5_old = krb5_gss_oid_array+1; const gss_OID_desc * const gss_mech_krb5_wrong = krb5_gss_oid_array+2; -const gss_OID_desc * const gss_nt_krb5_name = krb5_gss_oid_array+4; -const gss_OID_desc * const gss_nt_krb5_principal = krb5_gss_oid_array+5; -const gss_OID_desc * const GSS_KRB5_NT_PRINCIPAL_NAME = krb5_gss_oid_array+4; +const gss_OID_desc * const gss_mech_iakerb = krb5_gss_oid_array+3; + + +const gss_OID_desc * const gss_nt_krb5_name = krb5_gss_oid_array+5; +const gss_OID_desc * const gss_nt_krb5_principal = krb5_gss_oid_array+6; +const gss_OID_desc * const GSS_KRB5_NT_PRINCIPAL_NAME = krb5_gss_oid_array+5; static const gss_OID_set_desc oidsets[] = { - {1, (gss_OID) krb5_gss_oid_array+0}, - {1, (gss_OID) krb5_gss_oid_array+1}, - {3, (gss_OID) krb5_gss_oid_array+0}, + {1, (gss_OID) krb5_gss_oid_array+0}, /* RFC OID */ + {1, (gss_OID) krb5_gss_oid_array+1}, /* pre-RFC OID */ + {4, (gss_OID) krb5_gss_oid_array+0}, /* includes wrong OID & IAKERB */ {1, (gss_OID) krb5_gss_oid_array+2}, {3, (gss_OID) krb5_gss_oid_array+0}, }; @@ -697,18 +699,48 @@ static struct gss_config krb5_mechanism = { NULL, /* set_neg_mechs */ }; +static struct gss_config_ext krb5_mechanism_ext = { + krb5_gss_acquire_cred_with_password, +}; + +static struct gss_config_ext iakerb_mechanism_ext = { + iakerb_gss_acquire_cred_with_password, +}; #ifdef _GSS_STATIC_LINK #include "mglueP.h" +static int gss_iakerbmechglue_init(void) +{ + struct gss_mech_config mech_iakerb; + struct gss_config iakerb_mechanism = krb5_mechanism; + + /* IAKERB mechanism mirrors krb5, but with different context SPIs */ + iakerb_mechanism.gss_accept_sec_context = iakerb_gss_accept_sec_context; + iakerb_mechanism.gss_init_sec_context = iakerb_gss_init_sec_context; + iakerb_mechanism.gss_delete_sec_context = iakerb_gss_delete_sec_context; + iakerb_mechanism.gss_acquire_cred = iakerb_gss_acquire_cred; + + memset(&mech_iakerb, 0, sizeof(mech_iakerb)); + mech_iakerb.mech = &iakerb_mechanism; + mech_iakerb.mech_ext = &iakerb_mechanism_ext; + + mech_iakerb.mechNameStr = "iakerb"; + mech_iakerb.mech_type = (gss_OID)gss_mech_iakerb; + gssint_register_mechinfo(&mech_iakerb); + + return 0; +} + static int gss_krb5mechglue_init(void) { struct gss_mech_config mech_krb5; memset(&mech_krb5, 0, sizeof(mech_krb5)); mech_krb5.mech = &krb5_mechanism; + mech_krb5.mech_ext = &krb5_mechanism_ext; + mech_krb5.mechNameStr = "kerberos_v5"; mech_krb5.mech_type = (gss_OID)gss_mech_krb5; - gssint_register_mechinfo(&mech_krb5); mech_krb5.mechNameStr = "kerberos_v5_old"; @@ -769,6 +801,9 @@ int gss_krb5int_lib_init(void) err = gss_krb5mechglue_init(); if (err) return err; + err = gss_iakerbmechglue_init(); + if (err) + return err; #endif return 0; diff --git a/src/lib/gssapi/krb5/gssapi_krb5.hin b/src/lib/gssapi/krb5/gssapi_krb5.hin index 783387a52..ce96454fe 100644 --- a/src/lib/gssapi/krb5/gssapi_krb5.hin +++ b/src/lib/gssapi/krb5/gssapi_krb5.hin @@ -75,6 +75,7 @@ GSS_DLLIMP extern const gss_OID_desc * const GSS_KRB5_NT_PRINCIPAL_NAME; GSS_DLLIMP extern const gss_OID_desc * const gss_mech_krb5; GSS_DLLIMP extern const gss_OID_desc * const gss_mech_krb5_old; GSS_DLLIMP extern const gss_OID_desc * const gss_mech_krb5_wrong; +GSS_DLLIMP extern const gss_OID_desc * const gss_mech_iakerb; GSS_DLLIMP extern const gss_OID_set_desc * const gss_mech_set_krb5; GSS_DLLIMP extern const gss_OID_set_desc * const gss_mech_set_krb5_old; GSS_DLLIMP extern const gss_OID_set_desc * const gss_mech_set_krb5_both; diff --git a/src/lib/gssapi/krb5/iakerb.c b/src/lib/gssapi/krb5/iakerb.c new file mode 100644 index 000000000..3463a7f71 --- /dev/null +++ b/src/lib/gssapi/krb5/iakerb.c @@ -0,0 +1,1046 @@ +/* -*- mode: c; indent-tabs-mode: nil -*- */ +/* + * Copyright 2009 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + */ +#include "k5-int.h" +#include "gssapiP_krb5.h" + +/* + * IAKERB implementation + */ + +extern int gssint_get_der_length(unsigned char **, OM_uint32, unsigned int*); + +enum iakerb_state { + IAKERB_AS_REQ, /* acquiring ticket with initial creds */ + IAKERB_TGS_REQ, /* acquiring ticket with TGT */ + IAKERB_AP_REQ /* hand-off to normal GSS AP-REQ exchange */ +}; + +struct _iakerb_ctx_id_rec { + krb5_magic magic; /* KG_IAKERB_CONTEXT */ + krb5_context k5c; + enum iakerb_state state; /* Initiator only */ + krb5_init_creds_context icc; /* Initiator only */ + krb5_tkt_creds_context tcc; /* Initiator only */ + gss_ctx_id_t gssc; + krb5_data conv; /* conversation for checksumming */ + unsigned int count; /* number of round trips */ + krb5_get_init_creds_opt *gic_opts; +}; + +#define IAKERB_MAX_HOPS ( 16 /* MAX_IN_TKT_LOOPS */ + KRB5_REFERRAL_MAXHOPS ) + +typedef struct _iakerb_ctx_id_rec iakerb_ctx_id_rec; +typedef iakerb_ctx_id_rec *iakerb_ctx_id_t; + +/* + * Release an IAKERB context + */ +static void +iakerb_release_context(iakerb_ctx_id_t ctx) +{ + OM_uint32 tmp; + + if (ctx == NULL) + return; + + krb5_init_creds_free(ctx->k5c, ctx->icc); + krb5_tkt_creds_free(ctx->k5c, ctx->tcc); + krb5_gss_delete_sec_context(&tmp, &ctx->gssc, NULL); + krb5_free_data_contents(ctx->k5c, &ctx->conv); + krb5_get_init_creds_opt_free(ctx->k5c, ctx->gic_opts); + krb5_free_context(ctx->k5c); + free(ctx); +} + +/* + * Create a IAKERB-FINISHED structure containing a checksum of + * the entire IAKERB exchange. + */ +krb5_error_code +iakerb_make_finished(krb5_context context, + krb5_key key, + const krb5_data *conv, + krb5_data **finished) +{ + krb5_error_code code; + krb5_iakerb_finished iaf; + + *finished = NULL; + + memset(&iaf, 0, sizeof(iaf)); + + if (key == NULL) + return KRB5KDC_ERR_NULL_KEY; + + code = krb5_k_make_checksum(context, 0, key, KRB5_KEYUSAGE_IAKERB_FINISHED, + conv, &iaf.checksum); + if (code != 0) + return code; + + code = encode_krb5_iakerb_finished(&iaf, finished); + + krb5_free_checksum_contents(context, &iaf.checksum); + + return code; +} + +/* + * Verify a IAKERB-FINISHED structure submitted by the initiator + */ +krb5_error_code +iakerb_verify_finished(krb5_context context, + krb5_key key, + const krb5_data *conv, + const krb5_data *finished) +{ + krb5_error_code code; + krb5_iakerb_finished *iaf; + krb5_boolean valid = FALSE; + + if (key == NULL) + return KRB5KDC_ERR_NULL_KEY; + + code = decode_krb5_iakerb_finished(finished, &iaf); + if (code != 0) + return code; + + code = krb5_k_verify_checksum(context, key, KRB5_KEYUSAGE_IAKERB_FINISHED, + conv, &iaf->checksum, &valid); + if (code == 0 && valid == FALSE) + code = KRB5KRB_AP_ERR_BAD_INTEGRITY; + + krb5_free_iakerb_finished(context, iaf); + + return code; +} + +/* + * Save a token for future checksumming. + */ +static krb5_error_code +iakerb_save_token(iakerb_ctx_id_t ctx, const gss_buffer_t token) +{ + char *p; + + p = realloc(ctx->conv.data, ctx->conv.length + token->length); + if (p == NULL) + return ENOMEM; + + memcpy(p + ctx->conv.length, token->value, token->length); + ctx->conv.data = p; + ctx->conv.length += token->length; + + return 0; +} + +/* + * Parse a token into IAKERB-HEADER and KRB-KDC-REQ/REP + */ +static krb5_error_code +iakerb_parse_token(iakerb_ctx_id_t ctx, + int initialContextToken, + const gss_buffer_t token, + krb5_data *realm, + krb5_data **cookie, + krb5_data *request) +{ + krb5_error_code code; + krb5_iakerb_header *iah = NULL; + unsigned int bodysize, lenlen; + int length; + unsigned char *ptr; + int flags = 0; + krb5_data data; + + if (token == GSS_C_NO_BUFFER || token->length == 0) { + code = KRB5_BAD_MSIZE; + goto cleanup; + } + + if (initialContextToken) + flags |= G_VFY_TOKEN_HDR_WRAPPER_REQUIRED; + + ptr = token->value; + + code = g_verify_token_header(gss_mech_iakerb, + &bodysize, &ptr, + IAKERB_TOK_PROXY, + token->length, flags); + if (code != 0) + goto cleanup; + + data.data = (char *)ptr; + + if (bodysize-- == 0 || *ptr++ != 0x30 /* SEQUENCE */) { + code = ASN1_BAD_ID; + goto cleanup; + } + + length = gssint_get_der_length(&ptr, bodysize, &lenlen); + if (length < 0 || bodysize - lenlen < (unsigned int)length) { + code = KRB5_BAD_MSIZE; + goto cleanup; + } + data.length = 1 /* SEQUENCE */ + lenlen + length; + + ptr += length; + bodysize -= (lenlen + length); + + code = decode_krb5_iakerb_header(&data, &iah); + if (code != 0) + goto cleanup; + + if (realm != NULL) { + *realm = iah->target_realm; + iah->target_realm.data = NULL; + } + + if (cookie != NULL) { + *cookie = iah->cookie; + iah->cookie = NULL; + } + + request->data = (char *)ptr; + request->length = bodysize; + + assert(request->data + request->length == + (char *)token->value + token->length); + +cleanup: + krb5_free_iakerb_header(ctx->k5c, iah); + + return code; +} + +/* + * Create a token from IAKERB-HEADER and KRB-KDC-REQ/REP + */ +static krb5_error_code +iakerb_make_token(iakerb_ctx_id_t ctx, + krb5_data *realm, + krb5_data *cookie, + krb5_data *request, + int initialContextToken, + gss_buffer_t token) +{ + krb5_error_code code; + krb5_iakerb_header iah; + krb5_data *data = NULL; + char *p; + unsigned int tokenSize; + unsigned char *q; + + token->value = NULL; + token->length = 0; + + /* + * Assemble the IAKERB-HEADER from the realm and cookie + */ + memset(&iah, 0, sizeof(iah)); + iah.target_realm = *realm; + iah.cookie = cookie; + + code = encode_krb5_iakerb_header(&iah, &data); + if (code != 0) + goto cleanup; + + /* + * Concatenate Kerberos request. + */ + p = realloc(data->data, data->length + request->length); + if (p == NULL) { + code = ENOMEM; + goto cleanup; + } + data->data = p; + + memcpy(data->data + data->length, request->data, request->length); + data->length += request->length; + + if (initialContextToken) + tokenSize = g_token_size(gss_mech_iakerb, data->length); + else + tokenSize = 2 + data->length; + + token->value = q = k5alloc(tokenSize, &code); + if (code != 0) + goto cleanup; + token->length = tokenSize; + + if (initialContextToken) { + g_make_token_header(gss_mech_iakerb, data->length, &q, + IAKERB_TOK_PROXY); + } else { + store_16_be(IAKERB_TOK_PROXY, q); + q += 2; + } + memcpy(q, data->data, data->length); + q += data->length; + + assert(q == (unsigned char *)token->value + token->length); + +cleanup: + krb5_free_data(ctx->k5c, data); + + return code; +} + +/* + * Parse the IAKERB token in input_token and send the contained KDC + * request to the KDC for the realm. + * + * Wrap the KDC reply in output_token. + */ +static krb5_error_code +iakerb_acceptor_step(iakerb_ctx_id_t ctx, + int initialContextToken, + const gss_buffer_t input_token, + gss_buffer_t output_token) +{ + krb5_error_code code; + krb5_data request = empty_data(), reply = empty_data(); + krb5_data realm = empty_data(); + OM_uint32 tmp; + int tcp_only, use_master; + krb5_ui_4 kdc_code; + + output_token->length = 0; + output_token->value = NULL; + + if (ctx->count >= IAKERB_MAX_HOPS) { + code = KRB5_KDC_UNREACH; + goto cleanup; + } + + code = iakerb_parse_token(ctx, initialContextToken, input_token, &realm, + NULL, &request); + if (code != 0) + goto cleanup; + + if (realm.length == 0 || request.length == 0) { + code = KRB5_BAD_MSIZE; + goto cleanup; + } + + code = iakerb_save_token(ctx, input_token); + if (code != 0) + goto cleanup; + + for (tcp_only = 0; tcp_only <= 1; tcp_only++) { + use_master = 0; + code = krb5_sendto_kdc(ctx->k5c, &request, &realm, + &reply, &use_master, tcp_only); + if (code == 0 && krb5_is_krb_error(&reply)) { + krb5_error *error; + + code = decode_krb5_error(&reply, &error); + if (code != 0) + goto cleanup; + kdc_code = error->error; + krb5_free_error(ctx->k5c, error); + if (kdc_code == KRB_ERR_RESPONSE_TOO_BIG) { + krb5_free_data_contents(ctx->k5c, &reply); + reply = empty_data(); + continue; + } + } + break; + } + + if (code == KRB5_KDC_UNREACH || code == KRB5_REALM_UNKNOWN) { + krb5_error error; + + memset(&error, 0, sizeof(error)); + if (code == KRB5_KDC_UNREACH) + error.error = KRB_AP_ERR_IAKERB_KDC_NO_RESPONSE; + else if (code == KRB5_REALM_UNKNOWN) + error.error = KRB_AP_ERR_IAKERB_KDC_NOT_FOUND; + + code = krb5_mk_error(ctx->k5c, &error, &reply); + if (code != 0) + goto cleanup; + } else if (code != 0) + goto cleanup; + + code = iakerb_make_token(ctx, &realm, NULL, &reply, 0, output_token); + if (code != 0) + goto cleanup; + + code = iakerb_save_token(ctx, output_token); + if (code != 0) + goto cleanup; + + ctx->count++; + +cleanup: + if (code != 0) + gss_release_buffer(&tmp, output_token); + /* request is a pointer into input_token, no need to free */ + krb5_free_data_contents(ctx->k5c, &realm); + krb5_free_data_contents(ctx->k5c, &reply); + + return code; +} + +/* + * Initialise the krb5_init_creds context for the IAKERB context + */ +static krb5_error_code +iakerb_init_creds_ctx(iakerb_ctx_id_t ctx, + krb5_gss_cred_id_t cred, + OM_uint32 time_req) +{ + krb5_error_code code; + + if (cred->iakerb_mech == 0 || cred->password.data == NULL) { + code = EINVAL; + goto cleanup; + } + + assert(cred->name != NULL); + assert(cred->name->princ != NULL); + + code = krb5_get_init_creds_opt_alloc(ctx->k5c, &ctx->gic_opts); + if (code != 0) + goto cleanup; + + if (time_req != 0 && time_req != GSS_C_INDEFINITE) + krb5_get_init_creds_opt_set_tkt_life(ctx->gic_opts, time_req); + + code = krb5_get_init_creds_opt_set_out_ccache(ctx->k5c, ctx->gic_opts, + cred->ccache); + if (code != 0) + goto cleanup; + + code = krb5_init_creds_init(ctx->k5c, + cred->name->princ, + NULL, /* prompter */ + NULL, /* data */ + 0, /* start_time */ + ctx->gic_opts, + &ctx->icc); + if (code != 0) + goto cleanup; + + code = krb5_init_creds_set_password(ctx->k5c, ctx->icc, + cred->password.data); + if (code != 0) + goto cleanup; + +cleanup: + return code; +} + +/* + * Initialise the krb5_tkt_creds context for the IAKERB context + */ +static krb5_error_code +iakerb_tkt_creds_ctx(iakerb_ctx_id_t ctx, + krb5_gss_cred_id_t cred, + krb5_gss_name_t name, + OM_uint32 time_req) + +{ + krb5_error_code code; + krb5_creds creds; + krb5_timestamp now; + + assert(cred->name != NULL); + assert(cred->name->princ != NULL); + + memset(&creds, 0, sizeof(creds)); + + creds.client = cred->name->princ; + creds.server = name->princ; + + if (time_req != 0 && time_req != GSS_C_INDEFINITE) { + code = krb5_timeofday(ctx->k5c, &now); + if (code != 0) + goto cleanup; + + creds.times.endtime = now + time_req; + } + + if (cred->name->ad_context != NULL) { + code = krb5_authdata_export_authdata(ctx->k5c, + cred->name->ad_context, + AD_USAGE_TGS_REQ, + &creds.authdata); + if (code != 0) + goto cleanup; + } + + code = krb5_tkt_creds_init(ctx->k5c, cred->ccache, &creds, 0, &ctx->tcc); + if (code != 0) + goto cleanup; + +cleanup: + krb5_free_authdata(ctx->k5c, creds.authdata); + + return code; +} + +/* + * Parse the IAKERB token in input_token and process the KDC + * response. + * + * Emit the next KDC request, if any, in output_token. + */ +static krb5_error_code +iakerb_initiator_step(iakerb_ctx_id_t ctx, + krb5_gss_cred_id_t cred, + krb5_gss_name_t name, + OM_uint32 time_req, + const gss_buffer_t input_token, + gss_buffer_t output_token) +{ + krb5_error_code code; + krb5_data in, out, realm, *cookie = NULL; + OM_uint32 tmp; + int initialContextToken = (input_token == GSS_C_NO_BUFFER); + unsigned int flags = 0; + krb5_ticket_times times; + + output_token->length = 0; + output_token->value = NULL; + + in.data = NULL; + in.length = 0; + out.data = NULL; + out.length = 0; + realm.data = NULL; + realm.length = 0; + + if (initialContextToken) { + in.data = NULL; + in.length = 0; + } else { + code = iakerb_parse_token(ctx, + 0, + input_token, + NULL, + &cookie, + &in); + if (code != 0) + goto cleanup; + + code = iakerb_save_token(ctx, input_token); + if (code != 0) + goto cleanup; + } + + switch (ctx->state) { + case IAKERB_AS_REQ: + if (ctx->icc == NULL) { + code = iakerb_init_creds_ctx(ctx, cred, time_req); + if (code != 0) + goto cleanup; + } + + code = krb5_init_creds_step(ctx->k5c, ctx->icc, &in, &out, &realm, + &flags); + if (code != 0) + goto cleanup; + if (!(flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE)) { + krb5_init_creds_get_times(ctx->k5c, ctx->icc, ×); + cred->tgt_expire = times.endtime; + + krb5_init_creds_free(ctx->k5c, ctx->icc); + ctx->icc = NULL; + + ctx->state = IAKERB_TGS_REQ; + } else + break; + in = empty_data(); + case IAKERB_TGS_REQ: + if (ctx->tcc == NULL) { + code = iakerb_tkt_creds_ctx(ctx, cred, name, time_req); + if (code != 0) + goto cleanup; + } + + code = krb5_tkt_creds_step(ctx->k5c, ctx->tcc, &in, &out, &realm, + &flags); + if (code != 0) + goto cleanup; + if (!(flags & KRB5_TKT_CREDS_STEP_FLAG_CONTINUE)) { + krb5_tkt_creds_get_times(ctx->k5c, ctx->tcc, ×); + cred->tgt_expire = times.endtime; + + krb5_tkt_creds_free(ctx->k5c, ctx->tcc); + ctx->tcc = NULL; + + ctx->state = IAKERB_AP_REQ; + } else + break; + case IAKERB_AP_REQ: + break; + } + + if (out.length != 0) { + assert(ctx->state != IAKERB_AP_REQ); + + code = iakerb_make_token(ctx, &realm, cookie, &out, + (input_token == GSS_C_NO_BUFFER), + output_token); + if (code != 0) + goto cleanup; + + /* Save the token for generating a future checksum */ + code = iakerb_save_token(ctx, output_token); + if (code != 0) + goto cleanup; + + ctx->count++; + } + +cleanup: + if (code != 0) + gss_release_buffer(&tmp, output_token); + krb5_free_data(ctx->k5c, cookie); + krb5_free_data_contents(ctx->k5c, &out); + krb5_free_data_contents(ctx->k5c, &realm); + + return code; +} + +/* + * Determine the starting IAKERB state for a context. If we already + * have a ticket, we may not need to do IAKERB at all. + */ +static krb5_error_code +iakerb_get_initial_state(iakerb_ctx_id_t ctx, + krb5_gss_cred_id_t cred, + krb5_gss_name_t target, + OM_uint32 time_req, + enum iakerb_state *state) +{ + krb5_creds in_creds, *out_creds = NULL; + krb5_error_code code; + + memset(&in_creds, 0, sizeof(in_creds)); + + in_creds.client = cred->name->princ; + in_creds.server = target->princ; + + if (cred->name->ad_context != NULL) { + code = krb5_authdata_export_authdata(ctx->k5c, + cred->name->ad_context, + AD_USAGE_TGS_REQ, + &in_creds.authdata); + if (code != 0) + goto cleanup; + } + + if (time_req != 0 && time_req != GSS_C_INDEFINITE) { + krb5_timestamp now; + + code = krb5_timeofday(ctx->k5c, &now); + if (code != 0) + goto cleanup; + + in_creds.times.endtime = now + time_req; + } + + code = krb5_get_credentials(ctx->k5c, KRB5_GC_CACHED, + cred->ccache, + &in_creds, &out_creds); + if (code == KRB5_CC_NOTFOUND || code == KRB5_CC_NOT_KTYPE) { + krb5_principal tgs; + krb5_data *realm = krb5_princ_realm(ctx->k5c, in_creds.client); + + /* If we have a TGT for the client realm, can proceed to TGS-REQ. */ + code = krb5_build_principal_ext(ctx->k5c, + &tgs, + realm->length, + realm->data, + KRB5_TGS_NAME_SIZE, + KRB5_TGS_NAME, + realm->length, + realm->data, + NULL); + if (code != 0) + goto cleanup; + + in_creds.server = tgs; + + /* It would be nice if we could return KRB5KRB_AP_ERR_TKT_EXPIRED if + * the TGT is expired, for consistency with the krb5 mech. As it + * stands, we won't see the expired TGT and will return + * KRB5_CC_NOTFOUND. */ + code = krb5_get_credentials(ctx->k5c, KRB5_GC_CACHED, + cred->ccache, + &in_creds, &out_creds); + if (code == KRB5_CC_NOTFOUND && cred->password.data != NULL) { + *state = IAKERB_AS_REQ; + code = 0; + } else if (code == 0) { + *state = IAKERB_TGS_REQ; + krb5_free_creds(ctx->k5c, out_creds); + } + krb5_free_principal(ctx->k5c, tgs); + } else if (code == 0) { + *state = IAKERB_AP_REQ; + krb5_free_creds(ctx->k5c, out_creds); + } + +cleanup: + krb5_free_authdata(ctx->k5c, in_creds.authdata); + + return code; +} + +/* + * Allocate and initialise an IAKERB context + */ +static krb5_error_code +iakerb_alloc_context(iakerb_ctx_id_t *pctx) +{ + iakerb_ctx_id_t ctx; + krb5_error_code code; + + *pctx = NULL; + + ctx = k5alloc(sizeof(*ctx), &code); + if (ctx == NULL) + goto cleanup; + ctx->magic = KG_IAKERB_CONTEXT; + ctx->state = IAKERB_AS_REQ; + ctx->count = 0; + + code = krb5_gss_init_context(&ctx->k5c); + if (code != 0) + goto cleanup; + + *pctx = ctx; + +cleanup: + if (code != 0) + iakerb_release_context(ctx); + + return code; +} + +/* + * Delete an IAKERB context. This can also accept Kerberos context + * handles. The heuristic is similar to SPNEGO's delete_sec_context. + */ +OM_uint32 +iakerb_gss_delete_sec_context(OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + gss_buffer_t output_token) +{ + OM_uint32 major_status = GSS_S_COMPLETE; + + if (output_token != GSS_C_NO_BUFFER) { + output_token->length = 0; + output_token->value = NULL; + } + + *minor_status = 0; + + if (*context_handle != GSS_C_NO_CONTEXT) { + iakerb_ctx_id_t iakerb_ctx = (iakerb_ctx_id_t)*context_handle; + + if (iakerb_ctx->magic == KG_IAKERB_CONTEXT) { + iakerb_release_context(iakerb_ctx); + *context_handle = GSS_C_NO_CONTEXT; + } else { + assert(iakerb_ctx->magic == KG_CONTEXT); + + major_status = krb5_gss_delete_sec_context(minor_status, + context_handle, + output_token); + } + } + + return major_status; +} + +static krb5_boolean +iakerb_is_iakerb_token(const gss_buffer_t token) +{ + krb5_error_code code; + unsigned int bodysize = token->length; + unsigned char *ptr = token->value; + + code = g_verify_token_header(gss_mech_iakerb, + &bodysize, &ptr, + IAKERB_TOK_PROXY, + token->length, 0); + + return (code == 0); +} + +static void +iakerb_make_exts(iakerb_ctx_id_t ctx, krb5_gss_ctx_ext_rec *exts) +{ + memset(exts, 0, sizeof(*exts)); + + if (ctx->conv.length != 0) + exts->iakerb.conv = &ctx->conv; +} + +/* + * + */ +OM_uint32 +iakerb_gss_accept_sec_context(OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + gss_cred_id_t verifier_cred_handle, + gss_buffer_t input_token, + gss_channel_bindings_t input_chan_bindings, + gss_name_t *src_name, + gss_OID *mech_type, + gss_buffer_t output_token, + OM_uint32 *ret_flags, + OM_uint32 *time_rec, + gss_cred_id_t *delegated_cred_handle) +{ + OM_uint32 major_status = GSS_S_FAILURE; + OM_uint32 code; + iakerb_ctx_id_t ctx; + int initialContextToken = (*context_handle == GSS_C_NO_CONTEXT); + + if (initialContextToken) { + code = iakerb_alloc_context(&ctx); + if (code != 0) + goto cleanup; + + } else + ctx = (iakerb_ctx_id_t)*context_handle; + + if (iakerb_is_iakerb_token(input_token)) { + if (ctx->gssc != GSS_C_NO_CONTEXT) { + /* We shouldn't get an IAKERB token now. */ + code = G_WRONG_TOKID; + major_status = GSS_S_DEFECTIVE_TOKEN; + goto cleanup; + } + code = iakerb_acceptor_step(ctx, initialContextToken, + input_token, output_token); + if (code == (OM_uint32)KRB5_BAD_MSIZE) + major_status = GSS_S_DEFECTIVE_TOKEN; + if (code != 0) + goto cleanup; + if (initialContextToken) { + *context_handle = (gss_ctx_id_t)ctx; + ctx = NULL; + } + if (src_name != NULL) + *src_name = GSS_C_NO_NAME; + if (mech_type != NULL) + *mech_type = (gss_OID)gss_mech_iakerb; + if (ret_flags != NULL) + *ret_flags = 0; + if (time_rec != NULL) + *time_rec = 0; + if (delegated_cred_handle != NULL) + *delegated_cred_handle = GSS_C_NO_CREDENTIAL; + major_status = GSS_S_CONTINUE_NEEDED; + } else { + krb5_gss_ctx_ext_rec exts; + + iakerb_make_exts(ctx, &exts); + + major_status = krb5_gss_accept_sec_context_ext(&code, + &ctx->gssc, + verifier_cred_handle, + input_token, + input_chan_bindings, + src_name, + mech_type, + output_token, + ret_flags, + time_rec, + delegated_cred_handle, + &exts); + if (major_status == GSS_S_COMPLETE) { + *context_handle = ctx->gssc; + ctx->gssc = NULL; + iakerb_release_context(ctx); + } + } + +cleanup: + if (initialContextToken && GSS_ERROR(major_status)) { + iakerb_release_context(ctx); + *context_handle = GSS_C_NO_CONTEXT; + } + + *minor_status = code; + return major_status; +} + +OM_uint32 +iakerb_gss_init_sec_context(OM_uint32 *minor_status, + gss_cred_id_t claimant_cred_handle, + gss_ctx_id_t *context_handle, + gss_name_t target_name, + gss_OID mech_type, + OM_uint32 req_flags, + OM_uint32 time_req, + gss_channel_bindings_t input_chan_bindings, + gss_buffer_t input_token, + gss_OID *actual_mech_type, + gss_buffer_t output_token, + OM_uint32 *ret_flags, + OM_uint32 *time_rec) +{ + OM_uint32 major_status = GSS_S_FAILURE; + OM_uint32 tmpmin; + krb5_error_code code; + iakerb_ctx_id_t ctx; + gss_cred_id_t defcred = GSS_C_NO_CREDENTIAL; + krb5_gss_cred_id_t kcred; + krb5_gss_name_t kname; + int credLocked = 0; + int initialContextToken = (*context_handle == GSS_C_NO_CONTEXT); + + if (initialContextToken) { + code = iakerb_alloc_context(&ctx); + if (code != 0) { + *minor_status = code; + goto cleanup; + } + } else + ctx = (iakerb_ctx_id_t)*context_handle; + + if (!kg_validate_name(target_name)) { + *minor_status = G_VALIDATE_FAILED; + major_status = GSS_S_CALL_BAD_STRUCTURE | GSS_S_BAD_NAME; + goto cleanup; + } + + kname = (krb5_gss_name_t)target_name; + + if (claimant_cred_handle != GSS_C_NO_CREDENTIAL) { + major_status = krb5_gss_validate_cred_1(minor_status, + claimant_cred_handle, + ctx->k5c); + if (GSS_ERROR(major_status)) + goto cleanup; + + credLocked = 1; + kcred = (krb5_gss_cred_id_t)claimant_cred_handle; + } else { + major_status = iakerb_gss_acquire_cred(minor_status, NULL, + GSS_C_INDEFINITE, + GSS_C_NULL_OID_SET, + GSS_C_INITIATE, + &defcred, NULL, NULL); + if (GSS_ERROR(major_status)) + goto cleanup; + kcred = (krb5_gss_cred_id_t)defcred; + } + + major_status = GSS_S_FAILURE; + + if (initialContextToken) { + code = iakerb_get_initial_state(ctx, kcred, kname, time_req, + &ctx->state); + if (code != 0) { + *minor_status = code; + goto cleanup; + } + *context_handle = (gss_ctx_id_t)ctx; + } + + if (ctx->state != IAKERB_AP_REQ) { + /* We need to do IAKERB. */ + code = iakerb_initiator_step(ctx, + kcred, + kname, + time_req, + input_token, + output_token); + if (code == KRB5_BAD_MSIZE) + major_status = GSS_S_DEFECTIVE_TOKEN; + if (code != 0) { + *minor_status = code; + goto cleanup; + } + } + + if (ctx->state == IAKERB_AP_REQ) { + krb5_gss_ctx_ext_rec exts; + + /* Ensure cred is marked as usable for Kerberos mechanism */ + if (kcred != NULL) { + if (kcred->iakerb_mech) + kcred->rfc_mech = 1; + + k5_mutex_unlock(&kcred->lock); + credLocked = 0; + } + + iakerb_make_exts(ctx, &exts); + + if (ctx->gssc == GSS_C_NO_CONTEXT) + input_token = GSS_C_NO_BUFFER; + + /* IAKERB is finished, or we skipped to Kerberos directly. */ + major_status = krb5_gss_init_sec_context_ext(minor_status, + (gss_cred_id_t) kcred, + &ctx->gssc, + target_name, + GSS_C_NULL_OID, + req_flags, + time_req, + input_chan_bindings, + input_token, + actual_mech_type, + output_token, + ret_flags, + time_rec, + &exts); + if (major_status == GSS_S_COMPLETE) { + *context_handle = ctx->gssc; + ctx->gssc = GSS_C_NO_CONTEXT; + iakerb_release_context(ctx); + } + } else { + if (actual_mech_type != NULL) + *actual_mech_type = (gss_OID)gss_mech_iakerb; + if (ret_flags != NULL) + *ret_flags = 0; + if (time_rec != NULL) + *time_rec = 0; + major_status = GSS_S_CONTINUE_NEEDED; + } + +cleanup: + if (credLocked) + k5_mutex_unlock(&kcred->lock); + if (initialContextToken && GSS_ERROR(major_status)) { + iakerb_release_context(ctx); + *context_handle = GSS_C_NO_CONTEXT; + } + krb5_gss_release_cred(&tmpmin, &defcred); + + return major_status; +} + diff --git a/src/lib/gssapi/krb5/init_sec_context.c b/src/lib/gssapi/krb5/init_sec_context.c index b81d5b312..a7bff2f5d 100644 --- a/src/lib/gssapi/krb5/init_sec_context.c +++ b/src/lib/gssapi/krb5/init_sec_context.c @@ -197,8 +197,38 @@ static krb5_error_code get_credentials(context, cred, server, now, goto cleanup; } + /* Don't go out over the network if we used IAKERB */ + if (cred->iakerb_mech) + flags |= KRB5_GC_CACHED; + code = krb5_get_credentials(context, flags, cred->ccache, &in_creds, &result_creds); + if (code == KRB5_CC_NOTFOUND && cred->password.data != NULL && + !cred->iakerb_mech) { + krb5_creds tgt_creds; + + memset(&tgt_creds, 0, sizeof(tgt_creds)); + + /* No TGT in the ccache, but we can get one with the password. */ + code = krb5_get_init_creds_password(context, &tgt_creds, + in_creds.client, + cred->password.data, + NULL, NULL, + 0, NULL, NULL); + if (code) + goto cleanup; + + code = krb5_cc_store_cred(context, cred->ccache, &tgt_creds); + if (code) { + krb5_free_cred_contents(context, &tgt_creds); + goto cleanup; + } + cred->tgt_expire = tgt_creds.times.endtime; + krb5_free_cred_contents(context, &tgt_creds); + + code = krb5_get_credentials(context, flags, cred->ccache, + &in_creds, &result_creds); + } if (code) goto cleanup; @@ -237,6 +267,7 @@ struct gss_checksum_data { krb5_gss_cred_id_t cred; krb5_checksum md5; krb5_data checksum_data; + krb5_gss_ctx_ext_t exts; }; #ifdef CFX_EXERCISE @@ -252,6 +283,7 @@ make_gss_checksum (krb5_context context, krb5_auth_context auth_context, struct gss_checksum_data *data = cksum_data; krb5_data credmsg; unsigned int junk; + krb5_data *finished = NULL; data->checksum_data.data = 0; credmsg.data = 0; @@ -284,8 +316,8 @@ make_gss_checksum (krb5_context context, krb5_auth_context auth_context, data->checksum_data.length = 24; } else { if (credmsg.length+28 > KRB5_INT16_MAX) { - krb5_free_data_contents(context, &credmsg); - return(KRB5KRB_ERR_FIELD_TOOLONG); + code = KRB5KRB_ERR_FIELD_TOOLONG; + goto cleanup; } data->checksum_data.length = 28+credmsg.length; @@ -307,6 +339,26 @@ make_gss_checksum (krb5_context context, krb5_auth_context auth_context, junk = 0; #endif + assert(data->exts != NULL); + + if (data->exts->iakerb.conv) { + krb5_key key; + + code = krb5_auth_con_getsendsubkey_k(context, auth_context, &key); + if (code != 0) + goto cleanup; + + code = iakerb_make_finished(context, key, data->exts->iakerb.conv, + &finished); + if (code != 0) { + krb5_k_free_key(context, key); + goto cleanup; + } + + krb5_k_free_key(context, key); + data->checksum_data.length += 8 + finished->length; + } + data->checksum_data.length += junk; /* now allocate a buffer to hold the checksum data and @@ -314,9 +366,8 @@ make_gss_checksum (krb5_context context, krb5_auth_context auth_context, if ((data->checksum_data.data = (char *) xmalloc(data->checksum_data.length)) == NULL) { - if (credmsg.data) - krb5_free_data_contents(context, &credmsg); - return(ENOMEM); + code = ENOMEM; + goto cleanup; } ptr = (unsigned char *)data->checksum_data.data; @@ -332,19 +383,25 @@ make_gss_checksum (krb5_context context, krb5_auth_context auth_context, TWRITE_INT16(ptr, KRB5_GSS_FOR_CREDS_OPTION, 0); TWRITE_INT16(ptr, credmsg.length, 0); TWRITE_STR(ptr, credmsg.data, credmsg.length); - - /* free credmsg data */ - krb5_free_data_contents(context, &credmsg); + } + if (data->exts->iakerb.conv) { + TWRITE_INT(ptr, KRB5_GSS_EXTS_IAKERB_FINISHED, 1); + TWRITE_INT(ptr, finished->length, 1); + TWRITE_STR(ptr, finished->data, finished->length); } if (junk) memset(ptr, 'i', junk); *out = &data->checksum_data; - return 0; + code = 0; +cleanup: + krb5_free_data_contents(context, &credmsg); + krb5_free_data(context, finished); + return code; } static krb5_error_code make_ap_req_v1(context, ctx, cred, k_cred, ad_context, - chan_bindings, mech_type, token) + chan_bindings, mech_type, token, exts) krb5_context context; krb5_gss_ctx_id_rec *ctx; krb5_gss_cred_id_t cred; @@ -353,13 +410,13 @@ make_ap_req_v1(context, ctx, cred, k_cred, ad_context, gss_channel_bindings_t chan_bindings; gss_OID mech_type; gss_buffer_t token; + krb5_gss_ctx_ext_t exts; { krb5_flags mk_req_flags = 0; krb5_error_code code; struct gss_checksum_data cksum_struct; krb5_checksum md5; krb5_data ap_req; - krb5_data *checksum_data = NULL; unsigned char *ptr; unsigned char *t; unsigned int tlen; @@ -378,22 +435,9 @@ make_ap_req_v1(context, ctx, cred, k_cred, ad_context, cksum_struct.ctx = ctx; cksum_struct.cred = cred; cksum_struct.checksum_data.data = NULL; - switch (k_cred->keyblock.enctype) { - case ENCTYPE_DES_CBC_CRC: - case ENCTYPE_DES_CBC_MD4: - case ENCTYPE_DES_CBC_MD5: - case ENCTYPE_DES3_CBC_SHA1: - code = make_gss_checksum(context, ctx->auth_context, &cksum_struct, - &checksum_data); - if (code) - goto cleanup; - break; - default: - krb5_auth_con_set_checksum_func(context, ctx->auth_context, - make_gss_checksum, &cksum_struct); - break; - } - + cksum_struct.exts = exts; + krb5_auth_con_set_checksum_func(context, ctx->auth_context, + make_gss_checksum, &cksum_struct); /* call mk_req. subkey and ap_req need to be used or destroyed */ @@ -404,7 +448,7 @@ make_ap_req_v1(context, ctx, cred, k_cred, ad_context, krb5_auth_con_set_authdata_context(context, ctx->auth_context, ad_context); code = krb5_mk_req_extended(context, &ctx->auth_context, mk_req_flags, - checksum_data, k_cred, &ap_req); + NULL, k_cred, &ap_req); krb5_auth_con_set_authdata_context(context, ctx->auth_context, NULL); krb5_free_data_contents(context, &cksum_struct.checksum_data); if (code) @@ -450,8 +494,6 @@ make_ap_req_v1(context, ctx, cred, k_cred, ad_context, code = 0; cleanup: - if (checksum_data && checksum_data->data) - krb5_free_data_contents(context, checksum_data); if (ap_req.data) krb5_free_data_contents(context, &ap_req); @@ -479,7 +521,8 @@ kg_new_connection( OM_uint32 *ret_flags, OM_uint32 *time_rec, krb5_context context, - int default_mech) + int default_mech, + krb5_gss_ctx_ext_t exts) { OM_uint32 major_status; krb5_error_code code; @@ -519,6 +562,7 @@ kg_new_connection( /* fill in the ctx */ memset(ctx, 0, sizeof(krb5_gss_ctx_id_rec)); + ctx->magic = KG_CONTEXT; ctx_free = ctx; if ((code = krb5_auth_con_init(context, &ctx->auth_context))) goto fail; @@ -597,7 +641,7 @@ kg_new_connection( if ((code = make_ap_req_v1(context, ctx, cred, k_cred, ctx->here->ad_context, input_chan_bindings, - mech_type, &token))) { + mech_type, &token, exts))) { if ((code == KRB5_FCC_NOFILE) || (code == KRB5_CC_NOTFOUND) || (code == KG_EMPTY_CCACHE)) major_status = GSS_S_NO_CRED; @@ -882,24 +926,21 @@ fail: } OM_uint32 -krb5_gss_init_sec_context(minor_status, claimant_cred_handle, - context_handle, target_name, mech_type, - req_flags, time_req, input_chan_bindings, - input_token, actual_mech_type, output_token, - ret_flags, time_rec) - OM_uint32 *minor_status; - gss_cred_id_t claimant_cred_handle; - gss_ctx_id_t *context_handle; - gss_name_t target_name; - gss_OID mech_type; - OM_uint32 req_flags; - OM_uint32 time_req; - gss_channel_bindings_t input_chan_bindings; - gss_buffer_t input_token; - gss_OID *actual_mech_type; - gss_buffer_t output_token; - OM_uint32 *ret_flags; - OM_uint32 *time_rec; +krb5_gss_init_sec_context_ext( + OM_uint32 *minor_status, + gss_cred_id_t claimant_cred_handle, + gss_ctx_id_t *context_handle, + gss_name_t target_name, + gss_OID mech_type, + OM_uint32 req_flags, + OM_uint32 time_req, + gss_channel_bindings_t input_chan_bindings, + gss_buffer_t input_token, + gss_OID *actual_mech_type, + gss_buffer_t output_token, + OM_uint32 *ret_flags, + OM_uint32 *time_rec, + krb5_gss_ctx_ext_t exts) { krb5_context context; krb5_gss_cred_id_t cred; @@ -1017,7 +1058,7 @@ krb5_gss_init_sec_context(minor_status, claimant_cred_handle, time_req, input_chan_bindings, input_token, actual_mech_type, output_token, ret_flags, time_rec, - context, default_mech); + context, default_mech, exts); k5_mutex_unlock(&cred->lock); if (*context_handle == GSS_C_NO_CONTEXT) { save_error_info (*minor_status, context); @@ -1097,3 +1138,44 @@ krb5int_gss_use_kdc_context(OM_uint32 *minor_status, return GSS_S_COMPLETE; } #endif + +OM_uint32 +krb5_gss_init_sec_context(minor_status, claimant_cred_handle, + context_handle, target_name, mech_type, + req_flags, time_req, input_chan_bindings, + input_token, actual_mech_type, output_token, + ret_flags, time_rec) + OM_uint32 *minor_status; + gss_cred_id_t claimant_cred_handle; + gss_ctx_id_t *context_handle; + gss_name_t target_name; + gss_OID mech_type; + OM_uint32 req_flags; + OM_uint32 time_req; + gss_channel_bindings_t input_chan_bindings; + gss_buffer_t input_token; + gss_OID *actual_mech_type; + gss_buffer_t output_token; + OM_uint32 *ret_flags; + OM_uint32 *time_rec; +{ + krb5_gss_ctx_ext_rec exts; + + memset(&exts, 0, sizeof(exts)); + + return krb5_gss_init_sec_context_ext(minor_status, + claimant_cred_handle, + context_handle, + target_name, + mech_type, + req_flags, + time_req, + input_chan_bindings, + input_token, + actual_mech_type, + output_token, + ret_flags, + time_rec, + &exts); +} + diff --git a/src/lib/gssapi/krb5/inq_cred.c b/src/lib/gssapi/krb5/inq_cred.c index 9af0e4e85..8083b27cc 100644 --- a/src/lib/gssapi/krb5/inq_cred.c +++ b/src/lib/gssapi/krb5/inq_cred.c @@ -166,6 +166,10 @@ krb5_gss_inquire_cred(minor_status, cred_handle, name, lifetime_ret, (cred->rfc_mech && GSS_ERROR(ret = generic_gss_add_oid_set_member(minor_status, gss_mech_krb5, + &mechs))) || + (cred->iakerb_mech && + GSS_ERROR(ret = generic_gss_add_oid_set_member(minor_status, + gss_mech_iakerb, &mechs)))) { k5_mutex_unlock(&cred->lock); if (ret_name) diff --git a/src/lib/gssapi/krb5/inq_names.c b/src/lib/gssapi/krb5/inq_names.c index a3de420cb..19c477f83 100644 --- a/src/lib/gssapi/krb5/inq_names.c +++ b/src/lib/gssapi/krb5/inq_names.c @@ -44,7 +44,8 @@ krb5_gss_inquire_names_for_mech(minor_status, mechanism, name_types) */ if ((mechanism != GSS_C_NULL_OID) && !g_OID_equal(gss_mech_krb5, mechanism) && - !g_OID_equal(gss_mech_krb5_old, mechanism)) { + !g_OID_equal(gss_mech_krb5_old, mechanism) && + !g_OID_equal(gss_mech_iakerb, mechanism)) { *minor_status = 0; return(GSS_S_BAD_MECH); } diff --git a/src/lib/gssapi/krb5/rel_cred.c b/src/lib/gssapi/krb5/rel_cred.c index 05e24b23d..d1c571a2f 100644 --- a/src/lib/gssapi/krb5/rel_cred.c +++ b/src/lib/gssapi/krb5/rel_cred.c @@ -77,6 +77,11 @@ krb5_gss_release_cred(minor_status, cred_handle) if (cred->req_enctypes) free(cred->req_enctypes); + if (cred->password.data) { + zap(cred->password.data, cred->password.length); + krb5_free_data_contents(context, &cred->password); + } + xfree(cred); *cred_handle = NULL; diff --git a/src/lib/gssapi/krb5/ser_sctx.c b/src/lib/gssapi/krb5/ser_sctx.c index f5c1081cc..63a7bd82a 100644 --- a/src/lib/gssapi/krb5/ser_sctx.c +++ b/src/lib/gssapi/krb5/ser_sctx.c @@ -610,6 +610,7 @@ kg_ctx_internalize(kcontext, argp, buffer, lenremain) xmalloc(sizeof(krb5_gss_ctx_id_rec)))) { memset(ctx, 0, sizeof(krb5_gss_ctx_id_rec)); + ctx->magic = ibuf; ctx->k5_context = kcontext; /* Get static data */ diff --git a/src/lib/gssapi/libgssapi_krb5.exports b/src/lib/gssapi/libgssapi_krb5.exports index e038f5076..5b00fc218 100644 --- a/src/lib/gssapi/libgssapi_krb5.exports +++ b/src/lib/gssapi/libgssapi_krb5.exports @@ -9,6 +9,7 @@ GSS_C_NT_USER_NAME GSS_KRB5_NT_PRINCIPAL_NAME gss_accept_sec_context gss_acquire_cred +gss_acquire_cred_with_password gss_acquire_cred_impersonate_name gss_add_buffer_set_member gss_add_cred @@ -54,6 +55,7 @@ gss_krb5int_unseal_token_v3 gsskrb5_extract_authtime_from_sec_context gsskrb5_extract_authz_data_from_sec_context gss_map_name_to_any +gss_mech_iakerb gss_mech_krb5 gss_mech_krb5_old gss_mech_set_krb5 diff --git a/src/lib/gssapi/mechglue/Makefile.in b/src/lib/gssapi/mechglue/Makefile.in index f43571145..92cd6c99c 100644 --- a/src/lib/gssapi/mechglue/Makefile.in +++ b/src/lib/gssapi/mechglue/Makefile.in @@ -12,6 +12,7 @@ DEFS=-D_GSS_STATIC_LINK=1 SRCS = \ $(srcdir)/g_accept_sec_context.c \ $(srcdir)/g_acquire_cred.c \ + $(srcdir)/g_acquire_cred_with_pw.c \ $(srcdir)/g_acquire_cred_imp_name.c \ $(srcdir)/g_buffer_set.c \ $(srcdir)/g_canon_name.c \ @@ -67,6 +68,7 @@ SRCS = \ OBJS = \ $(OUTPRE)g_accept_sec_context.$(OBJEXT) \ $(OUTPRE)g_acquire_cred.$(OBJEXT) \ + $(OUTPRE)g_acquire_cred_with_pw.$(OBJEXT) \ $(OUTPRE)g_acquire_cred_imp_name.$(OBJEXT) \ $(OUTPRE)g_buffer_set.$(OBJEXT) \ $(OUTPRE)g_canon_name.$(OBJEXT) \ @@ -122,6 +124,7 @@ OBJS = \ STLIBOBJS = \ g_accept_sec_context.o \ g_acquire_cred.o \ + g_acquire_cred_with_pw.o \ g_acquire_cred_imp_name.o \ g_buffer_set.o \ g_canon_name.o \ diff --git a/src/lib/gssapi/mechglue/g_acquire_cred_with_pw.c b/src/lib/gssapi/mechglue/g_acquire_cred_with_pw.c new file mode 100644 index 000000000..f86630382 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_acquire_cred_with_pw.c @@ -0,0 +1,544 @@ +/* #pragma ident "@(#)g_acquire_cred.c 1.22 04/02/23 SMI" */ + +/* + * Copyright 1996 by Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Sun Microsystems not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Sun Microsystems makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * glue routine for gss_acquire_cred_with_password + */ + +#include "mglueP.h" +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#include +#include +#include + +static OM_uint32 +val_acq_cred_pw_args( + OM_uint32 *minor_status, + const gss_name_t desired_name, + const gss_buffer_t password, + OM_uint32 time_req, + const gss_OID_set desired_mechs, + int cred_usage, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec) +{ + + /* Initialize outputs. */ + + if (minor_status != NULL) + *minor_status = 0; + + if (output_cred_handle != NULL) + *output_cred_handle = GSS_C_NO_CREDENTIAL; + + if (actual_mechs != NULL) + *actual_mechs = GSS_C_NULL_OID_SET; + + if (time_rec != NULL) + *time_rec = 0; + + /* Validate arguments. */ + + if (minor_status == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (output_cred_handle == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (cred_usage != GSS_C_ACCEPT + && cred_usage != GSS_C_INITIATE + && cred_usage != GSS_C_BOTH) { + if (minor_status) { + *minor_status = EINVAL; + map_errcode(minor_status); + } + return GSS_S_FAILURE; + } + + if (password == GSS_C_NO_BUFFER || + password->length == 0 || + password->value == NULL) { + if (minor_status) { + *minor_status = EINVAL; + map_errcode(minor_status); + } + return GSS_S_FAILURE; + } + + return (GSS_S_COMPLETE); +} + + +OM_uint32 KRB5_CALLCONV +gss_acquire_cred_with_password( + minor_status, + desired_name, + password, + time_req, + desired_mechs, + cred_usage, + output_cred_handle, + actual_mechs, + time_rec) + +OM_uint32 * minor_status; +const gss_name_t desired_name; +const gss_buffer_t password; +OM_uint32 time_req; +const gss_OID_set desired_mechs; +int cred_usage; +gss_cred_id_t * output_cred_handle; +gss_OID_set * actual_mechs; +OM_uint32 * time_rec; +{ + OM_uint32 major = GSS_S_FAILURE; + OM_uint32 initTimeOut, acceptTimeOut, outTime = GSS_C_INDEFINITE; + gss_OID_set_desc default_OID_set; + gss_OID_set mechs; + gss_OID_desc default_OID; + gss_mechanism mech; + unsigned int i; + gss_union_cred_t creds; + + major = val_acq_cred_pw_args(minor_status, + desired_name, + password, + time_req, + desired_mechs, + cred_usage, + output_cred_handle, + actual_mechs, + time_rec); + if (major != GSS_S_COMPLETE) + return (major); + + /* Initial value needed below. */ + major = GSS_S_FAILURE; + + /* + * if desired_mechs equals GSS_C_NULL_OID_SET, then pick an + * appropriate default. We use the first mechanism in the + * mechansim list as the default. This set is created with + * statics thus needs not be freed + */ + if(desired_mechs == GSS_C_NULL_OID_SET) { + mech = gssint_get_mechanism(NULL); + if (mech == NULL) + return (GSS_S_BAD_MECH); + + mechs = &default_OID_set; + default_OID_set.count = 1; + default_OID_set.elements = &default_OID; + default_OID.length = mech->mech_type.length; + default_OID.elements = mech->mech_type.elements; + } else + mechs = desired_mechs; + + if (mechs->count == 0) + return (GSS_S_BAD_MECH); + + /* allocate the output credential structure */ + creds = (gss_union_cred_t)malloc(sizeof (gss_union_cred_desc)); + if (creds == NULL) + return (GSS_S_FAILURE); + + /* initialize to 0s */ + (void) memset(creds, 0, sizeof (gss_union_cred_desc)); + creds->loopback = creds; + + /* for each requested mech attempt to obtain a credential */ + for (i = 0; i < mechs->count; i++) { + major = gss_add_cred_with_password(minor_status, (gss_cred_id_t)creds, + desired_name, + &mechs->elements[i], + password, + cred_usage, time_req, time_req, NULL, + NULL, &initTimeOut, &acceptTimeOut); + if (major == GSS_S_COMPLETE) { + /* update the credential's time */ + if (cred_usage == GSS_C_ACCEPT) { + if (outTime > acceptTimeOut) + outTime = acceptTimeOut; + } else if (cred_usage == GSS_C_INITIATE) { + if (outTime > initTimeOut) + outTime = initTimeOut; + } else { + /* + * time_rec is the lesser of the + * init/accept times + */ + if (initTimeOut > acceptTimeOut) + outTime = (outTime > acceptTimeOut) ? + acceptTimeOut : outTime; + else + outTime = (outTime > initTimeOut) ? + initTimeOut : outTime; + } + } + } /* for */ + + /* ensure that we have at least one credential element */ + if (creds->count < 1) { + free(creds); + return (major); + } + + /* + * fill in output parameters + * setup the actual mechs output parameter + */ + if (actual_mechs != NULL) { + gss_OID_set_desc oids; + + oids.count = creds->count; + oids.elements = creds->mechs_array; + + major = generic_gss_copy_oid_set(minor_status, &oids, actual_mechs); + if (GSS_ERROR(major)) { + (void) gss_release_cred(minor_status, + (gss_cred_id_t *)&creds); + return (major); + } + } + + if (time_rec) + *time_rec = outTime; + + + creds->loopback = creds; + *output_cred_handle = (gss_cred_id_t)creds; + return (GSS_S_COMPLETE); +} + +static OM_uint32 +val_add_cred_pw_args( + OM_uint32 *minor_status, + gss_cred_id_t input_cred_handle, + const gss_name_t desired_name, + const gss_OID desired_mech, + const gss_buffer_t password, + gss_cred_usage_t cred_usage, + OM_uint32 initiator_time_req, + OM_uint32 acceptor_time_req, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *initiator_time_rec, + OM_uint32 *acceptor_time_rec) +{ + + /* Initialize outputs. */ + + if (minor_status != NULL) + *minor_status = 0; + + if (output_cred_handle != NULL) + *output_cred_handle = GSS_C_NO_CREDENTIAL; + + if (actual_mechs != NULL) + *actual_mechs = GSS_C_NO_OID_SET; + + if (acceptor_time_rec != NULL) + *acceptor_time_rec = 0; + + if (initiator_time_rec != NULL) + *initiator_time_rec = 0; + + /* Validate arguments. */ + + if (minor_status == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (input_cred_handle == GSS_C_NO_CREDENTIAL && + output_cred_handle == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE | GSS_S_NO_CRED); + + if (cred_usage != GSS_C_ACCEPT + && cred_usage != GSS_C_INITIATE + && cred_usage != GSS_C_BOTH) { + if (minor_status) { + *minor_status = EINVAL; + map_errcode(minor_status); + } + return GSS_S_FAILURE; + } + + if (password == GSS_C_NO_BUFFER || + password->length == 0 || + password->value == NULL) { + if (minor_status) { + *minor_status = EINVAL; + map_errcode(minor_status); + } + return GSS_S_FAILURE; + } + + + return (GSS_S_COMPLETE); +} + + +/* V2 KRB5_CALLCONV */ +OM_uint32 KRB5_CALLCONV +gss_add_cred_with_password(minor_status, input_cred_handle, + desired_name, desired_mech, password, cred_usage, + initiator_time_req, acceptor_time_req, + output_cred_handle, actual_mechs, + initiator_time_rec, acceptor_time_rec) + OM_uint32 *minor_status; + const gss_cred_id_t input_cred_handle; + const gss_name_t desired_name; + const gss_OID desired_mech; + const gss_buffer_t password; + gss_cred_usage_t cred_usage; + OM_uint32 initiator_time_req; + OM_uint32 acceptor_time_req; + gss_cred_id_t *output_cred_handle; + gss_OID_set *actual_mechs; + OM_uint32 *initiator_time_rec; + OM_uint32 *acceptor_time_rec; +{ + OM_uint32 status, temp_minor_status; + OM_uint32 time_req, time_rec; + gss_union_name_t union_name; + gss_union_cred_t new_union_cred, union_cred; + gss_name_t internal_name = GSS_C_NO_NAME; + gss_name_t allocated_name = GSS_C_NO_NAME; + gss_mechanism mech; + gss_mechanism_ext mech_ext; + gss_cred_id_t cred = NULL; + gss_OID new_mechs_array = NULL; + gss_cred_id_t * new_cred_array = NULL; + + status = val_add_cred_pw_args(minor_status, + input_cred_handle, + desired_name, + desired_mech, + password, + cred_usage, + initiator_time_req, + acceptor_time_req, + output_cred_handle, + actual_mechs, + initiator_time_rec, + acceptor_time_rec); + if (status != GSS_S_COMPLETE) + return (status); + + mech = gssint_get_mechanism(desired_mech); + if (!mech) + return GSS_S_BAD_MECH; + + mech_ext = gssint_get_mechanism_ext(desired_mech); + if (!mech_ext || !mech_ext->gssspi_acquire_cred_with_password) + return GSS_S_UNAVAILABLE; + + if (input_cred_handle == GSS_C_NO_CREDENTIAL) { + union_cred = malloc(sizeof (gss_union_cred_desc)); + if (union_cred == NULL) + return (GSS_S_FAILURE); + + (void) memset(union_cred, 0, sizeof (gss_union_cred_desc)); + + /* for default credentials we will use GSS_C_NO_NAME */ + internal_name = GSS_C_NO_NAME; + } else { + union_cred = (gss_union_cred_t)input_cred_handle; + if (gssint_get_mechanism_cred(union_cred, desired_mech) != + GSS_C_NO_CREDENTIAL) + return (GSS_S_DUPLICATE_ELEMENT); + + /* may need to create a mechanism specific name */ + if (desired_name) { + union_name = (gss_union_name_t)desired_name; + if (union_name->mech_type && + g_OID_equal(union_name->mech_type, + &mech->mech_type)) + internal_name = union_name->mech_name; + else { + if (gssint_import_internal_name(minor_status, + &mech->mech_type, union_name, + &allocated_name) != GSS_S_COMPLETE) + return (GSS_S_BAD_NAME); + internal_name = allocated_name; + } + } + } + + + if (cred_usage == GSS_C_ACCEPT) + time_req = acceptor_time_req; + else if (cred_usage == GSS_C_INITIATE) + time_req = initiator_time_req; + else if (cred_usage == GSS_C_BOTH) + time_req = (acceptor_time_req > initiator_time_req) ? + acceptor_time_req : initiator_time_req; + else + time_req = 0; + + status = mech_ext->gssspi_acquire_cred_with_password(minor_status, + internal_name, + password, + time_req, + GSS_C_NULL_OID_SET, + cred_usage, + &cred, + NULL, + &time_rec); + if (status != GSS_S_COMPLETE) { + map_error(minor_status, mech); + goto errout; + } + + /* may need to set credential auxinfo strucutre */ + if (union_cred->auxinfo.creation_time == 0) { + union_cred->auxinfo.creation_time = time(NULL); + union_cred->auxinfo.time_rec = time_rec; + union_cred->auxinfo.cred_usage = cred_usage; + + /* + * we must set the name; if name is not supplied + * we must do inquire cred to get it + */ + if (internal_name == NULL) { + if (mech->gss_inquire_cred == NULL || + ((status = mech->gss_inquire_cred( + &temp_minor_status, cred, + &allocated_name, NULL, NULL, + NULL)) != GSS_S_COMPLETE)) + goto errout; + internal_name = allocated_name; + } + + if (internal_name != GSS_C_NO_NAME) { + status = mech->gss_display_name(&temp_minor_status, internal_name, + &union_cred->auxinfo.name, + &union_cred->auxinfo.name_type); + + if (status != GSS_S_COMPLETE) + goto errout; + } + } + + /* now add the new credential elements */ + new_mechs_array = (gss_OID) + malloc(sizeof (gss_OID_desc) * (union_cred->count+1)); + + new_cred_array = (gss_cred_id_t *) + malloc(sizeof (gss_cred_id_t) * (union_cred->count+1)); + + if (!new_mechs_array || !new_cred_array) { + status = GSS_S_FAILURE; + goto errout; + } + + if (acceptor_time_rec) + if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH) + *acceptor_time_rec = time_rec; + if (initiator_time_rec) + if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) + *initiator_time_rec = time_rec; + + /* + * OK, expand the mechanism array and the credential array + */ + (void) memcpy(new_mechs_array, union_cred->mechs_array, + sizeof (gss_OID_desc) * union_cred->count); + (void) memcpy(new_cred_array, union_cred->cred_array, + sizeof (gss_cred_id_t) * union_cred->count); + + new_cred_array[union_cred->count] = cred; + if ((new_mechs_array[union_cred->count].elements = + malloc(mech->mech_type.length)) == NULL) + goto errout; + + g_OID_copy(&new_mechs_array[union_cred->count], + &mech->mech_type); + + if (actual_mechs != NULL) { + gss_OID_set_desc oids; + + oids.count = union_cred->count + 1; + oids.elements = new_mechs_array; + + status = generic_gss_copy_oid_set(minor_status, &oids, actual_mechs); + if (GSS_ERROR(status)) { + free(new_mechs_array[union_cred->count].elements); + goto errout; + } + } + + if (output_cred_handle == NULL) { + free(union_cred->mechs_array); + free(union_cred->cred_array); + new_union_cred = union_cred; + } else { + new_union_cred = malloc(sizeof (gss_union_cred_desc)); + if (new_union_cred == NULL) { + free(new_mechs_array[union_cred->count].elements); + goto errout; + } + *new_union_cred = *union_cred; + *output_cred_handle = (gss_cred_id_t)new_union_cred; + } + + new_union_cred->mechs_array = new_mechs_array; + new_union_cred->cred_array = new_cred_array; + new_union_cred->count++; + new_union_cred->loopback = new_union_cred; + + /* We're done with the internal name. Free it if we allocated it. */ + + if (allocated_name) + (void) gssint_release_internal_name(&temp_minor_status, + &mech->mech_type, + &allocated_name); + + return (GSS_S_COMPLETE); + +errout: + if (new_mechs_array) + free(new_mechs_array); + if (new_cred_array) + free(new_cred_array); + + if (cred != NULL && mech->gss_release_cred) + mech->gss_release_cred(&temp_minor_status, &cred); + + if (allocated_name) + (void) gssint_release_internal_name(&temp_minor_status, + &mech->mech_type, + &allocated_name); + + if (input_cred_handle == GSS_C_NO_CREDENTIAL && union_cred) { + if (union_cred->auxinfo.name.value) + free(union_cred->auxinfo.name.value); + free(union_cred); + } + + return (status); +} diff --git a/src/lib/gssapi/mechglue/g_initialize.c b/src/lib/gssapi/mechglue/g_initialize.c index 70c220391..a393a5309 100644 --- a/src/lib/gssapi/mechglue/g_initialize.c +++ b/src/lib/gssapi/mechglue/g_initialize.c @@ -609,6 +609,10 @@ releaseMechInfo(gss_mech_info *pCf) memset(cf->mech, 0, sizeof(*cf->mech)); free(cf->mech); } + if (cf->mech_ext != NULL) { + memset(cf->mech_ext, 0, sizeof(*cf->mech_ext)); + free(cf->mech_ext); + } if (cf->dl_handle != NULL) krb5int_close_plugin(cf->dl_handle); @@ -646,6 +650,16 @@ gssint_register_mechinfo(gss_mech_info template) new_cf->freeMech = 1; new_cf->next = NULL; + if (template->mech_ext != NULL) { + new_cf->mech_ext = (gss_mechanism_ext)calloc(1, + sizeof(struct gss_config_ext)); + if (new_cf->mech_ext == NULL) { + releaseMechInfo(&new_cf); + return ENOMEM; + } + *new_cf->mech_ext = *template->mech_ext; + } + if (template->kmodName != NULL) { new_cf->kmodName = strdup(template->kmodName); if (new_cf->kmodName == NULL) { @@ -785,6 +799,21 @@ build_dynamicMech(void *dl, const gss_OID mech_type) return mech; } +static gss_mechanism_ext +build_dynamicMechExt(void *dl, const gss_OID mech_type) +{ + gss_mechanism_ext mech_ext; + + mech_ext = (gss_mechanism_ext)calloc(1, sizeof(*mech_ext)); + if (mech_ext == NULL) { + return NULL; + } + + GSS_ADD_DYNAMIC_METHOD(dl, mech_ext, gssspi_acquire_cred_with_password); + + return mech_ext; +} + static void freeMechList(void) { @@ -906,59 +935,50 @@ gssint_get_mechanism_ext(oid) const gss_OID oid; { gss_mech_info aMech; - gss_mechanism_ext mech_ext; if (gssint_mechglue_initialize_library() != 0) return (NULL); + if (k5_mutex_lock(&g_mechListLock) != 0) + return NULL; /* check if the mechanism is already loaded */ - if ((aMech = searchMechList(oid)) != NULL && aMech->mech_ext != NULL) + if ((aMech = searchMechList(oid)) != NULL && aMech->mech_ext) { + (void) k5_mutex_unlock(&g_mechListLock); return (aMech->mech_ext); + } - if (gssint_get_mechanism(oid) == NULL) - return (NULL); - - if (aMech->dl_handle == NULL) - return (NULL); - - /* Load the gss_config_ext struct for this mech */ - - mech_ext = (gss_mechanism_ext)malloc(sizeof (struct gss_config_ext)); - - if (mech_ext == NULL) - return (NULL); - -#if 0 /* - * dlsym() the mech's 'method' functions for the extended APIs - * - * NOTE: Until the void *context argument is removed from the - * SPI method functions' signatures it will be necessary to have - * different function pointer typedefs and function names for - * the SPI methods than for the API. When this argument is - * removed it will be possible to rename gss_*_sfct to gss_*_fct - * and and gssspi_* to gss_*. + * might need to re-read the configuration file before loading + * the mechanism to ensure we have the latest info. */ - mech_ext->gss_acquire_cred_with_password = - (gss_acquire_cred_with_password_sfct)dlsym(aMech->dl_handle, - "gssspi_acquire_cred_with_password"); -#endif + updateMechList(); + + aMech = searchMechList(oid); + + /* is the mechanism present in the list ? */ + if (aMech == NULL || aMech->dl_handle == NULL) { + (void) k5_mutex_unlock(&g_mechListLock); + return ((gss_mechanism_ext)NULL); + } - /* Set aMech->mech_ext */ - (void) k5_mutex_lock(&g_mechListLock); + /* has another thread loaded the mech */ + if (aMech->mech_ext) { + (void) k5_mutex_unlock(&g_mechListLock); + return (aMech->mech_ext); + } - if (aMech->mech_ext == NULL) - aMech->mech_ext = mech_ext; - else - free(mech_ext); /* we raced and lost; don't leak */ + /* Try dynamic dispatch table */ + aMech->mech_ext = build_dynamicMechExt(aMech->dl_handle, + aMech->mech_type); + if (aMech->mech_ext == NULL) { + (void) k5_mutex_unlock(&g_mechListLock); + return ((gss_mechanism_ext)NULL); + } (void) k5_mutex_unlock(&g_mechListLock); - return (aMech->mech_ext); - } /* gssint_get_mechanism_ext */ - /* * this routine is used for searching the list of mechanism data. * diff --git a/src/lib/gssapi/mechglue/mglueP.h b/src/lib/gssapi/mechglue/mglueP.h index 3769caf87..f21929015 100644 --- a/src/lib/gssapi/mechglue/mglueP.h +++ b/src/lib/gssapi/mechglue/mglueP.h @@ -77,7 +77,6 @@ typedef struct gss_cred_id_struct { } gss_union_cred_desc, *gss_union_cred_t; typedef OM_uint32 (*gss_acquire_cred_with_password_sfct)( - void *, /* context */ OM_uint32 *, /* minor_status */ const gss_name_t, /* desired_name */ const gss_buffer_t, /* password */ @@ -593,7 +592,7 @@ typedef struct gss_config { /* This structure MUST NOT be used by any code outside libgss */ typedef struct gss_config_ext { - gss_acquire_cred_with_password_sfct gss_acquire_cred_with_password; + gss_acquire_cred_with_password_sfct gssspi_acquire_cred_with_password; } *gss_mechanism_ext; /* diff --git a/src/lib/gssapi/spnego/gssapiP_spnego.h b/src/lib/gssapi/spnego/gssapiP_spnego.h index a1cd2b4a6..d72c85da7 100644 --- a/src/lib/gssapi/spnego/gssapiP_spnego.h +++ b/src/lib/gssapi/spnego/gssapiP_spnego.h @@ -457,6 +457,18 @@ spnego_gss_acquire_cred_impersonate_name( gss_OID_set *, /* actual_mechs */ OM_uint32 *); /* time_rec */ +OM_uint32 +spnego_gss_acquire_cred_with_password( + OM_uint32 *minor_status, + const gss_name_t desired_name, + const gss_buffer_t password, + OM_uint32 time_req, + const gss_OID_set desired_mechs, + gss_cred_usage_t cred_usage, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec); + OM_uint32 spnego_gss_display_name_ext ( diff --git a/src/lib/gssapi/spnego/spnego_mech.c b/src/lib/gssapi/spnego/spnego_mech.c index 86ba89a39..c9cf441e0 100644 --- a/src/lib/gssapi/spnego/spnego_mech.c +++ b/src/lib/gssapi/spnego/spnego_mech.c @@ -274,6 +274,11 @@ static struct gss_config spnego_mechanism = spnego_gss_set_neg_mechs, }; +static struct gss_config_ext spnego_mechanism_ext = +{ + spnego_gss_acquire_cred_with_password +}; + #ifdef _GSS_STATIC_LINK #include "mglueP.h" @@ -283,6 +288,7 @@ static int gss_spnegomechglue_init(void) memset(&mech_spnego, 0, sizeof(mech_spnego)); mech_spnego.mech = &spnego_mechanism; + mech_spnego.mech_ext = &spnego_mechanism_ext; mech_spnego.mechNameStr = "spnego"; mech_spnego.mech_type = GSS_C_NO_OID; @@ -2446,6 +2452,64 @@ spnego_gss_acquire_cred_impersonate_name(OM_uint32 *minor_status, return (status); } +OM_uint32 +spnego_gss_acquire_cred_with_password(OM_uint32 *minor_status, + const gss_name_t desired_name, + const gss_buffer_t password, + OM_uint32 time_req, + const gss_OID_set desired_mechs, + gss_cred_usage_t cred_usage, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec) +{ + OM_uint32 status, tmpmin; + gss_OID_set amechs = GSS_C_NULL_OID_SET, dmechs; + gss_cred_id_t mcred = NULL; + spnego_gss_cred_id_t spcred = NULL; + + dsyslog("Entering spnego_gss_acquire_cred_with_password\n"); + + if (actual_mechs) + *actual_mechs = NULL; + + if (time_rec) + *time_rec = 0; + + dmechs = desired_mechs; + if (desired_mechs == GSS_C_NULL_OID_SET) { + status = get_available_mechs(minor_status, desired_name, + cred_usage, NULL, &amechs); + dmechs = amechs; + } + + status = gss_acquire_cred_with_password(minor_status, desired_name, + password, time_req, dmechs, + cred_usage, &mcred, + actual_mechs, time_rec); + if (status != GSS_S_COMPLETE) + goto cleanup; + + spcred = malloc(sizeof(spnego_gss_cred_id_rec)); + if (spcred == NULL) { + *minor_status = ENOMEM; + status = GSS_S_FAILURE; + goto cleanup; + } + spcred->neg_mechs = GSS_C_NULL_OID_SET; + spcred->mcred = mcred; + mcred = GSS_C_NO_CREDENTIAL; + *output_cred_handle = (gss_cred_id_t)spcred; + +cleanup: + + (void) gss_release_oid_set(&tmpmin, &amechs); + (void) gss_release_cred(&tmpmin, &mcred); + + dsyslog("Leaving spnego_gss_acquire_cred_with_password\n"); + return (status); +} + OM_uint32 spnego_gss_display_name_ext(OM_uint32 *minor_status, gss_name_t name, diff --git a/src/lib/krb5/asn.1/asn1_k_decode.c b/src/lib/krb5/asn.1/asn1_k_decode.c index 60d9455a1..c2dd5f6d4 100644 --- a/src/lib/krb5/asn.1/asn1_k_decode.c +++ b/src/lib/krb5/asn.1/asn1_k_decode.c @@ -1899,6 +1899,47 @@ error_out: return retval; } +asn1_error_code asn1_decode_iakerb_header +(asn1buf *buf, krb5_iakerb_header *val) +{ + setup(); + val->target_realm.data = NULL; + val->target_realm.length = 0; + val->cookie = NULL; + { + begin_structure(); + get_lenfield(val->target_realm.length, val->target_realm.data, + 1, asn1_decode_charstring); + if (tagnum == 2) { + alloc_data(val->cookie); + get_lenfield(val->cookie->length, val->cookie->data, + 2, asn1_decode_charstring); + } + end_structure(); + } + return 0; +error_out: + krb5_free_data_contents(NULL, &val->target_realm); + krb5_free_data(NULL, val->cookie); + return retval; +} + +asn1_error_code asn1_decode_iakerb_finished +(asn1buf *buf, krb5_iakerb_finished *val) +{ + setup(); + val->checksum.contents = NULL; + { + begin_structure(); + get_field(val->checksum, 1, asn1_decode_checksum); + end_structure(); + } + return 0; +error_out: + krb5_free_checksum_contents(NULL, &val->checksum); + return retval; +} + #ifndef DISABLE_PKINIT /* PKINIT */ diff --git a/src/lib/krb5/asn.1/asn1_k_decode.h b/src/lib/krb5/asn.1/asn1_k_decode.h index 1b0aa750b..79a4a05e4 100644 --- a/src/lib/krb5/asn.1/asn1_k_decode.h +++ b/src/lib/krb5/asn.1/asn1_k_decode.h @@ -271,4 +271,10 @@ asn1_error_code asn1_decode_ad_kdcissued_ptr(asn1buf *buf, asn1_error_code asn1_decode_ad_signedpath(asn1buf *buf, krb5_ad_signedpath *val); +asn1_error_code asn1_decode_iakerb_header(asn1buf *buf, + krb5_iakerb_header *val); + +asn1_error_code asn1_decode_iakerb_finished(asn1buf *buf, + krb5_iakerb_finished *val); + #endif diff --git a/src/lib/krb5/asn.1/asn1_k_encode.c b/src/lib/krb5/asn.1/asn1_k_encode.c index a35f561e6..d334ae632 100644 --- a/src/lib/krb5/asn.1/asn1_k_encode.c +++ b/src/lib/krb5/asn.1/asn1_k_encode.c @@ -1394,6 +1394,35 @@ static unsigned int ad_signedpath_optional(const void *p) DEFSEQTYPE(ad_signedpath, krb5_ad_signedpath, ad_signedpath_fields, ad_signedpath_optional); +static const struct field_info iakerb_header_fields[] = { + FIELDOF_NORM(krb5_iakerb_header, ostring_data, target_realm, 1), + FIELDOF_OPT(krb5_iakerb_header, ostring_data_ptr, cookie, 2, 2), +}; + +static unsigned int iakerb_header_optional(const void *p) +{ + unsigned int optional = 0; + const krb5_iakerb_header *val = p; + if (val->cookie && val->cookie->data) + optional |= (1u << 2); + return optional; +} + +DEFSEQTYPE(iakerb_header, krb5_iakerb_header, iakerb_header_fields, iakerb_header_optional); + +static const struct field_info iakerb_finished_fields[] = { + FIELDOF_NORM(krb5_iakerb_finished, checksum, checksum, 1), +}; + +static unsigned int iakerb_finished_optional(const void *p) +{ + unsigned int optional = 0; + return optional; +} + +DEFSEQTYPE(iakerb_finished, krb5_iakerb_finished, iakerb_finished_fields, +iakerb_finished_optional); + /* Exported complete encoders -- these produce a krb5_data with the encoding in the correct byte order. */ @@ -1472,6 +1501,8 @@ MAKE_FULL_ENCODER(encode_krb5_fast_response, fast_response); MAKE_FULL_ENCODER(encode_krb5_ad_kdcissued, ad_kdc_issued); MAKE_FULL_ENCODER(encode_krb5_ad_signedpath_data, ad_signedpath_data); MAKE_FULL_ENCODER(encode_krb5_ad_signedpath, ad_signedpath); +MAKE_FULL_ENCODER(encode_krb5_iakerb_header, iakerb_header); +MAKE_FULL_ENCODER(encode_krb5_iakerb_finished, iakerb_finished); diff --git a/src/lib/krb5/asn.1/krb5_decode.c b/src/lib/krb5/asn.1/krb5_decode.c index 542a626da..7aeb6bfe5 100644 --- a/src/lib/krb5/asn.1/krb5_decode.c +++ b/src/lib/krb5/asn.1/krb5_decode.c @@ -1218,6 +1218,30 @@ decode_krb5_ad_signedpath(const krb5_data *code, krb5_ad_signedpath **repptr) cleanup(free); } +krb5_error_code decode_krb5_iakerb_header +(const krb5_data *code, krb5_iakerb_header **repptr) +{ + setup_buf_only(krb5_iakerb_header *); + alloc_field(rep); + + retval = asn1_decode_iakerb_header(&buf, rep); + if (retval) clean_return(retval); + + cleanup(free); +} + +krb5_error_code decode_krb5_iakerb_finished +(const krb5_data *code, krb5_iakerb_finished **repptr) +{ + setup_buf_only(krb5_iakerb_finished *); + alloc_field(rep); + + retval = asn1_decode_iakerb_finished(&buf, rep); + if (retval) clean_return(retval); + + cleanup(free); +} + krb5_error_code krb5int_get_authdata_containee_types(krb5_context context, const krb5_authdata *authdata, diff --git a/src/lib/krb5/error_tables/krb5_err.et b/src/lib/krb5/error_tables/krb5_err.et index bf3404de1..56434ad47 100644 --- a/src/lib/krb5/error_tables/krb5_err.et +++ b/src/lib/krb5/error_tables/krb5_err.et @@ -126,8 +126,8 @@ error_code KRB5KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED, "Public key encrypti error_code KRB5PLACEHOLD_82, "KRB5 error code 82" error_code KRB5PLACEHOLD_83, "KRB5 error code 83" error_code KRB5PLACEHOLD_84, "KRB5 error code 84" -error_code KRB5PLACEHOLD_85, "KRB5 error code 85" -error_code KRB5PLACEHOLD_86, "KRB5 error code 86" +error_code KRB5KRB_AP_ERR_IAKERB_KDC_NOT_FOUND, "The IAKERB proxy could not find a KDC" +error_code KRB5KRB_AP_ERR_IAKERB_KDC_NO_RESPONSE, "The KDC did not respond to the IAKERB proxy" error_code KRB5PLACEHOLD_87, "KRB5 error code 87" error_code KRB5PLACEHOLD_88, "KRB5 error code 88" error_code KRB5PLACEHOLD_89, "KRB5 error code 89" diff --git a/src/lib/krb5/krb/kfree.c b/src/lib/krb5/krb/kfree.c index 2adaa4101..6a3e6b291 100644 --- a/src/lib/krb5/krb/kfree.c +++ b/src/lib/krb5/krb/kfree.c @@ -913,3 +913,22 @@ krb5_free_ad_signedpath(krb5_context context, krb5_ad_signedpath *val) krb5_free_pa_data(context, val->method_data); free(val); } + +void KRB5_CALLCONV +krb5_free_iakerb_header(krb5_context context, krb5_iakerb_header *val) +{ + if (val == NULL) + return ; + + krb5_free_data_contents(context, &val->target_realm); + krb5_free_data(context, val->cookie); +} + +void KRB5_CALLCONV +krb5_free_iakerb_finished(krb5_context context, krb5_iakerb_finished *val) +{ + if (val == NULL) + return ; + + krb5_free_checksum_contents(context, &val->checksum); +} diff --git a/src/lib/krb5/libkrb5.exports b/src/lib/krb5/libkrb5.exports index 58e4ddac7..5c517c89c 100644 --- a/src/lib/krb5/libkrb5.exports +++ b/src/lib/krb5/libkrb5.exports @@ -22,6 +22,8 @@ decode_krb5_error decode_krb5_etype_info decode_krb5_etype_info2 decode_krb5_fast_req +decode_krb5_iakerb_finished +decode_krb5_iakerb_header decode_krb5_kdc_req_body decode_krb5_pa_enc_ts decode_krb5_pa_for_user @@ -67,6 +69,8 @@ encode_krb5_error encode_krb5_etype_info encode_krb5_etype_info2 encode_krb5_fast_response +encode_krb5_iakerb_finished +encode_krb5_iakerb_header encode_krb5_kdc_req_body encode_krb5_pa_enc_ts encode_krb5_pa_for_user @@ -266,6 +270,8 @@ krb5_free_etype_info krb5_free_fast_armored_req krb5_free_fast_req krb5_free_host_realm +krb5_free_iakerb_finished +krb5_free_iakerb_header krb5_free_kdc_rep krb5_free_kdc_req krb5_free_keyblock diff --git a/src/tests/asn.1/krb5_decode_leak.c b/src/tests/asn.1/krb5_decode_leak.c index ab46fb946..ac7f5bfc8 100644 --- a/src/tests/asn.1/krb5_decode_leak.c +++ b/src/tests/asn.1/krb5_decode_leak.c @@ -704,6 +704,28 @@ main(int argc, char **argv) krb5_free_ad_signedpath); ktest_empty_ad_signedpath(&sp); } + /****************************************************************/ + /* encode_krb5_iakerb_header */ + { + krb5_iakerb_header ih, *tmp; + setup(ih, "iakerb_header", + ktest_make_sample_iakerb_header); + leak_test(ih, encode_krb5_iakerb_header, + decode_krb5_iakerb_header, + krb5_free_iakerb_header); + ktest_empty_iakerb_header(&ih); + } + /****************************************************************/ + /* encode_krb5_iakerb_finished */ + { + krb5_iakerb_finished ih, *tmp; + setup(ih, "iakerb_finished", + ktest_make_sample_iakerb_finished); + leak_test(ih, encode_krb5_iakerb_finished, + decode_krb5_iakerb_finished, + krb5_free_iakerb_finished); + ktest_empty_iakerb_finished(&ih); + } krb5_free_context(test_context); return 0; } diff --git a/src/tests/asn.1/krb5_decode_test.c b/src/tests/asn.1/krb5_decode_test.c index 2ef70cfe6..b3480a63d 100644 --- a/src/tests/asn.1/krb5_decode_test.c +++ b/src/tests/asn.1/krb5_decode_test.c @@ -916,6 +916,22 @@ int main(argc, argv) ktest_empty_ad_signedpath(&ref); } + /****************************************************************/ + /* decode_iakerb_header */ + { + setup(krb5_iakerb_header,"krb5_iakerb_header",ktest_make_sample_iakerb_header); + decode_run("iakerb_header","","30 18 A1 0A 04 08 6B 72 62 35 64 61 74 61 A2 0A 04 08 6B 72 62 35 64 61 74 61",decode_krb5_iakerb_header,ktest_equal_iakerb_header,krb5_free_iakerb_header); + ktest_empty_iakerb_header(&ref); + } + + /****************************************************************/ + /* decode_iakerb_finished */ + { + setup(krb5_iakerb_finished,"krb5_iakerb_finished",ktest_make_sample_iakerb_finished); + decode_run("iakerb_finished","","30 11 A1 0F 30 0D A0 03 02 01 01 A1 06 04 04 31 32 33 34",decode_krb5_iakerb_finished,ktest_equal_iakerb_finished,krb5_free_iakerb_finished); + ktest_empty_iakerb_finished(&ref); + } + #ifdef ENABLE_LDAP /* ldap sequence_of_keys */ { diff --git a/src/tests/asn.1/krb5_encode_test.c b/src/tests/asn.1/krb5_encode_test.c index 9694746fa..784d20329 100644 --- a/src/tests/asn.1/krb5_encode_test.c +++ b/src/tests/asn.1/krb5_encode_test.c @@ -740,6 +740,28 @@ main(argc, argv) encode_krb5_ad_signedpath); ktest_empty_ad_signedpath(&sp); } + /****************************************************************/ + /* encode_krb5_iakerb_header */ + { + krb5_iakerb_header ih; + setup(ih,krb5_ad_signedpath,"iakerb_header", + ktest_make_sample_iakerb_header); + encode_run(ih,krb5_iakerb_header, + "iakerb_header","", + encode_krb5_iakerb_header); + ktest_empty_iakerb_header(&ih); + } + /****************************************************************/ + /* encode_krb5_iakerb_finished */ + { + krb5_iakerb_finished ih; + setup(ih,krb5_ad_signedpath,"iakerb_finished", + ktest_make_sample_iakerb_finished); + encode_run(ih,krb5_iakerb_finished, + "iakerb_finished","", + encode_krb5_iakerb_finished); + ktest_empty_iakerb_finished(&ih); + } #ifdef ENABLE_LDAP { ldap_seqof_key_data skd; diff --git a/src/tests/asn.1/ktest.c b/src/tests/asn.1/ktest.c index eefbec9d5..0746d81b8 100644 --- a/src/tests/asn.1/ktest.c +++ b/src/tests/asn.1/ktest.c @@ -890,6 +890,28 @@ krb5_error_code ktest_make_sample_ad_signedpath(p) return retval; } +krb5_error_code ktest_make_sample_iakerb_header(ih) + krb5_iakerb_header *ih; +{ + krb5_error_code retval; + retval = ktest_make_sample_data(&(ih->target_realm)); + if (retval) return retval; + ih->cookie = k5alloc(sizeof(krb5_data), &retval); + if (retval) return retval; + retval = ktest_make_sample_data(ih->cookie); + if (retval) return retval; + return retval; +} + +krb5_error_code ktest_make_sample_iakerb_finished(ih) + krb5_iakerb_finished *ih; +{ + krb5_error_code retval; + retval = ktest_make_sample_checksum(&ih->checksum); + if (retval) return retval; + return retval; +} + #ifdef ENABLE_LDAP static krb5_error_code ktest_make_sample_key_data(krb5_key_data *p, int i) { @@ -1532,6 +1554,19 @@ void ktest_empty_ad_signedpath(p) ktest_destroy_pa_data_array(&p->method_data); } +void ktest_empty_iakerb_header(p) + krb5_iakerb_header *p; +{ + krb5_free_data_contents(NULL, &p->target_realm); + krb5_free_data(NULL, p->cookie); +} + +void ktest_empty_iakerb_finished(p) + krb5_iakerb_finished *p; +{ + krb5_free_checksum_contents(NULL, &p->checksum); +} + #ifdef ENABLE_LDAP void ktest_empty_ldap_seqof_key_data(ctx, p) krb5_context ctx; diff --git a/src/tests/asn.1/ktest.h b/src/tests/asn.1/ktest.h index 5f9b5ca38..c4059a7bb 100644 --- a/src/tests/asn.1/ktest.h +++ b/src/tests/asn.1/ktest.h @@ -109,6 +109,8 @@ krb5_error_code ktest_make_sample_pa_s4u_x509_user(krb5_pa_s4u_x509_user *p); krb5_error_code ktest_make_sample_ad_kdcissued(krb5_ad_kdcissued *p); krb5_error_code ktest_make_sample_ad_signedpath_data(krb5_ad_signedpath_data *p); krb5_error_code ktest_make_sample_ad_signedpath(krb5_ad_signedpath *p); +krb5_error_code ktest_make_sample_iakerb_header(krb5_iakerb_header *p); +krb5_error_code ktest_make_sample_iakerb_finished(krb5_iakerb_finished *p); #ifdef ENABLE_LDAP krb5_error_code ktest_make_sample_ldap_seqof_key_data(ldap_seqof_key_data * p); @@ -221,6 +223,8 @@ void ktest_empty_pa_s4u_x509_user(krb5_pa_s4u_x509_user *p); void ktest_empty_ad_kdcissued(krb5_ad_kdcissued *p); void ktest_empty_ad_signedpath_data(krb5_ad_signedpath_data *p); void ktest_empty_ad_signedpath(krb5_ad_signedpath *p); +void ktest_empty_iakerb_header(krb5_iakerb_header *p); +void ktest_empty_iakerb_finished(krb5_iakerb_finished *p); #ifdef ENABLE_LDAP void ktest_empty_ldap_seqof_key_data(krb5_context, ldap_seqof_key_data *p); diff --git a/src/tests/asn.1/ktest_equal.c b/src/tests/asn.1/ktest_equal.c index f84357b94..0a9280446 100644 --- a/src/tests/asn.1/ktest_equal.c +++ b/src/tests/asn.1/ktest_equal.c @@ -600,6 +600,29 @@ int ktest_equal_ad_signedpath(ref, var) return p; } +int ktest_equal_iakerb_header(ref, var) + krb5_iakerb_header *ref; + krb5_iakerb_header *var; +{ + int p = TRUE; + if (ref == var) return TRUE; + else if (ref == NULL || var == NULL) return FALSE; + p=p&&struct_equal(target_realm,ktest_equal_data); + p=p&&ptr_equal(cookie,ktest_equal_data); + return p; +} + +int ktest_equal_iakerb_finished(ref, var) + krb5_iakerb_finished *ref; + krb5_iakerb_finished *var; +{ + int p = TRUE; + if (ref == var) return TRUE; + else if (ref == NULL || var == NULL) return FALSE; + p=p&&struct_equal(checksum,ktest_equal_checksum); + return p; +} + #ifdef ENABLE_LDAP static int equal_key_data(ref, var) krb5_key_data *ref; diff --git a/src/tests/asn.1/ktest_equal.h b/src/tests/asn.1/ktest_equal.h index 80c38b639..4b55e2333 100644 --- a/src/tests/asn.1/ktest_equal.h +++ b/src/tests/asn.1/ktest_equal.h @@ -106,6 +106,12 @@ int ktest_equal_ad_signedpath_data int ktest_equal_ad_signedpath (krb5_ad_signedpath *ref, krb5_ad_signedpath *var); +int ktest_equal_iakerb_header + (krb5_iakerb_header *ref, + krb5_iakerb_header *var); +int ktest_equal_iakerb_finished + (krb5_iakerb_finished *ref, + krb5_iakerb_finished *var); int ktest_equal_ldap_sequence_of_keys(ldap_seqof_key_data *ref, ldap_seqof_key_data *var); diff --git a/src/tests/asn.1/reference_encode.out b/src/tests/asn.1/reference_encode.out index c6bcf619c..92d21b1a9 100644 --- a/src/tests/asn.1/reference_encode.out +++ b/src/tests/asn.1/reference_encode.out @@ -60,3 +60,5 @@ encode_krb5_pa_s4u_x509_user: 30 68 A0 55 30 53 A0 06 02 04 00 CA 14 9A A1 1A 30 encode_krb5_ad_kdcissued: 30 65 A0 0F 30 0D A0 03 02 01 01 A1 06 04 04 31 32 33 34 A1 10 1B 0E 41 54 48 45 4E 41 2E 4D 49 54 2E 45 44 55 A2 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A3 24 30 22 30 0F A0 03 02 01 01 A1 08 04 06 66 6F 6F 62 61 72 30 0F A0 03 02 01 01 A1 08 04 06 66 6F 6F 62 61 72 encode_krb5_ad_signedpath_data: 30 81 C7 A0 30 30 2E A0 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A1 10 1B 0E 41 54 48 45 4E 41 2E 4D 49 54 2E 45 44 55 A1 11 18 0F 31 39 39 34 30 36 31 30 30 36 30 33 31 37 5A A2 32 30 30 30 2E A0 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A1 10 1B 0E 41 54 48 45 4E 41 2E 4D 49 54 2E 45 44 55 A3 26 30 24 30 10 A1 03 02 01 0D A2 09 04 07 70 61 2D 64 61 74 61 30 10 A1 03 02 01 0D A2 09 04 07 70 61 2D 64 61 74 61 A4 24 30 22 30 0F A0 03 02 01 01 A1 08 04 06 66 6F 6F 62 61 72 30 0F A0 03 02 01 01 A1 08 04 06 66 6F 6F 62 61 72 encode_krb5_ad_signedpath: 30 3E A0 03 02 01 01 A1 0F 30 0D A0 03 02 01 01 A1 06 04 04 31 32 33 34 A3 26 30 24 30 10 A1 03 02 01 0D A2 09 04 07 70 61 2D 64 61 74 61 30 10 A1 03 02 01 0D A2 09 04 07 70 61 2D 64 61 74 61 +encode_krb5_iakerb_header: 30 18 A1 0A 04 08 6B 72 62 35 64 61 74 61 A2 0A 04 08 6B 72 62 35 64 61 74 61 +encode_krb5_iakerb_finished: 30 11 A1 0F 30 0D A0 03 02 01 01 A1 06 04 04 31 32 33 34 diff --git a/src/tests/asn.1/trval_reference.out b/src/tests/asn.1/trval_reference.out index 38e5b99ad..cc1daf3a2 100644 --- a/src/tests/asn.1/trval_reference.out +++ b/src/tests/asn.1/trval_reference.out @@ -1332,3 +1332,16 @@ encode_krb5_ad_signedpath: . . . [1] [Integer] 13 . . . [2] [Octet String] "pa-data" +encode_krb5_iakerb_header: + +[Sequence/Sequence Of] +. [1] [Octet String] "krb5data" +. [2] [Octet String] "krb5data" + +encode_krb5_iakerb_finished: + +[Sequence/Sequence Of] +. [1] [Sequence/Sequence Of] +. . [0] [Integer] 1 +. . [1] [Octet String] "1234" +