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