1 /* $NetBSD: accept_sec_context.c,v 1.3 2023/06/19 21:41:43 christos Exp $ */
2
3 /*
4 * Copyright (c) 1997 - 2006 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 HEIMDAL_MUTEX gssapi_keytab_mutex = HEIMDAL_MUTEX_INITIALIZER;
39 krb5_keytab _gsskrb5_keytab;
40
41 static krb5_error_code
validate_keytab(krb5_context context,const char * name,krb5_keytab * id)42 validate_keytab(krb5_context context, const char *name, krb5_keytab *id)
43 {
44 krb5_error_code ret;
45
46 ret = krb5_kt_resolve(context, name, id);
47 if (ret)
48 return ret;
49
50 ret = krb5_kt_have_content(context, *id);
51 if (ret) {
52 krb5_kt_close(context, *id);
53 *id = NULL;
54 }
55
56 return ret;
57 }
58
59 OM_uint32
_gsskrb5_register_acceptor_identity(OM_uint32 * min_stat,const char * identity)60 _gsskrb5_register_acceptor_identity(OM_uint32 *min_stat, const char *identity)
61 {
62 krb5_context context;
63 krb5_error_code ret;
64
65 *min_stat = 0;
66
67 ret = _gsskrb5_init(&context);
68 if(ret)
69 return GSS_S_FAILURE;
70
71 HEIMDAL_MUTEX_lock(&gssapi_keytab_mutex);
72
73 if(_gsskrb5_keytab != NULL) {
74 krb5_kt_close(context, _gsskrb5_keytab);
75 _gsskrb5_keytab = NULL;
76 }
77 if (identity == NULL) {
78 ret = krb5_kt_default(context, &_gsskrb5_keytab);
79 } else {
80 /*
81 * First check if we can the keytab as is and if it has content...
82 */
83 ret = validate_keytab(context, identity, &_gsskrb5_keytab);
84 /*
85 * if it doesn't, lets prepend FILE: and try again
86 */
87 if (ret) {
88 char *p = NULL;
89 ret = asprintf(&p, "FILE:%s", identity);
90 if(ret < 0 || p == NULL) {
91 HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex);
92 return GSS_S_FAILURE;
93 }
94 ret = validate_keytab(context, p, &_gsskrb5_keytab);
95 free(p);
96 }
97 }
98 HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex);
99 if(ret) {
100 *min_stat = ret;
101 return GSS_S_FAILURE;
102 }
103 return GSS_S_COMPLETE;
104 }
105
106 void
_gsskrb5i_is_cfx(krb5_context context,gsskrb5_ctx ctx,int acceptor)107 _gsskrb5i_is_cfx(krb5_context context, gsskrb5_ctx ctx, int acceptor)
108 {
109 krb5_keyblock *key;
110
111 if (acceptor) {
112 if (ctx->auth_context->local_subkey)
113 key = ctx->auth_context->local_subkey;
114 else
115 key = ctx->auth_context->remote_subkey;
116 } else {
117 if (ctx->auth_context->remote_subkey)
118 key = ctx->auth_context->remote_subkey;
119 else
120 key = ctx->auth_context->local_subkey;
121 }
122 if (key == NULL)
123 key = ctx->auth_context->keyblock;
124
125 if (key == NULL)
126 return;
127
128 switch (key->keytype) {
129 case ETYPE_DES_CBC_CRC:
130 case ETYPE_DES_CBC_MD4:
131 case ETYPE_DES_CBC_MD5:
132 case ETYPE_DES3_CBC_MD5:
133 case ETYPE_OLD_DES3_CBC_SHA1:
134 case ETYPE_DES3_CBC_SHA1:
135 case ETYPE_ARCFOUR_HMAC_MD5:
136 case ETYPE_ARCFOUR_HMAC_MD5_56:
137 break;
138 default :
139 ctx->more_flags |= IS_CFX;
140
141 if ((acceptor && ctx->auth_context->local_subkey) ||
142 (!acceptor && ctx->auth_context->remote_subkey))
143 ctx->more_flags |= ACCEPTOR_SUBKEY;
144 break;
145 }
146 if (ctx->crypto)
147 krb5_crypto_destroy(context, ctx->crypto);
148 /* XXX We really shouldn't ignore this; will come back to this */
149 (void) krb5_crypto_init(context, key, 0, &ctx->crypto);
150 }
151
152
153 static OM_uint32
gsskrb5_accept_delegated_token(OM_uint32 * minor_status,gsskrb5_ctx ctx,krb5_context context,gss_cred_id_t * delegated_cred_handle)154 gsskrb5_accept_delegated_token
155 (OM_uint32 * minor_status,
156 gsskrb5_ctx ctx,
157 krb5_context context,
158 gss_cred_id_t * delegated_cred_handle
159 )
160 {
161 krb5_ccache ccache = NULL;
162 krb5_error_code kret;
163 int32_t ac_flags, ret = GSS_S_COMPLETE;
164
165 *minor_status = 0;
166
167 /* XXX Create a new delegated_cred_handle? */
168 if (delegated_cred_handle == NULL) {
169 ret = GSS_S_COMPLETE;
170 goto out;
171 }
172
173 *delegated_cred_handle = NULL;
174 kret = krb5_cc_new_unique (context, krb5_cc_type_memory,
175 NULL, &ccache);
176 if (kret) {
177 ctx->flags &= ~GSS_C_DELEG_FLAG;
178 goto out;
179 }
180
181 kret = krb5_cc_initialize(context, ccache, ctx->source);
182 if (kret) {
183 ctx->flags &= ~GSS_C_DELEG_FLAG;
184 goto out;
185 }
186
187 krb5_auth_con_removeflags(context,
188 ctx->auth_context,
189 KRB5_AUTH_CONTEXT_DO_TIME,
190 &ac_flags);
191 kret = krb5_rd_cred2(context,
192 ctx->auth_context,
193 ccache,
194 &ctx->fwd_data);
195 krb5_auth_con_setflags(context,
196 ctx->auth_context,
197 ac_flags);
198 if (kret) {
199 ctx->flags &= ~GSS_C_DELEG_FLAG;
200 ret = GSS_S_FAILURE;
201 *minor_status = kret;
202 goto out;
203 }
204
205 if (delegated_cred_handle) {
206 gsskrb5_cred handle;
207
208 ret = _gsskrb5_krb5_import_cred(minor_status,
209 ccache,
210 NULL,
211 NULL,
212 delegated_cred_handle);
213 if (ret != GSS_S_COMPLETE)
214 goto out;
215
216 handle = (gsskrb5_cred) *delegated_cred_handle;
217
218 handle->cred_flags |= GSS_CF_DESTROY_CRED_ON_RELEASE;
219 krb5_cc_close(context, ccache);
220 ccache = NULL;
221 }
222
223 out:
224 if (ccache) {
225 /* Don't destroy the default cred cache */
226 if (delegated_cred_handle == NULL)
227 krb5_cc_close(context, ccache);
228 else
229 krb5_cc_destroy(context, ccache);
230 }
231 return ret;
232 }
233
234 static OM_uint32
gsskrb5_acceptor_ready(OM_uint32 * minor_status,gsskrb5_ctx ctx,krb5_context context,gss_cred_id_t * delegated_cred_handle)235 gsskrb5_acceptor_ready(OM_uint32 * minor_status,
236 gsskrb5_ctx ctx,
237 krb5_context context,
238 gss_cred_id_t *delegated_cred_handle)
239 {
240 OM_uint32 ret;
241 int32_t seq_number;
242 int is_cfx = 0;
243
244 krb5_auth_con_getremoteseqnumber (context,
245 ctx->auth_context,
246 &seq_number);
247
248 _gsskrb5i_is_cfx(context, ctx, 1);
249 is_cfx = (ctx->more_flags & IS_CFX);
250
251 ret = _gssapi_msg_order_create(minor_status,
252 &ctx->order,
253 _gssapi_msg_order_f(ctx->flags),
254 seq_number, 0, is_cfx);
255 if (ret)
256 return ret;
257
258 /*
259 * If requested, set local sequence num to remote sequence if this
260 * isn't a mutual authentication context
261 */
262 if (!(ctx->flags & GSS_C_MUTUAL_FLAG) && _gssapi_msg_order_f(ctx->flags)) {
263 krb5_auth_con_setlocalseqnumber(context,
264 ctx->auth_context,
265 seq_number);
266 }
267
268 /*
269 * We should handle the delegation ticket, in case it's there
270 */
271 if (ctx->fwd_data.length > 0 && (ctx->flags & GSS_C_DELEG_FLAG)) {
272 ret = gsskrb5_accept_delegated_token(minor_status,
273 ctx,
274 context,
275 delegated_cred_handle);
276 if (ret != GSS_S_COMPLETE)
277 return ret;
278 } else {
279 /* Well, looks like it wasn't there after all */
280 ctx->flags &= ~GSS_C_DELEG_FLAG;
281 }
282
283 ctx->state = ACCEPTOR_READY;
284 ctx->more_flags |= OPEN;
285
286 return GSS_S_COMPLETE;
287 }
288
289 static OM_uint32
send_error_token(OM_uint32 * minor_status,krb5_context context,krb5_error_code kret,krb5_principal server,krb5_data * indata,gss_buffer_t output_token)290 send_error_token(OM_uint32 *minor_status,
291 krb5_context context,
292 krb5_error_code kret,
293 krb5_principal server,
294 krb5_data *indata,
295 gss_buffer_t output_token)
296 {
297 krb5_principal ap_req_server = NULL;
298 krb5_error_code ret;
299 krb5_data outbuf;
300 /* this e_data value encodes KERB_AP_ERR_TYPE_SKEW_RECOVERY which
301 tells windows to try again with the corrected timestamp. See
302 [MS-KILE] 2.2.1 KERB-ERROR-DATA */
303 krb5_data e_data = { 7, rk_UNCONST("\x30\x05\xa1\x03\x02\x01\x02") };
304
305 /* build server from request if the acceptor had not selected one */
306 if (server == NULL) {
307 AP_REQ ap_req;
308
309 ret = krb5_decode_ap_req(context, indata, &ap_req);
310 if (ret) {
311 *minor_status = ret;
312 return GSS_S_FAILURE;
313 }
314 ret = _krb5_principalname2krb5_principal(context,
315 &ap_req_server,
316 ap_req.ticket.sname,
317 ap_req.ticket.realm);
318 free_AP_REQ(&ap_req);
319 if (ret) {
320 *minor_status = ret;
321 return GSS_S_FAILURE;
322 }
323 server = ap_req_server;
324 }
325
326 ret = krb5_mk_error(context, kret, NULL, &e_data, NULL,
327 server, NULL, NULL, &outbuf);
328 if (ap_req_server)
329 krb5_free_principal(context, ap_req_server);
330 if (ret) {
331 *minor_status = ret;
332 return GSS_S_FAILURE;
333 }
334
335 ret = _gsskrb5_encapsulate(minor_status,
336 &outbuf,
337 output_token,
338 "\x03\x00",
339 GSS_KRB5_MECHANISM);
340 krb5_data_free (&outbuf);
341 if (ret)
342 return ret;
343
344 *minor_status = 0;
345 return GSS_S_CONTINUE_NEEDED;
346 }
347
348
349 static OM_uint32
gsskrb5_acceptor_start(OM_uint32 * minor_status,gsskrb5_ctx ctx,krb5_context context,gss_const_cred_id_t acceptor_cred_handle,const gss_buffer_t input_token_buffer,const 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)350 gsskrb5_acceptor_start(OM_uint32 * minor_status,
351 gsskrb5_ctx ctx,
352 krb5_context context,
353 gss_const_cred_id_t acceptor_cred_handle,
354 const gss_buffer_t input_token_buffer,
355 const gss_channel_bindings_t input_chan_bindings,
356 gss_name_t * src_name,
357 gss_OID * mech_type,
358 gss_buffer_t output_token,
359 OM_uint32 * ret_flags,
360 OM_uint32 * time_rec,
361 gss_cred_id_t * delegated_cred_handle)
362 {
363 krb5_error_code kret;
364 OM_uint32 ret = GSS_S_COMPLETE;
365 krb5_data indata;
366 krb5_flags ap_options;
367 krb5_keytab keytab = NULL;
368 int is_cfx = 0;
369 int close_kt = 0;
370 const gsskrb5_cred acceptor_cred = (gsskrb5_cred)acceptor_cred_handle;
371
372 /*
373 * We may, or may not, have an escapsulation.
374 */
375 ret = _gsskrb5_decapsulate (minor_status,
376 input_token_buffer,
377 &indata,
378 "\x01\x00",
379 GSS_KRB5_MECHANISM);
380
381 if (ret) {
382 /* Assume that there is no OID wrapping. */
383 indata.length = input_token_buffer->length;
384 indata.data = input_token_buffer->value;
385 }
386
387 /*
388 * We need to get our keytab
389 */
390 if (acceptor_cred == NULL) {
391 HEIMDAL_MUTEX_lock(&gssapi_keytab_mutex);
392 if (_gsskrb5_keytab != NULL) {
393 char *name = NULL;
394 kret = krb5_kt_get_full_name(context, _gsskrb5_keytab, &name);
395 if (kret == 0) {
396 kret = krb5_kt_resolve(context, name, &keytab);
397 krb5_xfree(name);
398 }
399 if (kret == 0)
400 close_kt = 1;
401 else
402 keytab = NULL;
403 }
404 HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex);
405 } else if (acceptor_cred->keytab != NULL) {
406 keytab = acceptor_cred->keytab;
407 }
408
409 /*
410 * We need to check the ticket and create the AP-REP packet
411 */
412
413 {
414 krb5_rd_req_in_ctx in = NULL;
415 krb5_rd_req_out_ctx out = NULL;
416 krb5_principal server = NULL;
417
418 if (acceptor_cred)
419 server = acceptor_cred->principal;
420
421 kret = krb5_rd_req_in_ctx_alloc(context, &in);
422 if (kret == 0)
423 kret = krb5_rd_req_in_set_keytab(context, in, keytab);
424 if (kret) {
425 if (in)
426 krb5_rd_req_in_ctx_free(context, in);
427 if (close_kt)
428 krb5_kt_close(context, keytab);
429 *minor_status = kret;
430 return GSS_S_FAILURE;
431 }
432
433 kret = krb5_rd_req_ctx(context,
434 &ctx->auth_context,
435 &indata,
436 server,
437 in, &out);
438 krb5_rd_req_in_ctx_free(context, in);
439 if (close_kt)
440 krb5_kt_close(context, keytab);
441 if (kret == KRB5KRB_AP_ERR_SKEW || kret == KRB5KRB_AP_ERR_TKT_NYV) {
442 /*
443 * No reply in non-MUTUAL mode, but we don't know that its
444 * non-MUTUAL mode yet, thats inside the 8003 checksum, so
445 * lets only send the error token on clock skew, that
446 * limit when send error token for non-MUTUAL.
447 */
448 free_Authenticator(ctx->auth_context->authenticator);
449 return send_error_token(minor_status, context, kret,
450 server, &indata, output_token);
451 } else if (kret) {
452 *minor_status = kret;
453 return GSS_S_FAILURE;
454 }
455
456 /*
457 * we need to remember some data on the context_handle.
458 */
459 kret = krb5_rd_req_out_get_ap_req_options(context, out,
460 &ap_options);
461 if (kret == 0)
462 kret = krb5_rd_req_out_get_ticket(context, out,
463 &ctx->ticket);
464 if (kret == 0)
465 kret = krb5_rd_req_out_get_keyblock(context, out,
466 &ctx->service_keyblock);
467 ctx->endtime = ctx->ticket->ticket.endtime;
468
469 krb5_rd_req_out_ctx_free(context, out);
470 if (kret) {
471 ret = GSS_S_FAILURE;
472 *minor_status = kret;
473 return ret;
474 }
475 }
476
477
478 /*
479 * We need to copy the principal names to the context and the
480 * calling layer.
481 */
482 kret = krb5_copy_principal(context,
483 ctx->ticket->client,
484 &ctx->source);
485 if (kret) {
486 ret = GSS_S_FAILURE;
487 *minor_status = kret;
488 return ret;
489 }
490
491 kret = krb5_copy_principal(context,
492 ctx->ticket->server,
493 &ctx->target);
494 if (kret) {
495 ret = GSS_S_FAILURE;
496 *minor_status = kret;
497 return ret;
498 }
499
500 /*
501 * We need to setup some compat stuff, this assumes that
502 * context_handle->target is already set.
503 */
504 ret = _gss_DES3_get_mic_compat(minor_status, ctx, context);
505 if (ret)
506 return ret;
507
508 if (src_name != NULL) {
509 kret = krb5_copy_principal (context,
510 ctx->ticket->client,
511 (gsskrb5_name*)src_name);
512 if (kret) {
513 ret = GSS_S_FAILURE;
514 *minor_status = kret;
515 return ret;
516 }
517 }
518
519 /*
520 * We need to get the flags out of the 8003 checksum.
521 */
522
523 {
524 krb5_authenticator authenticator;
525
526 kret = krb5_auth_con_getauthenticator(context,
527 ctx->auth_context,
528 &authenticator);
529 if(kret) {
530 ret = GSS_S_FAILURE;
531 *minor_status = kret;
532 return ret;
533 }
534
535 if (authenticator->cksum != NULL
536 && authenticator->cksum->cksumtype == CKSUMTYPE_GSSAPI) {
537 ret = _gsskrb5_verify_8003_checksum(minor_status,
538 input_chan_bindings,
539 authenticator->cksum,
540 &ctx->flags,
541 &ctx->fwd_data);
542
543 if (ret) {
544 krb5_free_authenticator(context, &authenticator);
545 return ret;
546 }
547 } else {
548 if (authenticator->cksum != NULL) {
549 krb5_crypto crypto;
550
551 kret = krb5_crypto_init(context,
552 ctx->auth_context->keyblock,
553 0, &crypto);
554 if (kret) {
555 krb5_free_authenticator(context, &authenticator);
556 ret = GSS_S_FAILURE;
557 *minor_status = kret;
558 return ret;
559 }
560
561 /*
562 * Windows accepts Samba3's use of a kerberos, rather than
563 * GSSAPI checksum here
564 */
565
566 kret = krb5_verify_checksum(context,
567 crypto, KRB5_KU_AP_REQ_AUTH_CKSUM, NULL, 0,
568 authenticator->cksum);
569 krb5_crypto_destroy(context, crypto);
570
571 if (kret) {
572 krb5_free_authenticator(context, &authenticator);
573 ret = GSS_S_BAD_SIG;
574 *minor_status = kret;
575 return ret;
576 }
577 }
578
579 /*
580 * If there is no checksum or a kerberos checksum (which Windows
581 * and Samba accept), we use the ap_options to guess the mutual
582 * flag.
583 */
584
585 ctx->flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;
586 if (ap_options & AP_OPTS_MUTUAL_REQUIRED)
587 ctx->flags |= GSS_C_MUTUAL_FLAG;
588 }
589 krb5_free_authenticator(context, &authenticator);
590 }
591
592 if(ctx->flags & GSS_C_MUTUAL_FLAG) {
593 krb5_data outbuf;
594 int use_subkey = 0;
595
596 _gsskrb5i_is_cfx(context, ctx, 1);
597 is_cfx = (ctx->more_flags & IS_CFX);
598
599 if (is_cfx || (ap_options & AP_OPTS_USE_SUBKEY)) {
600 use_subkey = 1;
601 } else {
602 krb5_keyblock *rkey;
603
604 /*
605 * If there is a initiator subkey, copy that to acceptor
606 * subkey to match Windows behavior
607 */
608 kret = krb5_auth_con_getremotesubkey(context,
609 ctx->auth_context,
610 &rkey);
611 if (kret == 0) {
612 kret = krb5_auth_con_setlocalsubkey(context,
613 ctx->auth_context,
614 rkey);
615 if (kret == 0)
616 use_subkey = 1;
617 }
618 krb5_free_keyblock(context, rkey);
619 }
620 if (use_subkey) {
621 ctx->more_flags |= ACCEPTOR_SUBKEY;
622 krb5_auth_con_addflags(context, ctx->auth_context,
623 KRB5_AUTH_CONTEXT_USE_SUBKEY,
624 NULL);
625 }
626
627 kret = krb5_mk_rep(context,
628 ctx->auth_context,
629 &outbuf);
630 if (kret) {
631 *minor_status = kret;
632 return GSS_S_FAILURE;
633 }
634
635 if (IS_DCE_STYLE(ctx)) {
636 output_token->length = outbuf.length;
637 output_token->value = outbuf.data;
638 } else {
639 ret = _gsskrb5_encapsulate(minor_status,
640 &outbuf,
641 output_token,
642 "\x02\x00",
643 GSS_KRB5_MECHANISM);
644 krb5_data_free (&outbuf);
645 if (ret)
646 return ret;
647 }
648 }
649
650 ctx->flags |= GSS_C_TRANS_FLAG;
651
652 /* Remember the flags */
653
654 ctx->endtime = ctx->ticket->ticket.endtime;
655 ctx->more_flags |= OPEN;
656
657 if (mech_type)
658 *mech_type = GSS_KRB5_MECHANISM;
659
660 if (time_rec) {
661 ret = _gsskrb5_lifetime_left(minor_status,
662 context,
663 ctx->endtime,
664 time_rec);
665 if (ret) {
666 return ret;
667 }
668 }
669
670 /*
671 * When GSS_C_DCE_STYLE is in use, we need ask for a AP-REP from
672 * the client.
673 */
674 if (IS_DCE_STYLE(ctx)) {
675 /*
676 * Return flags to caller, but we haven't processed
677 * delgations yet
678 */
679 if (ret_flags)
680 *ret_flags = (ctx->flags & ~GSS_C_DELEG_FLAG);
681
682 ctx->state = ACCEPTOR_WAIT_FOR_DCESTYLE;
683 return GSS_S_CONTINUE_NEEDED;
684 }
685
686 ret = gsskrb5_acceptor_ready(minor_status, ctx, context,
687 delegated_cred_handle);
688
689 if (ret_flags)
690 *ret_flags = ctx->flags;
691
692 return ret;
693 }
694
695 static OM_uint32
acceptor_wait_for_dcestyle(OM_uint32 * minor_status,gsskrb5_ctx ctx,krb5_context context,gss_const_cred_id_t acceptor_cred_handle,const gss_buffer_t input_token_buffer,const 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)696 acceptor_wait_for_dcestyle(OM_uint32 * minor_status,
697 gsskrb5_ctx ctx,
698 krb5_context context,
699 gss_const_cred_id_t acceptor_cred_handle,
700 const gss_buffer_t input_token_buffer,
701 const gss_channel_bindings_t input_chan_bindings,
702 gss_name_t * src_name,
703 gss_OID * mech_type,
704 gss_buffer_t output_token,
705 OM_uint32 * ret_flags,
706 OM_uint32 * time_rec,
707 gss_cred_id_t * delegated_cred_handle)
708 {
709 OM_uint32 ret;
710 krb5_error_code kret;
711 krb5_data inbuf;
712 int32_t r_seq_number, l_seq_number;
713
714 /*
715 * We know it's GSS_C_DCE_STYLE so we don't need to decapsulate the AP_REP
716 */
717
718 inbuf.length = input_token_buffer->length;
719 inbuf.data = input_token_buffer->value;
720
721 /*
722 * We need to remeber the old remote seq_number, then check if the
723 * client has replied with our local seq_number, and then reset
724 * the remote seq_number to the old value
725 */
726 {
727 kret = krb5_auth_con_getlocalseqnumber(context,
728 ctx->auth_context,
729 &l_seq_number);
730 if (kret) {
731 *minor_status = kret;
732 return GSS_S_FAILURE;
733 }
734
735 kret = krb5_auth_con_getremoteseqnumber(context,
736 ctx->auth_context,
737 &r_seq_number);
738 if (kret) {
739 *minor_status = kret;
740 return GSS_S_FAILURE;
741 }
742
743 kret = krb5_auth_con_setremoteseqnumber(context,
744 ctx->auth_context,
745 l_seq_number);
746 if (kret) {
747 *minor_status = kret;
748 return GSS_S_FAILURE;
749 }
750 }
751
752 /*
753 * We need to verify the AP_REP, but we need to flag that this is
754 * DCE_STYLE, so don't check the timestamps this time, but put the
755 * flag DO_TIME back afterward.
756 */
757 {
758 krb5_ap_rep_enc_part *repl;
759 int32_t auth_flags;
760
761 krb5_auth_con_removeflags(context,
762 ctx->auth_context,
763 KRB5_AUTH_CONTEXT_DO_TIME,
764 &auth_flags);
765
766 kret = krb5_rd_rep(context, ctx->auth_context, &inbuf, &repl);
767 if (kret) {
768 *minor_status = kret;
769 return GSS_S_FAILURE;
770 }
771 krb5_free_ap_rep_enc_part(context, repl);
772 krb5_auth_con_setflags(context, ctx->auth_context, auth_flags);
773 }
774
775 /* We need to check the liftime */
776 {
777 OM_uint32 lifetime_rec;
778
779 ret = _gsskrb5_lifetime_left(minor_status,
780 context,
781 ctx->endtime,
782 &lifetime_rec);
783 if (ret) {
784 return ret;
785 }
786 if (lifetime_rec == 0) {
787 return GSS_S_CONTEXT_EXPIRED;
788 }
789
790 if (time_rec) *time_rec = lifetime_rec;
791 }
792
793 /* We need to give the caller the flags which are in use */
794 if (ret_flags) *ret_flags = ctx->flags;
795
796 if (src_name) {
797 kret = krb5_copy_principal(context,
798 ctx->source,
799 (gsskrb5_name*)src_name);
800 if (kret) {
801 *minor_status = kret;
802 return GSS_S_FAILURE;
803 }
804 }
805
806 /*
807 * After the krb5_rd_rep() the remote and local seq_number should
808 * be the same, because the client just replies the seq_number
809 * from our AP-REP in its AP-REP, but then the client uses the
810 * seq_number from its AP-REQ for GSS_wrap()
811 */
812 {
813 int32_t tmp_r_seq_number, tmp_l_seq_number;
814
815 kret = krb5_auth_con_getremoteseqnumber(context,
816 ctx->auth_context,
817 &tmp_r_seq_number);
818 if (kret) {
819 *minor_status = kret;
820 return GSS_S_FAILURE;
821 }
822
823 kret = krb5_auth_con_getlocalseqnumber(context,
824 ctx->auth_context,
825 &tmp_l_seq_number);
826 if (kret) {
827
828 *minor_status = kret;
829 return GSS_S_FAILURE;
830 }
831
832 /*
833 * Here we check if the client has responsed with our local seq_number,
834 */
835 if (tmp_r_seq_number != tmp_l_seq_number) {
836 return GSS_S_UNSEQ_TOKEN;
837 }
838 }
839
840 /*
841 * We need to reset the remote seq_number, because the client will use,
842 * the old one for the GSS_wrap() calls
843 */
844 {
845 kret = krb5_auth_con_setremoteseqnumber(context,
846 ctx->auth_context,
847 r_seq_number);
848 if (kret) {
849 *minor_status = kret;
850 return GSS_S_FAILURE;
851 }
852 }
853
854 return gsskrb5_acceptor_ready(minor_status, ctx, context,
855 delegated_cred_handle);
856 }
857
858
859 OM_uint32 GSSAPI_CALLCONV
_gsskrb5_accept_sec_context(OM_uint32 * minor_status,gss_ctx_id_t * context_handle,gss_const_cred_id_t acceptor_cred_handle,const gss_buffer_t input_token_buffer,const 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)860 _gsskrb5_accept_sec_context(OM_uint32 * minor_status,
861 gss_ctx_id_t * context_handle,
862 gss_const_cred_id_t acceptor_cred_handle,
863 const gss_buffer_t input_token_buffer,
864 const gss_channel_bindings_t input_chan_bindings,
865 gss_name_t * src_name,
866 gss_OID * mech_type,
867 gss_buffer_t output_token,
868 OM_uint32 * ret_flags,
869 OM_uint32 * time_rec,
870 gss_cred_id_t * delegated_cred_handle)
871 {
872 krb5_context context;
873 OM_uint32 ret;
874 gsskrb5_ctx ctx;
875
876 GSSAPI_KRB5_INIT(&context);
877
878 output_token->length = 0;
879 output_token->value = NULL;
880
881 if (src_name != NULL)
882 *src_name = NULL;
883 if (mech_type)
884 *mech_type = GSS_KRB5_MECHANISM;
885
886 if (*context_handle == GSS_C_NO_CONTEXT) {
887 ret = _gsskrb5_create_ctx(minor_status,
888 context_handle,
889 context,
890 input_chan_bindings,
891 ACCEPTOR_START);
892 if (ret)
893 return ret;
894 }
895
896 ctx = (gsskrb5_ctx)*context_handle;
897
898
899 /*
900 * TODO: check the channel_bindings
901 * (above just sets them to krb5 layer)
902 */
903
904 HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
905
906 switch (ctx->state) {
907 case ACCEPTOR_START:
908 ret = gsskrb5_acceptor_start(minor_status,
909 ctx,
910 context,
911 acceptor_cred_handle,
912 input_token_buffer,
913 input_chan_bindings,
914 src_name,
915 mech_type,
916 output_token,
917 ret_flags,
918 time_rec,
919 delegated_cred_handle);
920 break;
921 case ACCEPTOR_WAIT_FOR_DCESTYLE:
922 ret = acceptor_wait_for_dcestyle(minor_status,
923 ctx,
924 context,
925 acceptor_cred_handle,
926 input_token_buffer,
927 input_chan_bindings,
928 src_name,
929 mech_type,
930 output_token,
931 ret_flags,
932 time_rec,
933 delegated_cred_handle);
934 break;
935 case ACCEPTOR_READY:
936 /*
937 * If we get there, the caller have called
938 * gss_accept_sec_context() one time too many.
939 */
940 ret = GSS_S_BAD_STATUS;
941 break;
942 default:
943 /* TODO: is this correct here? --metze */
944 ret = GSS_S_BAD_STATUS;
945 break;
946 }
947
948 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
949
950 if (GSS_ERROR(ret)) {
951 OM_uint32 min2;
952 _gsskrb5_delete_sec_context(&min2, context_handle, GSS_C_NO_BUFFER);
953 }
954
955 return ret;
956 }
957