1178825Sdfr/* 2233294Sstas * Copyright (c) 1997 - 2008 Kungliga Tekniska H��gskolan 3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden). 4233294Sstas * All rights reserved. 5178825Sdfr * 6233294Sstas * Redistribution and use in source and binary forms, with or without 7233294Sstas * modification, are permitted provided that the following conditions 8233294Sstas * are met: 9178825Sdfr * 10233294Sstas * 1. Redistributions of source code must retain the above copyright 11233294Sstas * notice, this list of conditions and the following disclaimer. 12178825Sdfr * 13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright 14233294Sstas * notice, this list of conditions and the following disclaimer in the 15233294Sstas * documentation and/or other materials provided with the distribution. 16178825Sdfr * 17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors 18233294Sstas * may be used to endorse or promote products derived from this software 19233294Sstas * without specific prior written permission. 20178825Sdfr * 21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24233294Sstas * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31233294Sstas * SUCH DAMAGE. 32178825Sdfr */ 33178825Sdfr 34233294Sstas#include "gsskrb5_locl.h" 35178825Sdfr 36178825Sdfr/* 37178825Sdfr * copy the addresses from `input_chan_bindings' (if any) to 38178825Sdfr * the auth context `ac' 39178825Sdfr */ 40178825Sdfr 41178825Sdfrstatic OM_uint32 42178825Sdfrset_addresses (krb5_context context, 43178825Sdfr krb5_auth_context ac, 44233294Sstas const gss_channel_bindings_t input_chan_bindings) 45178825Sdfr{ 46233294Sstas /* Port numbers are expected to be in application_data.value, 47233294Sstas * initator's port first */ 48178825Sdfr 49178825Sdfr krb5_address initiator_addr, acceptor_addr; 50178825Sdfr krb5_error_code kret; 51233294Sstas 52178825Sdfr if (input_chan_bindings == GSS_C_NO_CHANNEL_BINDINGS 53178825Sdfr || input_chan_bindings->application_data.length != 54178825Sdfr 2 * sizeof(ac->local_port)) 55178825Sdfr return 0; 56178825Sdfr 57178825Sdfr memset(&initiator_addr, 0, sizeof(initiator_addr)); 58178825Sdfr memset(&acceptor_addr, 0, sizeof(acceptor_addr)); 59233294Sstas 60178825Sdfr ac->local_port = 61178825Sdfr *(int16_t *) input_chan_bindings->application_data.value; 62233294Sstas 63178825Sdfr ac->remote_port = 64178825Sdfr *((int16_t *) input_chan_bindings->application_data.value + 1); 65233294Sstas 66178825Sdfr kret = _gsskrb5i_address_to_krb5addr(context, 67178825Sdfr input_chan_bindings->acceptor_addrtype, 68178825Sdfr &input_chan_bindings->acceptor_address, 69178825Sdfr ac->remote_port, 70178825Sdfr &acceptor_addr); 71178825Sdfr if (kret) 72178825Sdfr return kret; 73233294Sstas 74178825Sdfr kret = _gsskrb5i_address_to_krb5addr(context, 75178825Sdfr input_chan_bindings->initiator_addrtype, 76178825Sdfr &input_chan_bindings->initiator_address, 77178825Sdfr ac->local_port, 78178825Sdfr &initiator_addr); 79178825Sdfr if (kret) { 80178825Sdfr krb5_free_address (context, &acceptor_addr); 81178825Sdfr return kret; 82178825Sdfr } 83233294Sstas 84178825Sdfr kret = krb5_auth_con_setaddrs(context, 85178825Sdfr ac, 86178825Sdfr &initiator_addr, /* local address */ 87178825Sdfr &acceptor_addr); /* remote address */ 88233294Sstas 89178825Sdfr krb5_free_address (context, &initiator_addr); 90178825Sdfr krb5_free_address (context, &acceptor_addr); 91233294Sstas 92178825Sdfr#if 0 93178825Sdfr free(input_chan_bindings->application_data.value); 94178825Sdfr input_chan_bindings->application_data.value = NULL; 95178825Sdfr input_chan_bindings->application_data.length = 0; 96178825Sdfr#endif 97178825Sdfr 98178825Sdfr return kret; 99178825Sdfr} 100178825Sdfr 101178825SdfrOM_uint32 102178825Sdfr_gsskrb5_create_ctx( 103178825Sdfr OM_uint32 * minor_status, 104178825Sdfr gss_ctx_id_t * context_handle, 105178825Sdfr krb5_context context, 106178825Sdfr const gss_channel_bindings_t input_chan_bindings, 107178825Sdfr enum gss_ctx_id_t_state state) 108178825Sdfr{ 109178825Sdfr krb5_error_code kret; 110178825Sdfr gsskrb5_ctx ctx; 111178825Sdfr 112178825Sdfr *context_handle = NULL; 113178825Sdfr 114178825Sdfr ctx = malloc(sizeof(*ctx)); 115178825Sdfr if (ctx == NULL) { 116178825Sdfr *minor_status = ENOMEM; 117178825Sdfr return GSS_S_FAILURE; 118178825Sdfr } 119178825Sdfr ctx->auth_context = NULL; 120233294Sstas ctx->deleg_auth_context = NULL; 121178825Sdfr ctx->source = NULL; 122178825Sdfr ctx->target = NULL; 123233294Sstas ctx->kcred = NULL; 124233294Sstas ctx->ccache = NULL; 125178825Sdfr ctx->state = state; 126178825Sdfr ctx->flags = 0; 127178825Sdfr ctx->more_flags = 0; 128178825Sdfr ctx->service_keyblock = NULL; 129178825Sdfr ctx->ticket = NULL; 130178825Sdfr krb5_data_zero(&ctx->fwd_data); 131178825Sdfr ctx->lifetime = GSS_C_INDEFINITE; 132178825Sdfr ctx->order = NULL; 133233294Sstas ctx->crypto = NULL; 134178825Sdfr HEIMDAL_MUTEX_init(&ctx->ctx_id_mutex); 135178825Sdfr 136178825Sdfr kret = krb5_auth_con_init (context, &ctx->auth_context); 137178825Sdfr if (kret) { 138178825Sdfr *minor_status = kret; 139233294Sstas HEIMDAL_MUTEX_destroy(&ctx->ctx_id_mutex); 140233294Sstas return GSS_S_FAILURE; 141233294Sstas } 142178825Sdfr 143233294Sstas kret = krb5_auth_con_init (context, &ctx->deleg_auth_context); 144233294Sstas if (kret) { 145233294Sstas *minor_status = kret; 146233294Sstas krb5_auth_con_free(context, ctx->auth_context); 147178825Sdfr HEIMDAL_MUTEX_destroy(&ctx->ctx_id_mutex); 148178825Sdfr return GSS_S_FAILURE; 149178825Sdfr } 150178825Sdfr 151178825Sdfr kret = set_addresses(context, ctx->auth_context, input_chan_bindings); 152178825Sdfr if (kret) { 153178825Sdfr *minor_status = kret; 154178825Sdfr 155233294Sstas krb5_auth_con_free(context, ctx->auth_context); 156233294Sstas krb5_auth_con_free(context, ctx->deleg_auth_context); 157233294Sstas 158178825Sdfr HEIMDAL_MUTEX_destroy(&ctx->ctx_id_mutex); 159178825Sdfr 160233294Sstas return GSS_S_BAD_BINDINGS; 161233294Sstas } 162233294Sstas 163233294Sstas kret = set_addresses(context, ctx->deleg_auth_context, input_chan_bindings); 164233294Sstas if (kret) { 165233294Sstas *minor_status = kret; 166233294Sstas 167178825Sdfr krb5_auth_con_free(context, ctx->auth_context); 168233294Sstas krb5_auth_con_free(context, ctx->deleg_auth_context); 169178825Sdfr 170233294Sstas HEIMDAL_MUTEX_destroy(&ctx->ctx_id_mutex); 171233294Sstas 172178825Sdfr return GSS_S_BAD_BINDINGS; 173178825Sdfr } 174178825Sdfr 175178825Sdfr /* 176178825Sdfr * We need a sequence number 177178825Sdfr */ 178178825Sdfr 179178825Sdfr krb5_auth_con_addflags(context, 180178825Sdfr ctx->auth_context, 181178825Sdfr KRB5_AUTH_CONTEXT_DO_SEQUENCE | 182178825Sdfr KRB5_AUTH_CONTEXT_CLEAR_FORWARDED_CRED, 183178825Sdfr NULL); 184178825Sdfr 185233294Sstas /* 186233294Sstas * We need a sequence number 187233294Sstas */ 188233294Sstas 189233294Sstas krb5_auth_con_addflags(context, 190233294Sstas ctx->deleg_auth_context, 191233294Sstas KRB5_AUTH_CONTEXT_DO_SEQUENCE | 192233294Sstas KRB5_AUTH_CONTEXT_CLEAR_FORWARDED_CRED, 193233294Sstas NULL); 194233294Sstas 195178825Sdfr *context_handle = (gss_ctx_id_t)ctx; 196178825Sdfr 197178825Sdfr return GSS_S_COMPLETE; 198178825Sdfr} 199178825Sdfr 200178825Sdfr 201178825Sdfrstatic OM_uint32 202178825Sdfrgsskrb5_get_creds( 203178825Sdfr OM_uint32 * minor_status, 204178825Sdfr krb5_context context, 205178825Sdfr krb5_ccache ccache, 206178825Sdfr gsskrb5_ctx ctx, 207233294Sstas const gss_name_t target_name, 208233294Sstas int use_dns, 209178825Sdfr OM_uint32 time_req, 210233294Sstas OM_uint32 * time_rec) 211178825Sdfr{ 212178825Sdfr OM_uint32 ret; 213178825Sdfr krb5_error_code kret; 214178825Sdfr krb5_creds this_cred; 215178825Sdfr OM_uint32 lifetime_rec; 216178825Sdfr 217233294Sstas if (ctx->target) { 218233294Sstas krb5_free_principal(context, ctx->target); 219233294Sstas ctx->target = NULL; 220233294Sstas } 221233294Sstas if (ctx->kcred) { 222233294Sstas krb5_free_creds(context, ctx->kcred); 223233294Sstas ctx->kcred = NULL; 224233294Sstas } 225178825Sdfr 226233294Sstas ret = _gsskrb5_canon_name(minor_status, context, use_dns, 227233294Sstas ctx->source, target_name, &ctx->target); 228233294Sstas if (ret) 229233294Sstas return ret; 230233294Sstas 231178825Sdfr memset(&this_cred, 0, sizeof(this_cred)); 232178825Sdfr this_cred.client = ctx->source; 233178825Sdfr this_cred.server = ctx->target; 234178825Sdfr 235178825Sdfr if (time_req && time_req != GSS_C_INDEFINITE) { 236178825Sdfr krb5_timestamp ts; 237178825Sdfr 238178825Sdfr krb5_timeofday (context, &ts); 239178825Sdfr this_cred.times.endtime = ts + time_req; 240178825Sdfr } else { 241178825Sdfr this_cred.times.endtime = 0; 242178825Sdfr } 243178825Sdfr 244178825Sdfr this_cred.session.keytype = KEYTYPE_NULL; 245178825Sdfr 246178825Sdfr kret = krb5_get_credentials(context, 247178825Sdfr 0, 248178825Sdfr ccache, 249178825Sdfr &this_cred, 250233294Sstas &ctx->kcred); 251178825Sdfr if (kret) { 252178825Sdfr *minor_status = kret; 253178825Sdfr return GSS_S_FAILURE; 254178825Sdfr } 255178825Sdfr 256233294Sstas ctx->lifetime = ctx->kcred->times.endtime; 257178825Sdfr 258178825Sdfr ret = _gsskrb5_lifetime_left(minor_status, context, 259178825Sdfr ctx->lifetime, &lifetime_rec); 260178825Sdfr if (ret) return ret; 261178825Sdfr 262178825Sdfr if (lifetime_rec == 0) { 263178825Sdfr *minor_status = 0; 264178825Sdfr return GSS_S_CONTEXT_EXPIRED; 265178825Sdfr } 266178825Sdfr 267178825Sdfr if (time_rec) *time_rec = lifetime_rec; 268178825Sdfr 269178825Sdfr return GSS_S_COMPLETE; 270178825Sdfr} 271178825Sdfr 272178825Sdfrstatic OM_uint32 273178825Sdfrgsskrb5_initiator_ready( 274178825Sdfr OM_uint32 * minor_status, 275178825Sdfr gsskrb5_ctx ctx, 276178825Sdfr krb5_context context) 277178825Sdfr{ 278233294Sstas OM_uint32 ret; 279233294Sstas int32_t seq_number; 280233294Sstas int is_cfx = 0; 281233294Sstas OM_uint32 flags = ctx->flags; 282178825Sdfr 283233294Sstas krb5_free_creds(context, ctx->kcred); 284233294Sstas ctx->kcred = NULL; 285178825Sdfr 286233294Sstas if (ctx->more_flags & CLOSE_CCACHE) 287233294Sstas krb5_cc_close(context, ctx->ccache); 288233294Sstas ctx->ccache = NULL; 289178825Sdfr 290233294Sstas krb5_auth_con_getremoteseqnumber (context, ctx->auth_context, &seq_number); 291178825Sdfr 292233294Sstas _gsskrb5i_is_cfx(context, ctx, 0); 293233294Sstas is_cfx = (ctx->more_flags & IS_CFX); 294178825Sdfr 295233294Sstas ret = _gssapi_msg_order_create(minor_status, 296233294Sstas &ctx->order, 297233294Sstas _gssapi_msg_order_f(flags), 298233294Sstas seq_number, 0, is_cfx); 299233294Sstas if (ret) return ret; 300233294Sstas 301233294Sstas ctx->state = INITIATOR_READY; 302233294Sstas ctx->more_flags |= OPEN; 303233294Sstas 304233294Sstas return GSS_S_COMPLETE; 305178825Sdfr} 306178825Sdfr 307178825Sdfr/* 308178825Sdfr * handle delegated creds in init-sec-context 309178825Sdfr */ 310178825Sdfr 311178825Sdfrstatic void 312178825Sdfrdo_delegation (krb5_context context, 313178825Sdfr krb5_auth_context ac, 314178825Sdfr krb5_ccache ccache, 315178825Sdfr krb5_creds *cred, 316178825Sdfr krb5_const_principal name, 317178825Sdfr krb5_data *fwd_data, 318233294Sstas uint32_t flagmask, 319178825Sdfr uint32_t *flags) 320178825Sdfr{ 321178825Sdfr krb5_creds creds; 322178825Sdfr KDCOptions fwd_flags; 323178825Sdfr krb5_error_code kret; 324233294Sstas 325178825Sdfr memset (&creds, 0, sizeof(creds)); 326178825Sdfr krb5_data_zero (fwd_data); 327233294Sstas 328178825Sdfr kret = krb5_cc_get_principal(context, ccache, &creds.client); 329233294Sstas if (kret) 330178825Sdfr goto out; 331233294Sstas 332233294Sstas kret = krb5_make_principal(context, 333233294Sstas &creds.server, 334233294Sstas creds.client->realm, 335233294Sstas KRB5_TGS_NAME, 336233294Sstas creds.client->realm, 337233294Sstas NULL); 338178825Sdfr if (kret) 339233294Sstas goto out; 340233294Sstas 341178825Sdfr creds.times.endtime = 0; 342233294Sstas 343178825Sdfr memset(&fwd_flags, 0, sizeof(fwd_flags)); 344178825Sdfr fwd_flags.forwarded = 1; 345178825Sdfr fwd_flags.forwardable = 1; 346233294Sstas 347178825Sdfr if ( /*target_name->name.name_type != KRB5_NT_SRV_HST ||*/ 348233294Sstas name->name.name_string.len < 2) 349178825Sdfr goto out; 350233294Sstas 351178825Sdfr kret = krb5_get_forwarded_creds(context, 352178825Sdfr ac, 353178825Sdfr ccache, 354178825Sdfr KDCOptions2int(fwd_flags), 355178825Sdfr name->name.name_string.val[1], 356178825Sdfr &creds, 357178825Sdfr fwd_data); 358233294Sstas 359178825Sdfr out: 360178825Sdfr if (kret) 361233294Sstas *flags &= ~flagmask; 362178825Sdfr else 363233294Sstas *flags |= flagmask; 364233294Sstas 365178825Sdfr if (creds.client) 366178825Sdfr krb5_free_principal(context, creds.client); 367178825Sdfr if (creds.server) 368178825Sdfr krb5_free_principal(context, creds.server); 369178825Sdfr} 370178825Sdfr 371178825Sdfr/* 372178825Sdfr * first stage of init-sec-context 373178825Sdfr */ 374178825Sdfr 375178825Sdfrstatic OM_uint32 376178825Sdfrinit_auth 377178825Sdfr(OM_uint32 * minor_status, 378233294Sstas gsskrb5_cred cred, 379178825Sdfr gsskrb5_ctx ctx, 380178825Sdfr krb5_context context, 381233294Sstas gss_name_t name, 382178825Sdfr const gss_OID mech_type, 383178825Sdfr OM_uint32 req_flags, 384178825Sdfr OM_uint32 time_req, 385178825Sdfr const gss_buffer_t input_token, 386178825Sdfr gss_OID * actual_mech_type, 387178825Sdfr gss_buffer_t output_token, 388178825Sdfr OM_uint32 * ret_flags, 389178825Sdfr OM_uint32 * time_rec 390178825Sdfr ) 391178825Sdfr{ 392178825Sdfr OM_uint32 ret = GSS_S_FAILURE; 393178825Sdfr krb5_error_code kret; 394178825Sdfr krb5_data outbuf; 395178825Sdfr krb5_data fwd_data; 396178825Sdfr OM_uint32 lifetime_rec; 397233294Sstas int allow_dns = 1; 398178825Sdfr 399178825Sdfr krb5_data_zero(&outbuf); 400178825Sdfr krb5_data_zero(&fwd_data); 401178825Sdfr 402178825Sdfr *minor_status = 0; 403178825Sdfr 404178825Sdfr if (actual_mech_type) 405178825Sdfr *actual_mech_type = GSS_KRB5_MECHANISM; 406178825Sdfr 407233294Sstas if (cred == NULL) { 408233294Sstas kret = krb5_cc_default (context, &ctx->ccache); 409178825Sdfr if (kret) { 410178825Sdfr *minor_status = kret; 411178825Sdfr ret = GSS_S_FAILURE; 412178825Sdfr goto failure; 413178825Sdfr } 414233294Sstas ctx->more_flags |= CLOSE_CCACHE; 415178825Sdfr } else 416233294Sstas ctx->ccache = cred->ccache; 417178825Sdfr 418233294Sstas kret = krb5_cc_get_principal (context, ctx->ccache, &ctx->source); 419178825Sdfr if (kret) { 420178825Sdfr *minor_status = kret; 421178825Sdfr ret = GSS_S_FAILURE; 422178825Sdfr goto failure; 423178825Sdfr } 424178825Sdfr 425178825Sdfr /* 426178825Sdfr * This is hideous glue for (NFS) clients that wants to limit the 427178825Sdfr * available enctypes to what it can support (encryption in 428178825Sdfr * kernel). If there is no enctypes selected for this credential, 429178825Sdfr * reset it to the default set of enctypes. 430178825Sdfr */ 431178825Sdfr { 432178825Sdfr krb5_enctype *enctypes = NULL; 433178825Sdfr 434233294Sstas if (cred && cred->enctypes) 435233294Sstas enctypes = cred->enctypes; 436178825Sdfr krb5_set_default_in_tkt_etypes(context, enctypes); 437178825Sdfr } 438178825Sdfr 439233294Sstas /* canon name if needed for client + target realm */ 440233294Sstas kret = krb5_cc_get_config(context, ctx->ccache, NULL, 441233294Sstas "realm-config", &outbuf); 442233294Sstas if (kret == 0) { 443233294Sstas /* XXX 2 is no server canon */ 444233294Sstas if (outbuf.length < 1 || ((((unsigned char *)outbuf.data)[0]) & 2)) 445233294Sstas allow_dns = 0; 446233294Sstas krb5_data_free(&outbuf); 447233294Sstas } 448233294Sstas 449233294Sstas /* 450233294Sstas * First we try w/o dns, hope that the KDC have register alias 451233294Sstas * (and referrals if cross realm) for this principal. If that 452233294Sstas * fails and if we are allowed to using this realm try again with 453233294Sstas * DNS canonicalizion. 454233294Sstas */ 455233294Sstas ret = gsskrb5_get_creds(minor_status, context, ctx->ccache, 456233294Sstas ctx, name, 0, time_req, 457233294Sstas time_rec); 458233294Sstas if (ret && allow_dns) 459233294Sstas ret = gsskrb5_get_creds(minor_status, context, ctx->ccache, 460233294Sstas ctx, name, 1, time_req, 461233294Sstas time_rec); 462178825Sdfr if (ret) 463178825Sdfr goto failure; 464178825Sdfr 465233294Sstas ctx->lifetime = ctx->kcred->times.endtime; 466178825Sdfr 467233294Sstas ret = _gss_DES3_get_mic_compat(minor_status, ctx, context); 468233294Sstas if (ret) 469233294Sstas goto failure; 470233294Sstas 471178825Sdfr ret = _gsskrb5_lifetime_left(minor_status, 472178825Sdfr context, 473178825Sdfr ctx->lifetime, 474178825Sdfr &lifetime_rec); 475233294Sstas if (ret) 476178825Sdfr goto failure; 477178825Sdfr 478178825Sdfr if (lifetime_rec == 0) { 479178825Sdfr *minor_status = 0; 480178825Sdfr ret = GSS_S_CONTEXT_EXPIRED; 481178825Sdfr goto failure; 482178825Sdfr } 483178825Sdfr 484233294Sstas krb5_auth_con_setkey(context, 485233294Sstas ctx->auth_context, 486233294Sstas &ctx->kcred->session); 487178825Sdfr 488233294Sstas kret = krb5_auth_con_generatelocalsubkey(context, 489178825Sdfr ctx->auth_context, 490233294Sstas &ctx->kcred->session); 491178825Sdfr if(kret) { 492178825Sdfr *minor_status = kret; 493178825Sdfr ret = GSS_S_FAILURE; 494178825Sdfr goto failure; 495178825Sdfr } 496233294Sstas 497233294Sstas return GSS_S_COMPLETE; 498233294Sstas 499233294Sstasfailure: 500233294Sstas if (ctx->ccache && (ctx->more_flags & CLOSE_CCACHE)) 501233294Sstas krb5_cc_close(context, ctx->ccache); 502233294Sstas ctx->ccache = NULL; 503233294Sstas 504233294Sstas return ret; 505233294Sstas 506233294Sstas} 507233294Sstas 508233294Sstasstatic OM_uint32 509233294Sstasinit_auth_restart 510233294Sstas(OM_uint32 * minor_status, 511233294Sstas gsskrb5_cred cred, 512233294Sstas gsskrb5_ctx ctx, 513233294Sstas krb5_context context, 514233294Sstas OM_uint32 req_flags, 515233294Sstas const gss_channel_bindings_t input_chan_bindings, 516233294Sstas const gss_buffer_t input_token, 517233294Sstas gss_OID * actual_mech_type, 518233294Sstas gss_buffer_t output_token, 519233294Sstas OM_uint32 * ret_flags, 520233294Sstas OM_uint32 * time_rec 521233294Sstas ) 522233294Sstas{ 523233294Sstas OM_uint32 ret = GSS_S_FAILURE; 524233294Sstas krb5_error_code kret; 525233294Sstas krb5_flags ap_options; 526233294Sstas krb5_data outbuf; 527233294Sstas uint32_t flags; 528233294Sstas krb5_data authenticator; 529233294Sstas Checksum cksum; 530233294Sstas krb5_enctype enctype; 531233294Sstas krb5_data fwd_data, timedata; 532233294Sstas int32_t offset = 0, oldoffset = 0; 533233294Sstas uint32_t flagmask; 534233294Sstas 535233294Sstas krb5_data_zero(&outbuf); 536233294Sstas krb5_data_zero(&fwd_data); 537233294Sstas 538233294Sstas *minor_status = 0; 539233294Sstas 540233294Sstas /* 541233294Sstas * If the credential doesn't have ok-as-delegate, check if there 542233294Sstas * is a realm setting and use that. 543178825Sdfr */ 544233294Sstas if (!ctx->kcred->flags.b.ok_as_delegate) { 545233294Sstas krb5_data data; 546233294Sstas 547233294Sstas ret = krb5_cc_get_config(context, ctx->ccache, NULL, 548233294Sstas "realm-config", &data); 549233294Sstas if (ret == 0) { 550233294Sstas /* XXX 1 is use ok-as-delegate */ 551233294Sstas if (data.length < 1 || ((((unsigned char *)data.data)[0]) & 1) == 0) 552233294Sstas req_flags &= ~(GSS_C_DELEG_FLAG|GSS_C_DELEG_POLICY_FLAG); 553233294Sstas krb5_data_free(&data); 554233294Sstas } 555178825Sdfr } 556178825Sdfr 557233294Sstas flagmask = 0; 558233294Sstas 559233294Sstas /* if we used GSS_C_DELEG_POLICY_FLAG, trust KDC */ 560233294Sstas if ((req_flags & GSS_C_DELEG_POLICY_FLAG) 561233294Sstas && ctx->kcred->flags.b.ok_as_delegate) 562233294Sstas flagmask |= GSS_C_DELEG_FLAG | GSS_C_DELEG_POLICY_FLAG; 563233294Sstas /* if there still is a GSS_C_DELEG_FLAG, use that */ 564233294Sstas if (req_flags & GSS_C_DELEG_FLAG) 565233294Sstas flagmask |= GSS_C_DELEG_FLAG; 566233294Sstas 567233294Sstas 568178825Sdfr flags = 0; 569178825Sdfr ap_options = 0; 570233294Sstas if (flagmask & GSS_C_DELEG_FLAG) { 571178825Sdfr do_delegation (context, 572233294Sstas ctx->deleg_auth_context, 573233294Sstas ctx->ccache, ctx->kcred, ctx->target, 574233294Sstas &fwd_data, flagmask, &flags); 575233294Sstas } 576233294Sstas 577178825Sdfr if (req_flags & GSS_C_MUTUAL_FLAG) { 578178825Sdfr flags |= GSS_C_MUTUAL_FLAG; 579178825Sdfr ap_options |= AP_OPTS_MUTUAL_REQUIRED; 580178825Sdfr } 581233294Sstas 582178825Sdfr if (req_flags & GSS_C_REPLAY_FLAG) 583178825Sdfr flags |= GSS_C_REPLAY_FLAG; 584178825Sdfr if (req_flags & GSS_C_SEQUENCE_FLAG) 585178825Sdfr flags |= GSS_C_SEQUENCE_FLAG; 586233294Sstas#if 0 587178825Sdfr if (req_flags & GSS_C_ANON_FLAG) 588178825Sdfr ; /* XXX */ 589233294Sstas#endif 590178825Sdfr if (req_flags & GSS_C_DCE_STYLE) { 591178825Sdfr /* GSS_C_DCE_STYLE implies GSS_C_MUTUAL_FLAG */ 592178825Sdfr flags |= GSS_C_DCE_STYLE | GSS_C_MUTUAL_FLAG; 593178825Sdfr ap_options |= AP_OPTS_MUTUAL_REQUIRED; 594178825Sdfr } 595178825Sdfr if (req_flags & GSS_C_IDENTIFY_FLAG) 596178825Sdfr flags |= GSS_C_IDENTIFY_FLAG; 597178825Sdfr if (req_flags & GSS_C_EXTENDED_ERROR_FLAG) 598178825Sdfr flags |= GSS_C_EXTENDED_ERROR_FLAG; 599178825Sdfr 600233294Sstas if (req_flags & GSS_C_CONF_FLAG) { 601233294Sstas flags |= GSS_C_CONF_FLAG; 602233294Sstas } 603233294Sstas if (req_flags & GSS_C_INTEG_FLAG) { 604233294Sstas flags |= GSS_C_INTEG_FLAG; 605233294Sstas } 606233294Sstas if (cred == NULL || !(cred->cred_flags & GSS_CF_NO_CI_FLAGS)) { 607233294Sstas flags |= GSS_C_CONF_FLAG; 608233294Sstas flags |= GSS_C_INTEG_FLAG; 609233294Sstas } 610178825Sdfr flags |= GSS_C_TRANS_FLAG; 611233294Sstas 612178825Sdfr if (ret_flags) 613178825Sdfr *ret_flags = flags; 614178825Sdfr ctx->flags = flags; 615178825Sdfr ctx->more_flags |= LOCAL; 616233294Sstas 617178825Sdfr ret = _gsskrb5_create_8003_checksum (minor_status, 618178825Sdfr input_chan_bindings, 619178825Sdfr flags, 620178825Sdfr &fwd_data, 621178825Sdfr &cksum); 622178825Sdfr krb5_data_free (&fwd_data); 623178825Sdfr if (ret) 624178825Sdfr goto failure; 625178825Sdfr 626178825Sdfr enctype = ctx->auth_context->keyblock->keytype; 627178825Sdfr 628233294Sstas ret = krb5_cc_get_config(context, ctx->ccache, ctx->target, 629233294Sstas "time-offset", &timedata); 630233294Sstas if (ret == 0) { 631233294Sstas if (timedata.length == 4) { 632233294Sstas const u_char *p = timedata.data; 633233294Sstas offset = (p[0] <<24) | (p[1] << 16) | (p[2] << 8) | (p[3] << 0); 634233294Sstas } 635233294Sstas krb5_data_free(&timedata); 636233294Sstas } 637233294Sstas 638233294Sstas if (offset) { 639233294Sstas krb5_get_kdc_sec_offset (context, &oldoffset, NULL); 640233294Sstas krb5_set_kdc_sec_offset (context, offset, -1); 641233294Sstas } 642233294Sstas 643233294Sstas kret = _krb5_build_authenticator(context, 644178825Sdfr ctx->auth_context, 645178825Sdfr enctype, 646233294Sstas ctx->kcred, 647178825Sdfr &cksum, 648178825Sdfr &authenticator, 649178825Sdfr KRB5_KU_AP_REQ_AUTH); 650178825Sdfr 651178825Sdfr if (kret) { 652233294Sstas if (offset) 653233294Sstas krb5_set_kdc_sec_offset (context, oldoffset, -1); 654178825Sdfr *minor_status = kret; 655178825Sdfr ret = GSS_S_FAILURE; 656178825Sdfr goto failure; 657178825Sdfr } 658178825Sdfr 659178825Sdfr kret = krb5_build_ap_req (context, 660178825Sdfr enctype, 661233294Sstas ctx->kcred, 662178825Sdfr ap_options, 663178825Sdfr authenticator, 664178825Sdfr &outbuf); 665233294Sstas if (offset) 666233294Sstas krb5_set_kdc_sec_offset (context, oldoffset, -1); 667178825Sdfr if (kret) { 668178825Sdfr *minor_status = kret; 669178825Sdfr ret = GSS_S_FAILURE; 670178825Sdfr goto failure; 671178825Sdfr } 672178825Sdfr 673233294Sstas if (flags & GSS_C_DCE_STYLE) { 674233294Sstas output_token->value = outbuf.data; 675233294Sstas output_token->length = outbuf.length; 676233294Sstas } else { 677233294Sstas ret = _gsskrb5_encapsulate (minor_status, &outbuf, output_token, 678233294Sstas (u_char *)(intptr_t)"\x01\x00", 679233294Sstas GSS_KRB5_MECHANISM); 680233294Sstas krb5_data_free (&outbuf); 681233294Sstas if (ret) 682233294Sstas goto failure; 683233294Sstas } 684178825Sdfr 685178825Sdfr free_Checksum(&cksum); 686178825Sdfr 687178825Sdfr if (flags & GSS_C_MUTUAL_FLAG) { 688178825Sdfr ctx->state = INITIATOR_WAIT_FOR_MUTAL; 689178825Sdfr return GSS_S_CONTINUE_NEEDED; 690178825Sdfr } 691178825Sdfr 692178825Sdfr return gsskrb5_initiator_ready(minor_status, ctx, context); 693178825Sdfrfailure: 694233294Sstas if (ctx->ccache && (ctx->more_flags & CLOSE_CCACHE)) 695233294Sstas krb5_cc_close(context, ctx->ccache); 696233294Sstas ctx->ccache = NULL; 697178825Sdfr 698178825Sdfr return ret; 699233294Sstas} 700178825Sdfr 701233294Sstasstatic krb5_error_code 702233294Sstashandle_error_packet(krb5_context context, 703233294Sstas gsskrb5_ctx ctx, 704233294Sstas krb5_data indata) 705233294Sstas{ 706233294Sstas krb5_error_code kret; 707233294Sstas KRB_ERROR error; 708233294Sstas 709233294Sstas kret = krb5_rd_error(context, &indata, &error); 710233294Sstas if (kret == 0) { 711233294Sstas kret = krb5_error_from_rd_error(context, &error, NULL); 712233294Sstas 713233294Sstas /* save the time skrew for this host */ 714233294Sstas if (kret == KRB5KRB_AP_ERR_SKEW) { 715233294Sstas krb5_data timedata; 716233294Sstas unsigned char p[4]; 717233294Sstas int32_t t = error.stime - time(NULL); 718233294Sstas 719233294Sstas p[0] = (t >> 24) & 0xFF; 720233294Sstas p[1] = (t >> 16) & 0xFF; 721233294Sstas p[2] = (t >> 8) & 0xFF; 722233294Sstas p[3] = (t >> 0) & 0xFF; 723233294Sstas 724233294Sstas timedata.data = p; 725233294Sstas timedata.length = sizeof(p); 726233294Sstas 727233294Sstas krb5_cc_set_config(context, ctx->ccache, ctx->target, 728233294Sstas "time-offset", &timedata); 729233294Sstas 730233294Sstas if ((ctx->more_flags & RETRIED) == 0) 731233294Sstas ctx->state = INITIATOR_RESTART; 732233294Sstas ctx->more_flags |= RETRIED; 733233294Sstas } 734233294Sstas free_KRB_ERROR (&error); 735233294Sstas } 736233294Sstas return kret; 737178825Sdfr} 738178825Sdfr 739233294Sstas 740178825Sdfrstatic OM_uint32 741178825Sdfrrepl_mutual 742178825Sdfr(OM_uint32 * minor_status, 743178825Sdfr gsskrb5_ctx ctx, 744178825Sdfr krb5_context context, 745178825Sdfr const gss_OID mech_type, 746178825Sdfr OM_uint32 req_flags, 747178825Sdfr OM_uint32 time_req, 748178825Sdfr const gss_channel_bindings_t input_chan_bindings, 749178825Sdfr const gss_buffer_t input_token, 750178825Sdfr gss_OID * actual_mech_type, 751178825Sdfr gss_buffer_t output_token, 752178825Sdfr OM_uint32 * ret_flags, 753178825Sdfr OM_uint32 * time_rec 754178825Sdfr ) 755178825Sdfr{ 756178825Sdfr OM_uint32 ret; 757178825Sdfr krb5_error_code kret; 758178825Sdfr krb5_data indata; 759178825Sdfr krb5_ap_rep_enc_part *repl; 760178825Sdfr 761178825Sdfr output_token->length = 0; 762178825Sdfr output_token->value = NULL; 763178825Sdfr 764178825Sdfr if (actual_mech_type) 765178825Sdfr *actual_mech_type = GSS_KRB5_MECHANISM; 766178825Sdfr 767233294Sstas if (IS_DCE_STYLE(ctx)) { 768178825Sdfr /* There is no OID wrapping. */ 769178825Sdfr indata.length = input_token->length; 770178825Sdfr indata.data = input_token->value; 771233294Sstas kret = krb5_rd_rep(context, 772233294Sstas ctx->auth_context, 773233294Sstas &indata, 774233294Sstas &repl); 775233294Sstas if (kret) { 776233294Sstas ret = _gsskrb5_decapsulate(minor_status, 777233294Sstas input_token, 778233294Sstas &indata, 779233294Sstas "\x03\x00", 780233294Sstas GSS_KRB5_MECHANISM); 781233294Sstas if (ret == GSS_S_COMPLETE) { 782233294Sstas *minor_status = handle_error_packet(context, ctx, indata); 783233294Sstas } else { 784233294Sstas *minor_status = kret; 785233294Sstas } 786233294Sstas return GSS_S_FAILURE; 787233294Sstas } 788178825Sdfr } else { 789178825Sdfr ret = _gsskrb5_decapsulate (minor_status, 790178825Sdfr input_token, 791178825Sdfr &indata, 792178825Sdfr "\x02\x00", 793178825Sdfr GSS_KRB5_MECHANISM); 794233294Sstas if (ret == GSS_S_DEFECTIVE_TOKEN) { 795233294Sstas /* check if there is an error token sent instead */ 796233294Sstas ret = _gsskrb5_decapsulate (minor_status, 797233294Sstas input_token, 798233294Sstas &indata, 799233294Sstas "\x03\x00", 800233294Sstas GSS_KRB5_MECHANISM); 801233294Sstas if (ret == GSS_S_COMPLETE) { 802233294Sstas *minor_status = handle_error_packet(context, ctx, indata); 803233294Sstas return GSS_S_FAILURE; 804233294Sstas } 805178825Sdfr } 806233294Sstas kret = krb5_rd_rep (context, 807233294Sstas ctx->auth_context, 808233294Sstas &indata, 809233294Sstas &repl); 810233294Sstas if (kret) { 811233294Sstas *minor_status = kret; 812233294Sstas return GSS_S_FAILURE; 813233294Sstas } 814178825Sdfr } 815178825Sdfr 816178825Sdfr krb5_free_ap_rep_enc_part (context, 817178825Sdfr repl); 818178825Sdfr 819178825Sdfr *minor_status = 0; 820178825Sdfr if (time_rec) { 821178825Sdfr ret = _gsskrb5_lifetime_left(minor_status, 822178825Sdfr context, 823178825Sdfr ctx->lifetime, 824178825Sdfr time_rec); 825178825Sdfr } else { 826178825Sdfr ret = GSS_S_COMPLETE; 827178825Sdfr } 828178825Sdfr if (ret_flags) 829178825Sdfr *ret_flags = ctx->flags; 830178825Sdfr 831178825Sdfr if (req_flags & GSS_C_DCE_STYLE) { 832233294Sstas int32_t local_seq, remote_seq; 833178825Sdfr krb5_data outbuf; 834178825Sdfr 835233294Sstas /* 836233294Sstas * So DCE_STYLE is strange. The client echos the seq number 837233294Sstas * that the server used in the server's mk_rep in its own 838233294Sstas * mk_rep(). After when done, it resets to it's own seq number 839233294Sstas * for the gss_wrap calls. 840233294Sstas */ 841178825Sdfr 842233294Sstas krb5_auth_con_getremoteseqnumber(context, ctx->auth_context, &remote_seq); 843233294Sstas krb5_auth_con_getlocalseqnumber(context, ctx->auth_context, &local_seq); 844233294Sstas krb5_auth_con_setlocalseqnumber(context, ctx->auth_context, remote_seq); 845233294Sstas 846233294Sstas kret = krb5_mk_rep(context, ctx->auth_context, &outbuf); 847178825Sdfr if (kret) { 848178825Sdfr *minor_status = kret; 849178825Sdfr return GSS_S_FAILURE; 850178825Sdfr } 851233294Sstas 852233294Sstas /* reset local seq number */ 853233294Sstas krb5_auth_con_setlocalseqnumber(context, ctx->auth_context, local_seq); 854233294Sstas 855178825Sdfr output_token->length = outbuf.length; 856178825Sdfr output_token->value = outbuf.data; 857178825Sdfr } 858178825Sdfr 859178825Sdfr return gsskrb5_initiator_ready(minor_status, ctx, context); 860178825Sdfr} 861178825Sdfr 862178825Sdfr/* 863178825Sdfr * gss_init_sec_context 864178825Sdfr */ 865178825Sdfr 866233294SstasOM_uint32 GSSAPI_CALLCONV _gsskrb5_init_sec_context 867178825Sdfr(OM_uint32 * minor_status, 868233294Sstas const gss_cred_id_t cred_handle, 869178825Sdfr gss_ctx_id_t * context_handle, 870178825Sdfr const gss_name_t target_name, 871178825Sdfr const gss_OID mech_type, 872178825Sdfr OM_uint32 req_flags, 873178825Sdfr OM_uint32 time_req, 874178825Sdfr const gss_channel_bindings_t input_chan_bindings, 875178825Sdfr const gss_buffer_t input_token, 876178825Sdfr gss_OID * actual_mech_type, 877178825Sdfr gss_buffer_t output_token, 878178825Sdfr OM_uint32 * ret_flags, 879178825Sdfr OM_uint32 * time_rec 880178825Sdfr ) 881178825Sdfr{ 882178825Sdfr krb5_context context; 883233294Sstas gsskrb5_cred cred = (gsskrb5_cred)cred_handle; 884178825Sdfr gsskrb5_ctx ctx; 885178825Sdfr OM_uint32 ret; 886178825Sdfr 887178825Sdfr GSSAPI_KRB5_INIT (&context); 888178825Sdfr 889178825Sdfr output_token->length = 0; 890178825Sdfr output_token->value = NULL; 891178825Sdfr 892178825Sdfr if (context_handle == NULL) { 893178825Sdfr *minor_status = 0; 894178825Sdfr return GSS_S_FAILURE | GSS_S_CALL_BAD_STRUCTURE; 895178825Sdfr } 896178825Sdfr 897178825Sdfr if (ret_flags) 898178825Sdfr *ret_flags = 0; 899178825Sdfr if (time_rec) 900178825Sdfr *time_rec = 0; 901178825Sdfr 902178825Sdfr if (target_name == GSS_C_NO_NAME) { 903178825Sdfr if (actual_mech_type) 904178825Sdfr *actual_mech_type = GSS_C_NO_OID; 905178825Sdfr *minor_status = 0; 906178825Sdfr return GSS_S_BAD_NAME; 907178825Sdfr } 908178825Sdfr 909233294Sstas if (mech_type != GSS_C_NO_OID && 910178825Sdfr !gss_oid_equal(mech_type, GSS_KRB5_MECHANISM)) 911178825Sdfr return GSS_S_BAD_MECH; 912178825Sdfr 913178825Sdfr if (input_token == GSS_C_NO_BUFFER || input_token->length == 0) { 914233294Sstas OM_uint32 ret1; 915178825Sdfr 916178825Sdfr if (*context_handle != GSS_C_NO_CONTEXT) { 917178825Sdfr *minor_status = 0; 918178825Sdfr return GSS_S_FAILURE | GSS_S_CALL_BAD_STRUCTURE; 919178825Sdfr } 920233294Sstas 921233294Sstas ret1 = _gsskrb5_create_ctx(minor_status, 922178825Sdfr context_handle, 923178825Sdfr context, 924178825Sdfr input_chan_bindings, 925178825Sdfr INITIATOR_START); 926233294Sstas if (ret1) 927233294Sstas return ret1; 928178825Sdfr } 929178825Sdfr 930178825Sdfr if (*context_handle == GSS_C_NO_CONTEXT) { 931178825Sdfr *minor_status = 0; 932178825Sdfr return GSS_S_FAILURE | GSS_S_CALL_BAD_STRUCTURE; 933178825Sdfr } 934178825Sdfr 935178825Sdfr ctx = (gsskrb5_ctx) *context_handle; 936178825Sdfr 937178825Sdfr HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); 938178825Sdfr 939233294Sstas again: 940178825Sdfr switch (ctx->state) { 941178825Sdfr case INITIATOR_START: 942178825Sdfr ret = init_auth(minor_status, 943178825Sdfr cred, 944178825Sdfr ctx, 945178825Sdfr context, 946233294Sstas target_name, 947178825Sdfr mech_type, 948178825Sdfr req_flags, 949178825Sdfr time_req, 950178825Sdfr input_token, 951178825Sdfr actual_mech_type, 952178825Sdfr output_token, 953178825Sdfr ret_flags, 954178825Sdfr time_rec); 955233294Sstas if (ret != GSS_S_COMPLETE) 956233294Sstas break; 957233294Sstas /* FALL THOUGH */ 958233294Sstas case INITIATOR_RESTART: 959233294Sstas ret = init_auth_restart(minor_status, 960233294Sstas cred, 961233294Sstas ctx, 962233294Sstas context, 963233294Sstas req_flags, 964233294Sstas input_chan_bindings, 965233294Sstas input_token, 966233294Sstas actual_mech_type, 967233294Sstas output_token, 968233294Sstas ret_flags, 969233294Sstas time_rec); 970178825Sdfr break; 971178825Sdfr case INITIATOR_WAIT_FOR_MUTAL: 972178825Sdfr ret = repl_mutual(minor_status, 973178825Sdfr ctx, 974178825Sdfr context, 975178825Sdfr mech_type, 976178825Sdfr req_flags, 977178825Sdfr time_req, 978178825Sdfr input_chan_bindings, 979178825Sdfr input_token, 980178825Sdfr actual_mech_type, 981178825Sdfr output_token, 982178825Sdfr ret_flags, 983178825Sdfr time_rec); 984233294Sstas if (ctx->state == INITIATOR_RESTART) 985233294Sstas goto again; 986178825Sdfr break; 987178825Sdfr case INITIATOR_READY: 988233294Sstas /* 989178825Sdfr * If we get there, the caller have called 990178825Sdfr * gss_init_sec_context() one time too many. 991178825Sdfr */ 992233294Sstas _gsskrb5_set_status(EINVAL, "init_sec_context " 993233294Sstas "called one time too many"); 994233294Sstas *minor_status = EINVAL; 995178825Sdfr ret = GSS_S_BAD_STATUS; 996178825Sdfr break; 997178825Sdfr default: 998233294Sstas _gsskrb5_set_status(EINVAL, "init_sec_context " 999233294Sstas "invalid state %d for client", 1000233294Sstas (int)ctx->state); 1001233294Sstas *minor_status = EINVAL; 1002178825Sdfr ret = GSS_S_BAD_STATUS; 1003178825Sdfr break; 1004178825Sdfr } 1005178825Sdfr HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 1006178825Sdfr 1007178825Sdfr /* destroy context in case of error */ 1008178825Sdfr if (GSS_ERROR(ret)) { 1009178825Sdfr OM_uint32 min2; 1010178825Sdfr _gsskrb5_delete_sec_context(&min2, context_handle, GSS_C_NO_BUFFER); 1011178825Sdfr } 1012178825Sdfr 1013178825Sdfr return ret; 1014178825Sdfr 1015178825Sdfr} 1016