1 /*
2 * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34 #include "gsskrb5_locl.h"
35
36 /*
37 * copy the addresses from `input_chan_bindings' (if any) to
38 * the auth context `ac'
39 */
40
41 static OM_uint32
set_addresses(krb5_context context,krb5_auth_context ac,const gss_channel_bindings_t input_chan_bindings)42 set_addresses (krb5_context context,
43 krb5_auth_context ac,
44 const gss_channel_bindings_t input_chan_bindings)
45 {
46 /* Port numbers are expected to be in application_data.value,
47 * initator's port first */
48
49 krb5_address initiator_addr, acceptor_addr;
50 krb5_error_code kret;
51
52 if (input_chan_bindings == GSS_C_NO_CHANNEL_BINDINGS
53 || input_chan_bindings->application_data.length !=
54 2 * sizeof(ac->local_port))
55 return 0;
56
57 memset(&initiator_addr, 0, sizeof(initiator_addr));
58 memset(&acceptor_addr, 0, sizeof(acceptor_addr));
59
60 ac->local_port =
61 *(int16_t *) input_chan_bindings->application_data.value;
62
63 ac->remote_port =
64 *((int16_t *) input_chan_bindings->application_data.value + 1);
65
66 kret = _gsskrb5i_address_to_krb5addr(context,
67 input_chan_bindings->acceptor_addrtype,
68 &input_chan_bindings->acceptor_address,
69 ac->remote_port,
70 &acceptor_addr);
71 if (kret)
72 return kret;
73
74 kret = _gsskrb5i_address_to_krb5addr(context,
75 input_chan_bindings->initiator_addrtype,
76 &input_chan_bindings->initiator_address,
77 ac->local_port,
78 &initiator_addr);
79 if (kret) {
80 krb5_free_address (context, &acceptor_addr);
81 return kret;
82 }
83
84 kret = krb5_auth_con_setaddrs(context,
85 ac,
86 &initiator_addr, /* local address */
87 &acceptor_addr); /* remote address */
88
89 krb5_free_address (context, &initiator_addr);
90 krb5_free_address (context, &acceptor_addr);
91
92 #if 0
93 free(input_chan_bindings->application_data.value);
94 input_chan_bindings->application_data.value = NULL;
95 input_chan_bindings->application_data.length = 0;
96 #endif
97
98 return kret;
99 }
100
101 OM_uint32
_gsskrb5_create_ctx(OM_uint32 * minor_status,gss_ctx_id_t * context_handle,krb5_context context,const gss_channel_bindings_t input_chan_bindings,enum gss_ctx_id_t_state state)102 _gsskrb5_create_ctx(
103 OM_uint32 * minor_status,
104 gss_ctx_id_t * context_handle,
105 krb5_context context,
106 const gss_channel_bindings_t input_chan_bindings,
107 enum gss_ctx_id_t_state state)
108 {
109 krb5_error_code kret;
110 gsskrb5_ctx ctx;
111
112 *context_handle = NULL;
113
114 ctx = malloc(sizeof(*ctx));
115 if (ctx == NULL) {
116 *minor_status = ENOMEM;
117 return GSS_S_FAILURE;
118 }
119 ctx->auth_context = NULL;
120 ctx->deleg_auth_context = NULL;
121 ctx->source = NULL;
122 ctx->target = NULL;
123 ctx->kcred = NULL;
124 ctx->ccache = NULL;
125 ctx->state = state;
126 ctx->flags = 0;
127 ctx->more_flags = 0;
128 ctx->service_keyblock = NULL;
129 ctx->ticket = NULL;
130 krb5_data_zero(&ctx->fwd_data);
131 ctx->lifetime = GSS_C_INDEFINITE;
132 ctx->order = NULL;
133 ctx->crypto = NULL;
134 HEIMDAL_MUTEX_init(&ctx->ctx_id_mutex);
135
136 kret = krb5_auth_con_init (context, &ctx->auth_context);
137 if (kret) {
138 *minor_status = kret;
139 HEIMDAL_MUTEX_destroy(&ctx->ctx_id_mutex);
140 return GSS_S_FAILURE;
141 }
142
143 kret = krb5_auth_con_init (context, &ctx->deleg_auth_context);
144 if (kret) {
145 *minor_status = kret;
146 krb5_auth_con_free(context, ctx->auth_context);
147 HEIMDAL_MUTEX_destroy(&ctx->ctx_id_mutex);
148 return GSS_S_FAILURE;
149 }
150
151 kret = set_addresses(context, ctx->auth_context, input_chan_bindings);
152 if (kret) {
153 *minor_status = kret;
154
155 krb5_auth_con_free(context, ctx->auth_context);
156 krb5_auth_con_free(context, ctx->deleg_auth_context);
157
158 HEIMDAL_MUTEX_destroy(&ctx->ctx_id_mutex);
159
160 return GSS_S_BAD_BINDINGS;
161 }
162
163 kret = set_addresses(context, ctx->deleg_auth_context, input_chan_bindings);
164 if (kret) {
165 *minor_status = kret;
166
167 krb5_auth_con_free(context, ctx->auth_context);
168 krb5_auth_con_free(context, ctx->deleg_auth_context);
169
170 HEIMDAL_MUTEX_destroy(&ctx->ctx_id_mutex);
171
172 return GSS_S_BAD_BINDINGS;
173 }
174
175 /*
176 * We need a sequence number
177 */
178
179 krb5_auth_con_addflags(context,
180 ctx->auth_context,
181 KRB5_AUTH_CONTEXT_DO_SEQUENCE |
182 KRB5_AUTH_CONTEXT_CLEAR_FORWARDED_CRED,
183 NULL);
184
185 /*
186 * We need a sequence number
187 */
188
189 krb5_auth_con_addflags(context,
190 ctx->deleg_auth_context,
191 KRB5_AUTH_CONTEXT_DO_SEQUENCE |
192 KRB5_AUTH_CONTEXT_CLEAR_FORWARDED_CRED,
193 NULL);
194
195 *context_handle = (gss_ctx_id_t)ctx;
196
197 return GSS_S_COMPLETE;
198 }
199
200
201 static OM_uint32
gsskrb5_get_creds(OM_uint32 * minor_status,krb5_context context,krb5_ccache ccache,gsskrb5_ctx ctx,const gss_name_t target_name,int use_dns,OM_uint32 time_req,OM_uint32 * time_rec)202 gsskrb5_get_creds(
203 OM_uint32 * minor_status,
204 krb5_context context,
205 krb5_ccache ccache,
206 gsskrb5_ctx ctx,
207 const gss_name_t target_name,
208 int use_dns,
209 OM_uint32 time_req,
210 OM_uint32 * time_rec)
211 {
212 OM_uint32 ret;
213 krb5_error_code kret;
214 krb5_creds this_cred;
215 OM_uint32 lifetime_rec;
216
217 if (ctx->target) {
218 krb5_free_principal(context, ctx->target);
219 ctx->target = NULL;
220 }
221 if (ctx->kcred) {
222 krb5_free_creds(context, ctx->kcred);
223 ctx->kcred = NULL;
224 }
225
226 ret = _gsskrb5_canon_name(minor_status, context, use_dns,
227 ctx->source, target_name, &ctx->target);
228 if (ret)
229 return ret;
230
231 memset(&this_cred, 0, sizeof(this_cred));
232 this_cred.client = ctx->source;
233 this_cred.server = ctx->target;
234
235 if (time_req && time_req != GSS_C_INDEFINITE) {
236 krb5_timestamp ts;
237
238 krb5_timeofday (context, &ts);
239 this_cred.times.endtime = ts + time_req;
240 } else {
241 this_cred.times.endtime = 0;
242 }
243
244 this_cred.session.keytype = KEYTYPE_NULL;
245
246 kret = krb5_get_credentials(context,
247 0,
248 ccache,
249 &this_cred,
250 &ctx->kcred);
251 if (kret) {
252 *minor_status = kret;
253 return GSS_S_FAILURE;
254 }
255
256 ctx->lifetime = ctx->kcred->times.endtime;
257
258 ret = _gsskrb5_lifetime_left(minor_status, context,
259 ctx->lifetime, &lifetime_rec);
260 if (ret) return ret;
261
262 if (lifetime_rec == 0) {
263 *minor_status = 0;
264 return GSS_S_CONTEXT_EXPIRED;
265 }
266
267 if (time_rec) *time_rec = lifetime_rec;
268
269 return GSS_S_COMPLETE;
270 }
271
272 static OM_uint32
gsskrb5_initiator_ready(OM_uint32 * minor_status,gsskrb5_ctx ctx,krb5_context context)273 gsskrb5_initiator_ready(
274 OM_uint32 * minor_status,
275 gsskrb5_ctx ctx,
276 krb5_context context)
277 {
278 OM_uint32 ret;
279 int32_t seq_number;
280 int is_cfx = 0;
281 OM_uint32 flags = ctx->flags;
282
283 krb5_free_creds(context, ctx->kcred);
284 ctx->kcred = NULL;
285
286 if (ctx->more_flags & CLOSE_CCACHE)
287 krb5_cc_close(context, ctx->ccache);
288 ctx->ccache = NULL;
289
290 krb5_auth_con_getremoteseqnumber (context, ctx->auth_context, &seq_number);
291
292 _gsskrb5i_is_cfx(context, ctx, 0);
293 is_cfx = (ctx->more_flags & IS_CFX);
294
295 ret = _gssapi_msg_order_create(minor_status,
296 &ctx->order,
297 _gssapi_msg_order_f(flags),
298 seq_number, 0, is_cfx);
299 if (ret) return ret;
300
301 ctx->state = INITIATOR_READY;
302 ctx->more_flags |= OPEN;
303
304 return GSS_S_COMPLETE;
305 }
306
307 /*
308 * handle delegated creds in init-sec-context
309 */
310
311 static void
do_delegation(krb5_context context,krb5_auth_context ac,krb5_ccache ccache,krb5_creds * cred,krb5_const_principal name,krb5_data * fwd_data,uint32_t flagmask,uint32_t * flags)312 do_delegation (krb5_context context,
313 krb5_auth_context ac,
314 krb5_ccache ccache,
315 krb5_creds *cred,
316 krb5_const_principal name,
317 krb5_data *fwd_data,
318 uint32_t flagmask,
319 uint32_t *flags)
320 {
321 krb5_creds creds;
322 KDCOptions fwd_flags;
323 krb5_error_code kret;
324
325 memset (&creds, 0, sizeof(creds));
326 krb5_data_zero (fwd_data);
327
328 kret = krb5_cc_get_principal(context, ccache, &creds.client);
329 if (kret)
330 goto out;
331
332 kret = krb5_make_principal(context,
333 &creds.server,
334 creds.client->realm,
335 KRB5_TGS_NAME,
336 creds.client->realm,
337 NULL);
338 if (kret)
339 goto out;
340
341 creds.times.endtime = 0;
342
343 memset(&fwd_flags, 0, sizeof(fwd_flags));
344 fwd_flags.forwarded = 1;
345 fwd_flags.forwardable = 1;
346
347 if ( /*target_name->name.name_type != KRB5_NT_SRV_HST ||*/
348 name->name.name_string.len < 2)
349 goto out;
350
351 kret = krb5_get_forwarded_creds(context,
352 ac,
353 ccache,
354 KDCOptions2int(fwd_flags),
355 name->name.name_string.val[1],
356 &creds,
357 fwd_data);
358
359 out:
360 if (kret)
361 *flags &= ~flagmask;
362 else
363 *flags |= flagmask;
364
365 if (creds.client)
366 krb5_free_principal(context, creds.client);
367 if (creds.server)
368 krb5_free_principal(context, creds.server);
369 }
370
371 /*
372 * first stage of init-sec-context
373 */
374
375 static OM_uint32
init_auth(OM_uint32 * minor_status,gsskrb5_cred cred,gsskrb5_ctx ctx,krb5_context context,gss_name_t name,const gss_OID mech_type,OM_uint32 req_flags,OM_uint32 time_req,const gss_buffer_t input_token,gss_OID * actual_mech_type,gss_buffer_t output_token,OM_uint32 * ret_flags,OM_uint32 * time_rec)376 init_auth
377 (OM_uint32 * minor_status,
378 gsskrb5_cred cred,
379 gsskrb5_ctx ctx,
380 krb5_context context,
381 gss_name_t name,
382 const gss_OID mech_type,
383 OM_uint32 req_flags,
384 OM_uint32 time_req,
385 const gss_buffer_t input_token,
386 gss_OID * actual_mech_type,
387 gss_buffer_t output_token,
388 OM_uint32 * ret_flags,
389 OM_uint32 * time_rec
390 )
391 {
392 OM_uint32 ret = GSS_S_FAILURE;
393 krb5_error_code kret;
394 krb5_data outbuf;
395 krb5_data fwd_data;
396 OM_uint32 lifetime_rec;
397 int allow_dns = 1;
398
399 krb5_data_zero(&outbuf);
400 krb5_data_zero(&fwd_data);
401
402 *minor_status = 0;
403
404 if (actual_mech_type)
405 *actual_mech_type = GSS_KRB5_MECHANISM;
406
407 if (cred == NULL) {
408 kret = krb5_cc_default (context, &ctx->ccache);
409 if (kret) {
410 *minor_status = kret;
411 ret = GSS_S_FAILURE;
412 goto failure;
413 }
414 ctx->more_flags |= CLOSE_CCACHE;
415 } else
416 ctx->ccache = cred->ccache;
417
418 kret = krb5_cc_get_principal (context, ctx->ccache, &ctx->source);
419 if (kret) {
420 *minor_status = kret;
421 ret = GSS_S_FAILURE;
422 goto failure;
423 }
424
425 /*
426 * This is hideous glue for (NFS) clients that wants to limit the
427 * available enctypes to what it can support (encryption in
428 * kernel). If there is no enctypes selected for this credential,
429 * reset it to the default set of enctypes.
430 */
431 {
432 krb5_enctype *enctypes = NULL;
433
434 if (cred && cred->enctypes)
435 enctypes = cred->enctypes;
436 krb5_set_default_in_tkt_etypes(context, enctypes);
437 }
438
439 /* canon name if needed for client + target realm */
440 kret = krb5_cc_get_config(context, ctx->ccache, NULL,
441 "realm-config", &outbuf);
442 if (kret == 0) {
443 /* XXX 2 is no server canon */
444 if (outbuf.length < 1 || ((((unsigned char *)outbuf.data)[0]) & 2))
445 allow_dns = 0;
446 krb5_data_free(&outbuf);
447 }
448
449 /*
450 * First we try w/o dns, hope that the KDC have register alias
451 * (and referrals if cross realm) for this principal. If that
452 * fails and if we are allowed to using this realm try again with
453 * DNS canonicalizion.
454 */
455 ret = gsskrb5_get_creds(minor_status, context, ctx->ccache,
456 ctx, name, 0, time_req,
457 time_rec);
458 if (ret && allow_dns)
459 ret = gsskrb5_get_creds(minor_status, context, ctx->ccache,
460 ctx, name, 1, time_req,
461 time_rec);
462 if (ret)
463 goto failure;
464
465 ctx->lifetime = ctx->kcred->times.endtime;
466
467 ret = _gss_DES3_get_mic_compat(minor_status, ctx, context);
468 if (ret)
469 goto failure;
470
471 ret = _gsskrb5_lifetime_left(minor_status,
472 context,
473 ctx->lifetime,
474 &lifetime_rec);
475 if (ret)
476 goto failure;
477
478 if (lifetime_rec == 0) {
479 *minor_status = 0;
480 ret = GSS_S_CONTEXT_EXPIRED;
481 goto failure;
482 }
483
484 krb5_auth_con_setkey(context,
485 ctx->auth_context,
486 &ctx->kcred->session);
487
488 kret = krb5_auth_con_generatelocalsubkey(context,
489 ctx->auth_context,
490 &ctx->kcred->session);
491 if(kret) {
492 *minor_status = kret;
493 ret = GSS_S_FAILURE;
494 goto failure;
495 }
496
497 return GSS_S_COMPLETE;
498
499 failure:
500 if (ctx->ccache && (ctx->more_flags & CLOSE_CCACHE))
501 krb5_cc_close(context, ctx->ccache);
502 ctx->ccache = NULL;
503
504 return ret;
505
506 }
507
508 static OM_uint32
init_auth_restart(OM_uint32 * minor_status,gsskrb5_cred cred,gsskrb5_ctx ctx,krb5_context context,OM_uint32 req_flags,const gss_channel_bindings_t input_chan_bindings,const gss_buffer_t input_token,gss_OID * actual_mech_type,gss_buffer_t output_token,OM_uint32 * ret_flags,OM_uint32 * time_rec)509 init_auth_restart
510 (OM_uint32 * minor_status,
511 gsskrb5_cred cred,
512 gsskrb5_ctx ctx,
513 krb5_context context,
514 OM_uint32 req_flags,
515 const gss_channel_bindings_t input_chan_bindings,
516 const gss_buffer_t input_token,
517 gss_OID * actual_mech_type,
518 gss_buffer_t output_token,
519 OM_uint32 * ret_flags,
520 OM_uint32 * time_rec
521 )
522 {
523 OM_uint32 ret = GSS_S_FAILURE;
524 krb5_error_code kret;
525 krb5_flags ap_options;
526 krb5_data outbuf;
527 uint32_t flags;
528 krb5_data authenticator;
529 Checksum cksum;
530 krb5_enctype enctype;
531 krb5_data fwd_data, timedata;
532 int32_t offset = 0, oldoffset = 0;
533 uint32_t flagmask;
534
535 krb5_data_zero(&outbuf);
536 krb5_data_zero(&fwd_data);
537
538 *minor_status = 0;
539
540 /*
541 * If the credential doesn't have ok-as-delegate, check if there
542 * is a realm setting and use that.
543 */
544 if (!ctx->kcred->flags.b.ok_as_delegate) {
545 krb5_data data;
546
547 ret = krb5_cc_get_config(context, ctx->ccache, NULL,
548 "realm-config", &data);
549 if (ret == 0) {
550 /* XXX 1 is use ok-as-delegate */
551 if (data.length < 1 || ((((unsigned char *)data.data)[0]) & 1) == 0)
552 req_flags &= ~(GSS_C_DELEG_FLAG|GSS_C_DELEG_POLICY_FLAG);
553 krb5_data_free(&data);
554 }
555 }
556
557 flagmask = 0;
558
559 /* if we used GSS_C_DELEG_POLICY_FLAG, trust KDC */
560 if ((req_flags & GSS_C_DELEG_POLICY_FLAG)
561 && ctx->kcred->flags.b.ok_as_delegate)
562 flagmask |= GSS_C_DELEG_FLAG | GSS_C_DELEG_POLICY_FLAG;
563 /* if there still is a GSS_C_DELEG_FLAG, use that */
564 if (req_flags & GSS_C_DELEG_FLAG)
565 flagmask |= GSS_C_DELEG_FLAG;
566
567
568 flags = 0;
569 ap_options = 0;
570 if (flagmask & GSS_C_DELEG_FLAG) {
571 do_delegation (context,
572 ctx->deleg_auth_context,
573 ctx->ccache, ctx->kcred, ctx->target,
574 &fwd_data, flagmask, &flags);
575 }
576
577 if (req_flags & GSS_C_MUTUAL_FLAG) {
578 flags |= GSS_C_MUTUAL_FLAG;
579 ap_options |= AP_OPTS_MUTUAL_REQUIRED;
580 }
581
582 if (req_flags & GSS_C_REPLAY_FLAG)
583 flags |= GSS_C_REPLAY_FLAG;
584 if (req_flags & GSS_C_SEQUENCE_FLAG)
585 flags |= GSS_C_SEQUENCE_FLAG;
586 #if 0
587 if (req_flags & GSS_C_ANON_FLAG)
588 ; /* XXX */
589 #endif
590 if (req_flags & GSS_C_DCE_STYLE) {
591 /* GSS_C_DCE_STYLE implies GSS_C_MUTUAL_FLAG */
592 flags |= GSS_C_DCE_STYLE | GSS_C_MUTUAL_FLAG;
593 ap_options |= AP_OPTS_MUTUAL_REQUIRED;
594 }
595 if (req_flags & GSS_C_IDENTIFY_FLAG)
596 flags |= GSS_C_IDENTIFY_FLAG;
597 if (req_flags & GSS_C_EXTENDED_ERROR_FLAG)
598 flags |= GSS_C_EXTENDED_ERROR_FLAG;
599
600 if (req_flags & GSS_C_CONF_FLAG) {
601 flags |= GSS_C_CONF_FLAG;
602 }
603 if (req_flags & GSS_C_INTEG_FLAG) {
604 flags |= GSS_C_INTEG_FLAG;
605 }
606 if (cred == NULL || !(cred->cred_flags & GSS_CF_NO_CI_FLAGS)) {
607 flags |= GSS_C_CONF_FLAG;
608 flags |= GSS_C_INTEG_FLAG;
609 }
610 flags |= GSS_C_TRANS_FLAG;
611
612 if (ret_flags)
613 *ret_flags = flags;
614 ctx->flags = flags;
615 ctx->more_flags |= LOCAL;
616
617 ret = _gsskrb5_create_8003_checksum (minor_status,
618 input_chan_bindings,
619 flags,
620 &fwd_data,
621 &cksum);
622 krb5_data_free (&fwd_data);
623 if (ret)
624 goto failure;
625
626 enctype = ctx->auth_context->keyblock->keytype;
627
628 ret = krb5_cc_get_config(context, ctx->ccache, ctx->target,
629 "time-offset", &timedata);
630 if (ret == 0) {
631 if (timedata.length == 4) {
632 const u_char *p = timedata.data;
633 offset = (p[0] <<24) | (p[1] << 16) | (p[2] << 8) | (p[3] << 0);
634 }
635 krb5_data_free(&timedata);
636 }
637
638 if (offset) {
639 krb5_get_kdc_sec_offset (context, &oldoffset, NULL);
640 krb5_set_kdc_sec_offset (context, offset, -1);
641 }
642
643 kret = _krb5_build_authenticator(context,
644 ctx->auth_context,
645 enctype,
646 ctx->kcred,
647 &cksum,
648 &authenticator,
649 KRB5_KU_AP_REQ_AUTH);
650
651 if (kret) {
652 if (offset)
653 krb5_set_kdc_sec_offset (context, oldoffset, -1);
654 *minor_status = kret;
655 ret = GSS_S_FAILURE;
656 goto failure;
657 }
658
659 kret = krb5_build_ap_req (context,
660 enctype,
661 ctx->kcred,
662 ap_options,
663 authenticator,
664 &outbuf);
665 if (offset)
666 krb5_set_kdc_sec_offset (context, oldoffset, -1);
667 if (kret) {
668 *minor_status = kret;
669 ret = GSS_S_FAILURE;
670 goto failure;
671 }
672
673 if (flags & GSS_C_DCE_STYLE) {
674 output_token->value = outbuf.data;
675 output_token->length = outbuf.length;
676 } else {
677 ret = _gsskrb5_encapsulate (minor_status, &outbuf, output_token,
678 (u_char *)(intptr_t)"\x01\x00",
679 GSS_KRB5_MECHANISM);
680 krb5_data_free (&outbuf);
681 if (ret)
682 goto failure;
683 }
684
685 free_Checksum(&cksum);
686
687 if (flags & GSS_C_MUTUAL_FLAG) {
688 ctx->state = INITIATOR_WAIT_FOR_MUTAL;
689 return GSS_S_CONTINUE_NEEDED;
690 }
691
692 return gsskrb5_initiator_ready(minor_status, ctx, context);
693 failure:
694 if (ctx->ccache && (ctx->more_flags & CLOSE_CCACHE))
695 krb5_cc_close(context, ctx->ccache);
696 ctx->ccache = NULL;
697
698 return ret;
699 }
700
701 static krb5_error_code
handle_error_packet(krb5_context context,gsskrb5_ctx ctx,krb5_data indata)702 handle_error_packet(krb5_context context,
703 gsskrb5_ctx ctx,
704 krb5_data indata)
705 {
706 krb5_error_code kret;
707 KRB_ERROR error;
708
709 kret = krb5_rd_error(context, &indata, &error);
710 if (kret == 0) {
711 kret = krb5_error_from_rd_error(context, &error, NULL);
712
713 /* save the time skrew for this host */
714 if (kret == KRB5KRB_AP_ERR_SKEW) {
715 krb5_data timedata;
716 unsigned char p[4];
717 int32_t t = error.stime - time(NULL);
718
719 p[0] = (t >> 24) & 0xFF;
720 p[1] = (t >> 16) & 0xFF;
721 p[2] = (t >> 8) & 0xFF;
722 p[3] = (t >> 0) & 0xFF;
723
724 timedata.data = p;
725 timedata.length = sizeof(p);
726
727 krb5_cc_set_config(context, ctx->ccache, ctx->target,
728 "time-offset", &timedata);
729
730 if ((ctx->more_flags & RETRIED) == 0)
731 ctx->state = INITIATOR_RESTART;
732 ctx->more_flags |= RETRIED;
733 }
734 free_KRB_ERROR (&error);
735 }
736 return kret;
737 }
738
739
740 static OM_uint32
repl_mutual(OM_uint32 * minor_status,gsskrb5_ctx ctx,krb5_context context,const gss_OID mech_type,OM_uint32 req_flags,OM_uint32 time_req,const gss_channel_bindings_t input_chan_bindings,const gss_buffer_t input_token,gss_OID * actual_mech_type,gss_buffer_t output_token,OM_uint32 * ret_flags,OM_uint32 * time_rec)741 repl_mutual
742 (OM_uint32 * minor_status,
743 gsskrb5_ctx ctx,
744 krb5_context context,
745 const gss_OID mech_type,
746 OM_uint32 req_flags,
747 OM_uint32 time_req,
748 const gss_channel_bindings_t input_chan_bindings,
749 const gss_buffer_t input_token,
750 gss_OID * actual_mech_type,
751 gss_buffer_t output_token,
752 OM_uint32 * ret_flags,
753 OM_uint32 * time_rec
754 )
755 {
756 OM_uint32 ret;
757 krb5_error_code kret;
758 krb5_data indata;
759 krb5_ap_rep_enc_part *repl;
760
761 output_token->length = 0;
762 output_token->value = NULL;
763
764 if (actual_mech_type)
765 *actual_mech_type = GSS_KRB5_MECHANISM;
766
767 if (IS_DCE_STYLE(ctx)) {
768 /* There is no OID wrapping. */
769 indata.length = input_token->length;
770 indata.data = input_token->value;
771 kret = krb5_rd_rep(context,
772 ctx->auth_context,
773 &indata,
774 &repl);
775 if (kret) {
776 ret = _gsskrb5_decapsulate(minor_status,
777 input_token,
778 &indata,
779 "\x03\x00",
780 GSS_KRB5_MECHANISM);
781 if (ret == GSS_S_COMPLETE) {
782 *minor_status = handle_error_packet(context, ctx, indata);
783 } else {
784 *minor_status = kret;
785 }
786 return GSS_S_FAILURE;
787 }
788 } else {
789 ret = _gsskrb5_decapsulate (minor_status,
790 input_token,
791 &indata,
792 "\x02\x00",
793 GSS_KRB5_MECHANISM);
794 if (ret == GSS_S_DEFECTIVE_TOKEN) {
795 /* check if there is an error token sent instead */
796 ret = _gsskrb5_decapsulate (minor_status,
797 input_token,
798 &indata,
799 "\x03\x00",
800 GSS_KRB5_MECHANISM);
801 if (ret == GSS_S_COMPLETE) {
802 *minor_status = handle_error_packet(context, ctx, indata);
803 return GSS_S_FAILURE;
804 }
805 }
806 kret = krb5_rd_rep (context,
807 ctx->auth_context,
808 &indata,
809 &repl);
810 if (kret) {
811 *minor_status = kret;
812 return GSS_S_FAILURE;
813 }
814 }
815
816 krb5_free_ap_rep_enc_part (context,
817 repl);
818
819 *minor_status = 0;
820 if (time_rec) {
821 ret = _gsskrb5_lifetime_left(minor_status,
822 context,
823 ctx->lifetime,
824 time_rec);
825 } else {
826 ret = GSS_S_COMPLETE;
827 }
828 if (ret_flags)
829 *ret_flags = ctx->flags;
830
831 if (req_flags & GSS_C_DCE_STYLE) {
832 int32_t local_seq, remote_seq;
833 krb5_data outbuf;
834
835 /*
836 * So DCE_STYLE is strange. The client echos the seq number
837 * that the server used in the server's mk_rep in its own
838 * mk_rep(). After when done, it resets to it's own seq number
839 * for the gss_wrap calls.
840 */
841
842 krb5_auth_con_getremoteseqnumber(context, ctx->auth_context, &remote_seq);
843 krb5_auth_con_getlocalseqnumber(context, ctx->auth_context, &local_seq);
844 krb5_auth_con_setlocalseqnumber(context, ctx->auth_context, remote_seq);
845
846 kret = krb5_mk_rep(context, ctx->auth_context, &outbuf);
847 if (kret) {
848 *minor_status = kret;
849 return GSS_S_FAILURE;
850 }
851
852 /* reset local seq number */
853 krb5_auth_con_setlocalseqnumber(context, ctx->auth_context, local_seq);
854
855 output_token->length = outbuf.length;
856 output_token->value = outbuf.data;
857 }
858
859 return gsskrb5_initiator_ready(minor_status, ctx, context);
860 }
861
862 /*
863 * gss_init_sec_context
864 */
865
_gsskrb5_init_sec_context(OM_uint32 * minor_status,const gss_cred_id_t cred_handle,gss_ctx_id_t * context_handle,const gss_name_t target_name,const gss_OID mech_type,OM_uint32 req_flags,OM_uint32 time_req,const gss_channel_bindings_t input_chan_bindings,const gss_buffer_t input_token,gss_OID * actual_mech_type,gss_buffer_t output_token,OM_uint32 * ret_flags,OM_uint32 * time_rec)866 OM_uint32 GSSAPI_CALLCONV _gsskrb5_init_sec_context
867 (OM_uint32 * minor_status,
868 const gss_cred_id_t cred_handle,
869 gss_ctx_id_t * context_handle,
870 const gss_name_t target_name,
871 const gss_OID mech_type,
872 OM_uint32 req_flags,
873 OM_uint32 time_req,
874 const gss_channel_bindings_t input_chan_bindings,
875 const gss_buffer_t input_token,
876 gss_OID * actual_mech_type,
877 gss_buffer_t output_token,
878 OM_uint32 * ret_flags,
879 OM_uint32 * time_rec
880 )
881 {
882 krb5_context context;
883 gsskrb5_cred cred = (gsskrb5_cred)cred_handle;
884 gsskrb5_ctx ctx;
885 OM_uint32 ret;
886
887 GSSAPI_KRB5_INIT (&context);
888
889 output_token->length = 0;
890 output_token->value = NULL;
891
892 if (context_handle == NULL) {
893 *minor_status = 0;
894 return GSS_S_FAILURE | GSS_S_CALL_BAD_STRUCTURE;
895 }
896
897 if (ret_flags)
898 *ret_flags = 0;
899 if (time_rec)
900 *time_rec = 0;
901
902 if (target_name == GSS_C_NO_NAME) {
903 if (actual_mech_type)
904 *actual_mech_type = GSS_C_NO_OID;
905 *minor_status = 0;
906 return GSS_S_BAD_NAME;
907 }
908
909 if (mech_type != GSS_C_NO_OID &&
910 !gss_oid_equal(mech_type, GSS_KRB5_MECHANISM))
911 return GSS_S_BAD_MECH;
912
913 if (input_token == GSS_C_NO_BUFFER || input_token->length == 0) {
914 OM_uint32 ret1;
915
916 if (*context_handle != GSS_C_NO_CONTEXT) {
917 *minor_status = 0;
918 return GSS_S_FAILURE | GSS_S_CALL_BAD_STRUCTURE;
919 }
920
921 ret1 = _gsskrb5_create_ctx(minor_status,
922 context_handle,
923 context,
924 input_chan_bindings,
925 INITIATOR_START);
926 if (ret1)
927 return ret1;
928 }
929
930 if (*context_handle == GSS_C_NO_CONTEXT) {
931 *minor_status = 0;
932 return GSS_S_FAILURE | GSS_S_CALL_BAD_STRUCTURE;
933 }
934
935 ctx = (gsskrb5_ctx) *context_handle;
936
937 HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
938
939 again:
940 switch (ctx->state) {
941 case INITIATOR_START:
942 ret = init_auth(minor_status,
943 cred,
944 ctx,
945 context,
946 target_name,
947 mech_type,
948 req_flags,
949 time_req,
950 input_token,
951 actual_mech_type,
952 output_token,
953 ret_flags,
954 time_rec);
955 if (ret != GSS_S_COMPLETE)
956 break;
957 /* FALL THOUGH */
958 case INITIATOR_RESTART:
959 ret = init_auth_restart(minor_status,
960 cred,
961 ctx,
962 context,
963 req_flags,
964 input_chan_bindings,
965 input_token,
966 actual_mech_type,
967 output_token,
968 ret_flags,
969 time_rec);
970 break;
971 case INITIATOR_WAIT_FOR_MUTAL:
972 ret = repl_mutual(minor_status,
973 ctx,
974 context,
975 mech_type,
976 req_flags,
977 time_req,
978 input_chan_bindings,
979 input_token,
980 actual_mech_type,
981 output_token,
982 ret_flags,
983 time_rec);
984 if (ctx->state == INITIATOR_RESTART)
985 goto again;
986 break;
987 case INITIATOR_READY:
988 /*
989 * If we get there, the caller have called
990 * gss_init_sec_context() one time too many.
991 */
992 _gsskrb5_set_status(EINVAL, "init_sec_context "
993 "called one time too many");
994 *minor_status = EINVAL;
995 ret = GSS_S_BAD_STATUS;
996 break;
997 default:
998 _gsskrb5_set_status(EINVAL, "init_sec_context "
999 "invalid state %d for client",
1000 (int)ctx->state);
1001 *minor_status = EINVAL;
1002 ret = GSS_S_BAD_STATUS;
1003 break;
1004 }
1005 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
1006
1007 /* destroy context in case of error */
1008 if (GSS_ERROR(ret)) {
1009 OM_uint32 min2;
1010 _gsskrb5_delete_sec_context(&min2, context_handle, GSS_C_NO_BUFFER);
1011 }
1012
1013 return ret;
1014
1015 }
1016