1 /*-
2  * Copyright (c) 2005 Doug Rabson
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  *	$FreeBSD$
27  */
28 
29 #include <gssapi/gssapi.h>
30 #include <gssapi/gssapi_krb5.h>
31 
32 /* RCSID("$Id: gss_krb5.c 21889 2007-08-09 07:43:24Z lha $"); */
33 
34 #include <krb5.h>
35 #include <roken.h>
36 #include <der.h>
37 
38 OM_uint32
39 gss_krb5_copy_ccache(OM_uint32 *minor_status,
40 		     gss_cred_id_t cred,
41 		     krb5_ccache out)
42 {
43     gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
44     krb5_context context;
45     krb5_error_code kret;
46     krb5_ccache id;
47     OM_uint32 ret;
48     char *str;
49 
50     ret = gss_inquire_cred_by_oid(minor_status,
51 				  cred,
52 				  GSS_KRB5_COPY_CCACHE_X,
53 				  &data_set);
54     if (ret)
55 	return ret;
56 
57     if (data_set == GSS_C_NO_BUFFER_SET || data_set->count != 1) {
58 	gss_release_buffer_set(minor_status, &data_set);
59 	*minor_status = EINVAL;
60 	return GSS_S_FAILURE;
61     }
62 
63     kret = krb5_init_context(&context);
64     if (kret) {
65 	*minor_status = kret;
66 	gss_release_buffer_set(minor_status, &data_set);
67 	return GSS_S_FAILURE;
68     }
69 
70     kret = asprintf(&str, "%.*s", (int)data_set->elements[0].length,
71 		    (char *)data_set->elements[0].value);
72     gss_release_buffer_set(minor_status, &data_set);
73     if (kret == -1) {
74 	*minor_status = ENOMEM;
75 	return GSS_S_FAILURE;
76     }
77 
78     kret = krb5_cc_resolve(context, str, &id);
79     free(str);
80     if (kret) {
81 	*minor_status = kret;
82 	return GSS_S_FAILURE;
83     }
84 
85     kret = krb5_cc_copy_cache(context, id, out);
86     krb5_cc_close(context, id);
87     krb5_free_context(context);
88     if (kret) {
89 	*minor_status = kret;
90 	return GSS_S_FAILURE;
91     }
92 
93     return ret;
94 }
95 
96 OM_uint32
97 gss_krb5_import_cred(OM_uint32 *minor_status,
98 		     krb5_ccache id,
99 		     krb5_principal keytab_principal,
100 		     krb5_keytab keytab,
101 		     gss_cred_id_t *cred)
102 {
103     gss_buffer_desc buffer;
104     OM_uint32 major_status;
105     krb5_context context;
106     krb5_error_code ret;
107     krb5_storage *sp;
108     krb5_data data;
109     char *str;
110 
111     *cred = GSS_C_NO_CREDENTIAL;
112 
113     ret = krb5_init_context(&context);
114     if (ret) {
115 	*minor_status = ret;
116 	return GSS_S_FAILURE;
117     }
118 
119     sp = krb5_storage_emem();
120     if (sp == NULL) {
121 	*minor_status = ENOMEM;
122 	major_status = GSS_S_FAILURE;
123 	goto out;
124     }
125 
126     if (id) {
127 	ret = krb5_cc_get_full_name(context, id, &str);
128 	if (ret == 0) {
129 	    ret = krb5_store_string(sp, str);
130 	    free(str);
131 	}
132     } else
133 	ret = krb5_store_string(sp, "");
134     if (ret) {
135 	*minor_status = ret;
136 	major_status = GSS_S_FAILURE;
137 	goto out;
138     }
139 
140     if (keytab_principal) {
141 	ret = krb5_unparse_name(context, keytab_principal, &str);
142 	if (ret == 0) {
143 	    ret = krb5_store_string(sp, str);
144 	    free(str);
145 	}
146     } else
147 	krb5_store_string(sp, "");
148     if (ret) {
149 	*minor_status = ret;
150 	major_status = GSS_S_FAILURE;
151 	goto out;
152     }
153 
154 
155     if (keytab) {
156 	ret = krb5_kt_get_full_name(context, keytab, &str);
157 	if (ret == 0) {
158 	    ret = krb5_store_string(sp, str);
159 	    free(str);
160 	}
161     } else
162 	krb5_store_string(sp, "");
163     if (ret) {
164 	*minor_status = ret;
165 	major_status = GSS_S_FAILURE;
166 	goto out;
167     }
168 
169     ret = krb5_storage_to_data(sp, &data);
170     if (ret) {
171 	*minor_status = ret;
172 	major_status = GSS_S_FAILURE;
173 	goto out;
174     }
175 
176     buffer.value = data.data;
177     buffer.length = data.length;
178 
179     major_status = gss_set_cred_option(minor_status,
180 				       cred,
181 				       GSS_KRB5_IMPORT_CRED_X,
182 				       &buffer);
183     krb5_data_free(&data);
184 out:
185     if (sp)
186 	krb5_storage_free(sp);
187     krb5_free_context(context);
188     return major_status;
189 }
190 
191 OM_uint32
192 gsskrb5_register_acceptor_identity(const char *identity)
193 {
194 	gss_buffer_desc buffer;
195 	OM_uint32 junk;
196 
197 	buffer.value = rk_UNCONST(identity);
198 	buffer.length = strlen(identity);
199 
200 	gss_set_sec_context_option(&junk, NULL,
201 	    GSS_KRB5_REGISTER_ACCEPTOR_IDENTITY_X, &buffer);
202 
203 	return (GSS_S_COMPLETE);
204 }
205 
206 OM_uint32
207 gsskrb5_set_dns_canonicalize(int flag)
208 {
209 	gss_buffer_desc buffer;
210 	OM_uint32 junk;
211 	char b = (flag != 0);
212 
213 	buffer.value = &b;
214 	buffer.length = sizeof(b);
215 
216 	gss_set_sec_context_option(&junk, NULL,
217 	    GSS_KRB5_SET_DNS_CANONICALIZE_X, &buffer);
218 
219 	return (GSS_S_COMPLETE);
220 }
221 
222 
223 
224 static krb5_error_code
225 set_key(krb5_keyblock *keyblock, gss_krb5_lucid_key_t *key)
226 {
227     key->type = keyblock->keytype;
228     key->length = keyblock->keyvalue.length;
229     key->data = malloc(key->length);
230     if (key->data == NULL && key->length != 0)
231 	return ENOMEM;
232     memcpy(key->data, keyblock->keyvalue.data, key->length);
233     return 0;
234 }
235 
236 static void
237 free_key(gss_krb5_lucid_key_t *key)
238 {
239     memset(key->data, 0, key->length);
240     free(key->data);
241     memset(key, 0, sizeof(*key));
242 }
243 
244 OM_uint32
245 gss_krb5_export_lucid_sec_context(OM_uint32 *minor_status,
246 				  gss_ctx_id_t *context_handle,
247 				  OM_uint32 version,
248 				  void **rctx)
249 {
250     krb5_context context = NULL;
251     krb5_error_code ret;
252     gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
253     OM_uint32 major_status;
254     gss_krb5_lucid_context_v1_t *ctx = NULL;
255     krb5_storage *sp = NULL;
256     uint32_t num;
257 
258     if (context_handle == NULL
259 	|| *context_handle == GSS_C_NO_CONTEXT
260 	|| version != 1)
261     {
262 	ret = EINVAL;
263 	return GSS_S_FAILURE;
264     }
265 
266     major_status =
267 	gss_inquire_sec_context_by_oid (minor_status,
268 					*context_handle,
269 					GSS_KRB5_EXPORT_LUCID_CONTEXT_V1_X,
270 					&data_set);
271     if (major_status)
272 	return major_status;
273 
274     if (data_set == GSS_C_NO_BUFFER_SET || data_set->count != 1) {
275 	gss_release_buffer_set(minor_status, &data_set);
276 	*minor_status = EINVAL;
277 	return GSS_S_FAILURE;
278     }
279 
280     ret = krb5_init_context(&context);
281     if (ret)
282 	goto out;
283 
284     ctx = calloc(1, sizeof(*ctx));
285     if (ctx == NULL) {
286 	ret = ENOMEM;
287 	goto out;
288     }
289 
290     sp = krb5_storage_from_mem(data_set->elements[0].value,
291 			       data_set->elements[0].length);
292     if (sp == NULL) {
293 	ret = ENOMEM;
294 	goto out;
295     }
296 
297     ret = krb5_ret_uint32(sp, &num);
298     if (ret) goto out;
299     if (num != 1) {
300 	ret = EINVAL;
301 	goto out;
302     }
303     ctx->version = 1;
304     /* initiator */
305     ret = krb5_ret_uint32(sp, &ctx->initiate);
306     if (ret) goto out;
307     /* endtime */
308     ret = krb5_ret_uint32(sp, &ctx->endtime);
309     if (ret) goto out;
310     /* send_seq */
311     ret = krb5_ret_uint32(sp, &num);
312     if (ret) goto out;
313     ctx->send_seq = ((uint64_t)num) << 32;
314     ret = krb5_ret_uint32(sp, &num);
315     if (ret) goto out;
316     ctx->send_seq |= num;
317     /* recv_seq */
318     ret = krb5_ret_uint32(sp, &num);
319     if (ret) goto out;
320     ctx->recv_seq = ((uint64_t)num) << 32;
321     ret = krb5_ret_uint32(sp, &num);
322     if (ret) goto out;
323     ctx->recv_seq |= num;
324     /* protocol */
325     ret = krb5_ret_uint32(sp, &ctx->protocol);
326     if (ret) goto out;
327     if (ctx->protocol == 0) {
328 	krb5_keyblock key;
329 
330 	/* sign_alg */
331 	ret = krb5_ret_uint32(sp, &ctx->rfc1964_kd.sign_alg);
332 	if (ret) goto out;
333 	/* seal_alg */
334 	ret = krb5_ret_uint32(sp, &ctx->rfc1964_kd.seal_alg);
335 	if (ret) goto out;
336 	/* ctx_key */
337 	ret = krb5_ret_keyblock(sp, &key);
338 	if (ret) goto out;
339 	ret = set_key(&key, &ctx->rfc1964_kd.ctx_key);
340 	krb5_free_keyblock_contents(context, &key);
341 	if (ret) goto out;
342     } else if (ctx->protocol == 1) {
343 	krb5_keyblock key;
344 
345 	/* acceptor_subkey */
346 	ret = krb5_ret_uint32(sp, &ctx->cfx_kd.have_acceptor_subkey);
347 	if (ret) goto out;
348 	/* ctx_key */
349 	ret = krb5_ret_keyblock(sp, &key);
350 	if (ret) goto out;
351 	ret = set_key(&key, &ctx->cfx_kd.ctx_key);
352 	krb5_free_keyblock_contents(context, &key);
353 	if (ret) goto out;
354 	/* acceptor_subkey */
355 	if (ctx->cfx_kd.have_acceptor_subkey) {
356 	    ret = krb5_ret_keyblock(sp, &key);
357 	    if (ret) goto out;
358 	    ret = set_key(&key, &ctx->cfx_kd.acceptor_subkey);
359 	    krb5_free_keyblock_contents(context, &key);
360 	    if (ret) goto out;
361 	}
362     } else {
363 	ret = EINVAL;
364 	goto out;
365     }
366 
367     *rctx = ctx;
368 
369 out:
370     gss_release_buffer_set(minor_status, &data_set);
371     if (sp)
372 	krb5_storage_free(sp);
373     if (context)
374 	krb5_free_context(context);
375 
376     if (ret) {
377 	if (ctx)
378 	    gss_krb5_free_lucid_sec_context(NULL, ctx);
379 
380 	*minor_status = ret;
381 	return GSS_S_FAILURE;
382     }
383     *minor_status = 0;
384     return GSS_S_COMPLETE;
385 }
386 
387 OM_uint32
388 gss_krb5_free_lucid_sec_context(OM_uint32 *minor_status, void *c)
389 {
390     gss_krb5_lucid_context_v1_t *ctx = c;
391 
392     if (ctx->version != 1) {
393 	if (minor_status)
394 	    *minor_status = 0;
395 	return GSS_S_FAILURE;
396     }
397 
398     if (ctx->protocol == 0) {
399 	free_key(&ctx->rfc1964_kd.ctx_key);
400     } else if (ctx->protocol == 1) {
401 	free_key(&ctx->cfx_kd.ctx_key);
402 	if (ctx->cfx_kd.have_acceptor_subkey)
403 	    free_key(&ctx->cfx_kd.acceptor_subkey);
404     }
405     free(ctx);
406     if (minor_status)
407 	*minor_status = 0;
408     return GSS_S_COMPLETE;
409 }
410 
411 /*
412  *
413  */
414 
415 OM_uint32
416 gss_krb5_set_allowable_enctypes(OM_uint32 *minor_status,
417 				gss_cred_id_t cred,
418 				OM_uint32 num_enctypes,
419 				int32_t *enctypes)
420 {
421     krb5_error_code ret;
422     OM_uint32 maj_status;
423     gss_buffer_desc buffer;
424     krb5_storage *sp;
425     krb5_data data;
426     int i;
427 
428     sp = krb5_storage_emem();
429     if (sp == NULL) {
430 	*minor_status = ENOMEM;
431 	maj_status = GSS_S_FAILURE;
432 	goto out;
433     }
434 
435     for (i = 0; i < num_enctypes; i++) {
436 	ret = krb5_store_int32(sp, enctypes[i]);
437 	if (ret) {
438 	    *minor_status = ret;
439 	    maj_status = GSS_S_FAILURE;
440 	    goto out;
441 	}
442     }
443 
444     ret = krb5_storage_to_data(sp, &data);
445     if (ret) {
446 	*minor_status = ret;
447 	maj_status = GSS_S_FAILURE;
448 	goto out;
449     }
450 
451     buffer.value = data.data;
452     buffer.length = data.length;
453 
454     maj_status = gss_set_cred_option(minor_status,
455 				     &cred,
456 				     GSS_KRB5_SET_ALLOWABLE_ENCTYPES_X,
457 				     &buffer);
458     krb5_data_free(&data);
459 out:
460     if (sp)
461 	krb5_storage_free(sp);
462     return maj_status;
463 }
464 
465 /*
466  *
467  */
468 
469 OM_uint32
470 gsskrb5_set_send_to_kdc(struct gsskrb5_send_to_kdc *c)
471 {
472     gss_buffer_desc buffer;
473     OM_uint32 junk;
474 
475     if (c) {
476 	buffer.value = c;
477 	buffer.length = sizeof(*c);
478     } else {
479 	buffer.value = NULL;
480 	buffer.length = 0;
481     }
482 
483     gss_set_sec_context_option(&junk, NULL,
484 	    GSS_KRB5_SEND_TO_KDC_X, &buffer);
485 
486     return (GSS_S_COMPLETE);
487 }
488 
489 /*
490  *
491  */
492 
493 OM_uint32
494 gss_krb5_ccache_name(OM_uint32 *minor_status,
495 		     const char *name,
496 		     const char **out_name)
497 {
498     gss_buffer_desc buffer;
499     OM_uint32 junk;
500 
501     if (out_name)
502 	*out_name = NULL;
503 
504     buffer.value = rk_UNCONST(name);
505     buffer.length = strlen(name);
506 
507     gss_set_sec_context_option(&junk, NULL,
508 	    GSS_KRB5_CCACHE_NAME_X, &buffer);
509 
510     return (GSS_S_COMPLETE);
511 }
512 
513 
514 /*
515  *
516  */
517 
518 OM_uint32
519 gsskrb5_extract_authtime_from_sec_context(OM_uint32 *minor_status,
520 					  gss_ctx_id_t context_handle,
521 					  time_t *authtime)
522 {
523     gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
524     OM_uint32 maj_stat;
525 
526     if (context_handle == GSS_C_NO_CONTEXT) {
527 	*minor_status = EINVAL;
528 	return GSS_S_FAILURE;
529     }
530 
531     maj_stat =
532 	gss_inquire_sec_context_by_oid (minor_status,
533 					context_handle,
534 					GSS_KRB5_GET_AUTHTIME_X,
535 					&data_set);
536     if (maj_stat)
537 	return maj_stat;
538 
539     if (data_set == GSS_C_NO_BUFFER_SET) {
540 	gss_release_buffer_set(minor_status, &data_set);
541 	*minor_status = EINVAL;
542 	return GSS_S_FAILURE;
543     }
544 
545     if (data_set->count != 1) {
546 	gss_release_buffer_set(minor_status, &data_set);
547 	*minor_status = EINVAL;
548 	return GSS_S_FAILURE;
549     }
550 
551     if (data_set->elements[0].length != 4) {
552 	gss_release_buffer_set(minor_status, &data_set);
553 	*minor_status = EINVAL;
554 	return GSS_S_FAILURE;
555     }
556 
557     {
558 	unsigned char *buf = data_set->elements[0].value;
559 	*authtime = (buf[3] <<24) | (buf[2] << 16) |
560 	    (buf[1] << 8) | (buf[0] << 0);
561     }
562 
563     gss_release_buffer_set(minor_status, &data_set);
564 
565     *minor_status = 0;
566     return GSS_S_COMPLETE;
567 }
568 
569 /*
570  *
571  */
572 
573 OM_uint32
574 gsskrb5_extract_authz_data_from_sec_context(OM_uint32 *minor_status,
575 					    gss_ctx_id_t context_handle,
576 					    int ad_type,
577 					    gss_buffer_t ad_data)
578 {
579     gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
580     OM_uint32 maj_stat;
581     gss_OID_desc oid_flat;
582     heim_oid baseoid, oid;
583     size_t size;
584 
585     if (context_handle == GSS_C_NO_CONTEXT) {
586 	*minor_status = EINVAL;
587 	return GSS_S_FAILURE;
588     }
589 
590     /* All this to append an integer to an oid... */
591 
592     if (der_get_oid(GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X->elements,
593 		    GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X->length,
594 		    &baseoid, NULL) != 0) {
595 	*minor_status = EINVAL;
596 	return GSS_S_FAILURE;
597     }
598 
599     oid.length = baseoid.length + 1;
600     oid.components = calloc(oid.length, sizeof(*oid.components));
601     if (oid.components == NULL) {
602 	der_free_oid(&baseoid);
603 
604 	*minor_status = ENOMEM;
605 	return GSS_S_FAILURE;
606     }
607 
608     memcpy(oid.components, baseoid.components,
609 	   baseoid.length * sizeof(*baseoid.components));
610 
611     der_free_oid(&baseoid);
612 
613     oid.components[oid.length - 1] = ad_type;
614 
615     oid_flat.length = der_length_oid(&oid);
616     oid_flat.elements = malloc(oid_flat.length);
617     if (oid_flat.elements == NULL) {
618 	free(oid.components);
619 	*minor_status = ENOMEM;
620 	return GSS_S_FAILURE;
621     }
622 
623     if (der_put_oid((unsigned char *)oid_flat.elements + oid_flat.length - 1,
624 		    oid_flat.length, &oid, &size) != 0) {
625 	free(oid.components);
626 	free(oid_flat.elements);
627 	*minor_status = EINVAL;
628 	return GSS_S_FAILURE;
629     }
630     if (oid_flat.length != size)
631 	abort();
632 
633     free(oid.components);
634 
635     /* FINALLY, we have the OID */
636 
637     maj_stat = gss_inquire_sec_context_by_oid (minor_status,
638 					       context_handle,
639 					       &oid_flat,
640 					       &data_set);
641 
642     free(oid_flat.elements);
643 
644     if (maj_stat)
645 	return maj_stat;
646 
647     if (data_set == GSS_C_NO_BUFFER_SET || data_set->count != 1) {
648 	gss_release_buffer_set(minor_status, &data_set);
649 	*minor_status = EINVAL;
650 	return GSS_S_FAILURE;
651     }
652 
653     ad_data->value = malloc(data_set->elements[0].length);
654     if (ad_data->value == NULL) {
655 	gss_release_buffer_set(minor_status, &data_set);
656 	*minor_status = ENOMEM;
657 	return GSS_S_FAILURE;
658     }
659 
660     ad_data->length = data_set->elements[0].length;
661     memcpy(ad_data->value, data_set->elements[0].value, ad_data->length);
662     gss_release_buffer_set(minor_status, &data_set);
663 
664     *minor_status = 0;
665     return GSS_S_COMPLETE;
666 }
667 
668 /*
669  *
670  */
671 
672 static OM_uint32
673 gsskrb5_extract_key(OM_uint32 *minor_status,
674 		    gss_ctx_id_t context_handle,
675 		    const gss_OID oid,
676 		    krb5_keyblock **keyblock)
677 {
678     krb5_error_code ret;
679     gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
680     OM_uint32 major_status;
681     krb5_context context = NULL;
682     krb5_storage *sp = NULL;
683 
684     if (context_handle == GSS_C_NO_CONTEXT) {
685 	ret = EINVAL;
686 	return GSS_S_FAILURE;
687     }
688 
689     ret = krb5_init_context(&context);
690     if(ret) {
691 	*minor_status = ret;
692 	return GSS_S_FAILURE;
693     }
694 
695     major_status =
696 	gss_inquire_sec_context_by_oid (minor_status,
697 					context_handle,
698 					oid,
699 					&data_set);
700     if (major_status)
701 	return major_status;
702 
703     if (data_set == GSS_C_NO_BUFFER_SET || data_set->count != 1) {
704 	gss_release_buffer_set(minor_status, &data_set);
705 	*minor_status = EINVAL;
706 	return GSS_S_FAILURE;
707     }
708 
709     sp = krb5_storage_from_mem(data_set->elements[0].value,
710 			       data_set->elements[0].length);
711     if (sp == NULL) {
712 	ret = ENOMEM;
713 	goto out;
714     }
715 
716     *keyblock = calloc(1, sizeof(**keyblock));
717     if (keyblock == NULL) {
718 	ret = ENOMEM;
719 	goto out;
720     }
721 
722     ret = krb5_ret_keyblock(sp, *keyblock);
723 
724 out:
725     gss_release_buffer_set(minor_status, &data_set);
726     if (sp)
727 	krb5_storage_free(sp);
728     if (ret && keyblock) {
729 	krb5_free_keyblock(context, *keyblock);
730 	*keyblock = NULL;
731     }
732     if (context)
733 	krb5_free_context(context);
734 
735     *minor_status = ret;
736     if (ret)
737 	return GSS_S_FAILURE;
738 
739     return GSS_S_COMPLETE;
740 }
741 
742 /*
743  *
744  */
745 
746 OM_uint32
747 gsskrb5_extract_service_keyblock(OM_uint32 *minor_status,
748 				 gss_ctx_id_t context_handle,
749 				 krb5_keyblock **keyblock)
750 {
751     return gsskrb5_extract_key(minor_status,
752 			       context_handle,
753 			       GSS_KRB5_GET_SERVICE_KEYBLOCK_X,
754 			       keyblock);
755 }
756 
757 OM_uint32
758 gsskrb5_get_initiator_subkey(OM_uint32 *minor_status,
759 			     gss_ctx_id_t context_handle,
760 			     krb5_keyblock **keyblock)
761 {
762     return gsskrb5_extract_key(minor_status,
763 			       context_handle,
764 			       GSS_KRB5_GET_INITIATOR_SUBKEY_X,
765 			       keyblock);
766 }
767 
768 OM_uint32
769 gsskrb5_get_subkey(OM_uint32 *minor_status,
770 		   gss_ctx_id_t context_handle,
771 		   krb5_keyblock **keyblock)
772 {
773     return gsskrb5_extract_key(minor_status,
774 			       context_handle,
775 			       GSS_KRB5_GET_SUBKEY_X,
776 			       keyblock);
777 }
778 
779 OM_uint32
780 gsskrb5_set_default_realm(const char *realm)
781 {
782 	gss_buffer_desc buffer;
783 	OM_uint32 junk;
784 
785 	buffer.value = rk_UNCONST(realm);
786 	buffer.length = strlen(realm);
787 
788 	gss_set_sec_context_option(&junk, NULL,
789 	    GSS_KRB5_SET_DEFAULT_REALM_X, &buffer);
790 
791 	return (GSS_S_COMPLETE);
792 }
793 
794 OM_uint32
795 gss_krb5_get_tkt_flags(OM_uint32 *minor_status,
796 		       gss_ctx_id_t context_handle,
797 		       OM_uint32 *tkt_flags)
798 {
799 
800     OM_uint32 major_status;
801     gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
802 
803     if (context_handle == GSS_C_NO_CONTEXT) {
804 	*minor_status = EINVAL;
805 	return GSS_S_FAILURE;
806     }
807 
808     major_status =
809 	gss_inquire_sec_context_by_oid (minor_status,
810 					context_handle,
811 					GSS_KRB5_GET_TKT_FLAGS_X,
812 					&data_set);
813     if (major_status)
814 	return major_status;
815 
816     if (data_set == GSS_C_NO_BUFFER_SET ||
817 	data_set->count != 1 ||
818 	data_set->elements[0].length < 4) {
819 	gss_release_buffer_set(minor_status, &data_set);
820 	*minor_status = EINVAL;
821 	return GSS_S_FAILURE;
822     }
823 
824     {
825 	const u_char *p = data_set->elements[0].value;
826 	*tkt_flags = (p[0] << 0) | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
827     }
828 
829     gss_release_buffer_set(minor_status, &data_set);
830     return GSS_S_COMPLETE;
831 }
832 
833