heimdal: import heimdal's trunk svn rev 23697 + lorikeet-heimdal patches
[kai/samba.git] / source4 / heimdal / lib / gssapi / mech / gss_krb5.c
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: src/lib/libgssapi/gss_krb5.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
27  */
28
29 #include "mech_locl.h"
30 RCSID("$Id$");
31
32 #include <krb5.h>
33 #include <roken.h>
34
35
36 OM_uint32 GSSAPI_LIB_FUNCTION
37 gss_krb5_copy_ccache(OM_uint32 *minor_status,
38                      gss_cred_id_t cred,
39                      krb5_ccache out)
40 {
41     gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
42     krb5_context context;
43     krb5_error_code kret;
44     krb5_ccache id;
45     OM_uint32 ret;
46     char *str;
47
48     ret = gss_inquire_cred_by_oid(minor_status,
49                                   cred,
50                                   GSS_KRB5_COPY_CCACHE_X,
51                                   &data_set);
52     if (ret)
53         return ret;
54
55     if (data_set == GSS_C_NO_BUFFER_SET || data_set->count < 1) {
56         gss_release_buffer_set(minor_status, &data_set);
57         *minor_status = EINVAL;
58         return GSS_S_FAILURE;
59     }
60
61     kret = krb5_init_context(&context);
62     if (kret) {
63         *minor_status = kret;
64         gss_release_buffer_set(minor_status, &data_set);
65         return GSS_S_FAILURE;
66     }
67
68     kret = asprintf(&str, "%.*s", (int)data_set->elements[0].length,
69                     (char *)data_set->elements[0].value);
70     gss_release_buffer_set(minor_status, &data_set);
71     if (kret == -1) {
72         *minor_status = ENOMEM;
73         return GSS_S_FAILURE;
74     }
75
76     kret = krb5_cc_resolve(context, str, &id);
77     free(str);
78     if (kret) {
79         *minor_status = kret;
80         return GSS_S_FAILURE;
81     }
82
83     kret = krb5_cc_copy_cache(context, id, out);
84     krb5_cc_close(context, id);
85     krb5_free_context(context);
86     if (kret) {
87         *minor_status = kret;
88         return GSS_S_FAILURE;
89     }
90
91     return ret;
92 }
93
94 OM_uint32 GSSAPI_LIB_FUNCTION
95 gss_krb5_import_cred(OM_uint32 *minor_status,
96                      krb5_ccache id,
97                      krb5_principal keytab_principal,
98                      krb5_keytab keytab,
99                      gss_cred_id_t *cred)
100 {
101     gss_buffer_desc buffer;
102     OM_uint32 major_status;
103     krb5_context context;
104     krb5_error_code ret;
105     krb5_storage *sp;
106     krb5_data data;
107     char *str;
108
109     *cred = GSS_C_NO_CREDENTIAL;
110
111     ret = krb5_init_context(&context);
112     if (ret) {
113         *minor_status = ret;
114         return GSS_S_FAILURE;
115     }
116
117     sp = krb5_storage_emem();
118     if (sp == NULL) {
119         *minor_status = ENOMEM;
120         major_status = GSS_S_FAILURE;
121         goto out;
122     }
123
124     if (id) {
125         ret = krb5_cc_get_full_name(context, id, &str);
126         if (ret == 0) {
127             ret = krb5_store_string(sp, str);
128             free(str);
129         }
130     } else
131         ret = krb5_store_string(sp, "");
132     if (ret) {
133         *minor_status = ret;
134         major_status = GSS_S_FAILURE;
135         goto out;
136     }
137
138     if (keytab_principal) {
139         ret = krb5_unparse_name(context, keytab_principal, &str);
140         if (ret == 0) {
141             ret = krb5_store_string(sp, str);
142             free(str);
143         }
144     } else
145         krb5_store_string(sp, "");
146     if (ret) {
147         *minor_status = ret;
148         major_status = GSS_S_FAILURE;
149         goto out;
150     }
151
152
153     if (keytab) {
154         ret = krb5_kt_get_full_name(context, keytab, &str);
155         if (ret == 0) {
156             ret = krb5_store_string(sp, str);
157             free(str);
158         }
159     } else
160         krb5_store_string(sp, "");
161     if (ret) {
162         *minor_status = ret;
163         major_status = GSS_S_FAILURE;
164         goto out;
165     }
166
167     ret = krb5_storage_to_data(sp, &data);
168     if (ret) {
169         *minor_status = ret;
170         major_status = GSS_S_FAILURE;
171         goto out;
172     }
173
174     buffer.value = data.data;
175     buffer.length = data.length;
176     
177     major_status = gss_set_cred_option(minor_status,
178                                        cred,
179                                        GSS_KRB5_IMPORT_CRED_X,
180                                        &buffer);
181     krb5_data_free(&data);
182 out:
183     if (sp)
184         krb5_storage_free(sp);
185     krb5_free_context(context);
186     return major_status;
187 }
188
189 OM_uint32 GSSAPI_LIB_FUNCTION
190 gsskrb5_register_acceptor_identity(const char *identity)
191 {
192         struct _gss_mech_switch *m;
193         gss_buffer_desc buffer;
194         OM_uint32 junk;
195
196         _gss_load_mech();
197
198         buffer.value = rk_UNCONST(identity);
199         buffer.length = strlen(identity);
200
201         SLIST_FOREACH(m, &_gss_mechs, gm_link) {
202                 if (m->gm_mech.gm_set_sec_context_option == NULL)
203                         continue;
204                 m->gm_mech.gm_set_sec_context_option(&junk, NULL,
205                     GSS_KRB5_REGISTER_ACCEPTOR_IDENTITY_X, &buffer);
206         }
207
208         return (GSS_S_COMPLETE);
209 }
210
211 OM_uint32 GSSAPI_LIB_FUNCTION
212 krb5_gss_register_acceptor_identity(const char *identity)
213 {
214     return gsskrb5_register_acceptor_identity(identity);
215 }
216
217
218 OM_uint32 GSSAPI_LIB_FUNCTION
219 gsskrb5_set_dns_canonicalize(int flag)
220 {
221         struct _gss_mech_switch *m;
222         gss_buffer_desc buffer;
223         OM_uint32 junk;
224         char b = (flag != 0);
225
226         _gss_load_mech();
227
228         buffer.value = &b;
229         buffer.length = sizeof(b);
230
231         SLIST_FOREACH(m, &_gss_mechs, gm_link) {
232                 if (m->gm_mech.gm_set_sec_context_option == NULL)
233                         continue;
234                 m->gm_mech.gm_set_sec_context_option(&junk, NULL,
235                     GSS_KRB5_SET_DNS_CANONICALIZE_X, &buffer);
236         }
237
238         return (GSS_S_COMPLETE);
239 }
240
241
242
243 static krb5_error_code
244 set_key(krb5_keyblock *keyblock, gss_krb5_lucid_key_t *key)
245 {
246     key->type = keyblock->keytype;
247     key->length = keyblock->keyvalue.length;
248     key->data = malloc(key->length);
249     if (key->data == NULL && key->length != 0)
250         return ENOMEM;
251     memcpy(key->data, keyblock->keyvalue.data, key->length);
252     return 0;
253 }
254
255 static void
256 free_key(gss_krb5_lucid_key_t *key)
257 {
258     memset(key->data, 0, key->length);
259     free(key->data);
260     memset(key, 0, sizeof(*key));
261 }
262
263 OM_uint32 GSSAPI_LIB_FUNCTION
264 gss_krb5_export_lucid_sec_context(OM_uint32 *minor_status,
265                                   gss_ctx_id_t *context_handle,
266                                   OM_uint32 version,
267                                   void **rctx)
268 {
269     krb5_context context = NULL;
270     krb5_error_code ret;
271     gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
272     OM_uint32 major_status;
273     gss_krb5_lucid_context_v1_t *ctx = NULL;
274     krb5_storage *sp = NULL;
275     uint32_t num;
276
277     if (context_handle == NULL
278         || *context_handle == GSS_C_NO_CONTEXT
279         || version != 1)
280     {
281         ret = EINVAL;
282         return GSS_S_FAILURE;
283     }
284     
285     major_status =
286         gss_inquire_sec_context_by_oid (minor_status,
287                                         *context_handle,
288                                         GSS_KRB5_EXPORT_LUCID_CONTEXT_V1_X,
289                                         &data_set);
290     if (major_status)
291         return major_status;
292     
293     if (data_set == GSS_C_NO_BUFFER_SET || data_set->count != 1) {
294         gss_release_buffer_set(minor_status, &data_set);
295         *minor_status = EINVAL;
296         return GSS_S_FAILURE;
297     }
298
299     ret = krb5_init_context(&context);
300     if (ret)
301         goto out;
302
303     ctx = calloc(1, sizeof(*ctx));
304     if (ctx == NULL) {
305         ret = ENOMEM;
306         goto out;
307     }
308
309     sp = krb5_storage_from_mem(data_set->elements[0].value,
310                                data_set->elements[0].length);
311     if (sp == NULL) {
312         ret = ENOMEM;
313         goto out;
314     }
315     
316     ret = krb5_ret_uint32(sp, &num);
317     if (ret) goto out;
318     if (num != 1) {
319         ret = EINVAL;
320         goto out;
321     }
322     ctx->version = 1;
323     /* initiator */
324     ret = krb5_ret_uint32(sp, &ctx->initiate);
325     if (ret) goto out;
326     /* endtime */
327     ret = krb5_ret_uint32(sp, &ctx->endtime);
328     if (ret) goto out;
329     /* send_seq */
330     ret = krb5_ret_uint32(sp, &num);
331     if (ret) goto out;
332     ctx->send_seq = ((uint64_t)num) << 32;
333     ret = krb5_ret_uint32(sp, &num);
334     if (ret) goto out;
335     ctx->send_seq |= num;
336     /* recv_seq */
337     ret = krb5_ret_uint32(sp, &num);
338     if (ret) goto out;
339     ctx->recv_seq = ((uint64_t)num) << 32;
340     ret = krb5_ret_uint32(sp, &num);
341     if (ret) goto out;
342     ctx->recv_seq |= num;
343     /* protocol */
344     ret = krb5_ret_uint32(sp, &ctx->protocol);
345     if (ret) goto out;
346     if (ctx->protocol == 0) {
347         krb5_keyblock key;
348
349         /* sign_alg */
350         ret = krb5_ret_uint32(sp, &ctx->rfc1964_kd.sign_alg);
351         if (ret) goto out;
352         /* seal_alg */
353         ret = krb5_ret_uint32(sp, &ctx->rfc1964_kd.seal_alg);
354         if (ret) goto out;
355         /* ctx_key */
356         ret = krb5_ret_keyblock(sp, &key);
357         if (ret) goto out;
358         ret = set_key(&key, &ctx->rfc1964_kd.ctx_key);
359         krb5_free_keyblock_contents(context, &key);
360         if (ret) goto out;
361     } else if (ctx->protocol == 1) {
362         krb5_keyblock key;
363
364         /* acceptor_subkey */
365         ret = krb5_ret_uint32(sp, &ctx->cfx_kd.have_acceptor_subkey);
366         if (ret) goto out;
367         /* ctx_key */
368         ret = krb5_ret_keyblock(sp, &key);
369         if (ret) goto out;
370         ret = set_key(&key, &ctx->cfx_kd.ctx_key);
371         krb5_free_keyblock_contents(context, &key);
372         if (ret) goto out;
373         /* acceptor_subkey */
374         if (ctx->cfx_kd.have_acceptor_subkey) {
375             ret = krb5_ret_keyblock(sp, &key);
376             if (ret) goto out;
377             ret = set_key(&key, &ctx->cfx_kd.acceptor_subkey);
378             krb5_free_keyblock_contents(context, &key);
379             if (ret) goto out;
380         }
381     } else {
382         ret = EINVAL;
383         goto out;
384     }
385
386     *rctx = ctx;
387
388 out:
389     gss_release_buffer_set(minor_status, &data_set);
390     if (sp)
391         krb5_storage_free(sp);
392     if (context)
393         krb5_free_context(context);
394
395     if (ret) {
396         if (ctx)
397             gss_krb5_free_lucid_sec_context(NULL, ctx);
398
399         *minor_status = ret;
400         return GSS_S_FAILURE;
401     }
402     *minor_status = 0;
403     return GSS_S_COMPLETE;
404 }
405
406 OM_uint32 GSSAPI_LIB_FUNCTION
407 gss_krb5_free_lucid_sec_context(OM_uint32 *minor_status, void *c)
408 {
409     gss_krb5_lucid_context_v1_t *ctx = c;
410
411     if (ctx->version != 1) {
412         if (minor_status)
413             *minor_status = 0;
414         return GSS_S_FAILURE;
415     }
416
417     if (ctx->protocol == 0) {
418         free_key(&ctx->rfc1964_kd.ctx_key);
419     } else if (ctx->protocol == 1) {
420         free_key(&ctx->cfx_kd.ctx_key);
421         if (ctx->cfx_kd.have_acceptor_subkey)
422             free_key(&ctx->cfx_kd.acceptor_subkey);
423     }
424     free(ctx);
425     if (minor_status)
426         *minor_status = 0;
427     return GSS_S_COMPLETE;
428 }
429
430 /*
431  *
432  */
433
434 OM_uint32 GSSAPI_LIB_FUNCTION
435 gss_krb5_set_allowable_enctypes(OM_uint32 *minor_status, 
436                                 gss_cred_id_t cred,
437                                 OM_uint32 num_enctypes,
438                                 int32_t *enctypes)
439 {
440     krb5_error_code ret;
441     OM_uint32 maj_status;
442     gss_buffer_desc buffer;
443     krb5_storage *sp;
444     krb5_data data;
445     int i;
446
447     sp = krb5_storage_emem();
448     if (sp == NULL) {
449         *minor_status = ENOMEM;
450         maj_status = GSS_S_FAILURE;
451         goto out;
452     }
453
454     for (i = 0; i < num_enctypes; i++) {
455         ret = krb5_store_int32(sp, enctypes[i]);
456         if (ret) {
457             *minor_status = ret;
458             maj_status = GSS_S_FAILURE;
459             goto out;
460         }
461     }
462
463     ret = krb5_storage_to_data(sp, &data);
464     if (ret) {
465         *minor_status = ret;
466         maj_status = GSS_S_FAILURE;
467         goto out;
468     }
469
470     buffer.value = data.data;
471     buffer.length = data.length;
472
473     maj_status = gss_set_cred_option(minor_status,
474                                      &cred,
475                                      GSS_KRB5_SET_ALLOWABLE_ENCTYPES_X,
476                                      &buffer);
477     krb5_data_free(&data);
478 out:
479     if (sp)
480         krb5_storage_free(sp);
481     return maj_status;
482 }
483
484 /*
485  *
486  */
487
488 OM_uint32 GSSAPI_LIB_FUNCTION
489 gsskrb5_set_send_to_kdc(struct gsskrb5_send_to_kdc *c)
490 {
491     struct _gss_mech_switch *m;
492     gss_buffer_desc buffer;
493     OM_uint32 junk;
494
495     _gss_load_mech();
496
497     if (c) {
498         buffer.value = c;
499         buffer.length = sizeof(*c);
500     } else {
501         buffer.value = NULL;
502         buffer.length = 0;
503     }
504
505     SLIST_FOREACH(m, &_gss_mechs, gm_link) {
506         if (m->gm_mech.gm_set_sec_context_option == NULL)
507             continue;
508         m->gm_mech.gm_set_sec_context_option(&junk, NULL,
509             GSS_KRB5_SEND_TO_KDC_X, &buffer);
510     }
511
512     return (GSS_S_COMPLETE);
513 }
514
515 /*
516  *
517  */
518
519 OM_uint32 GSSAPI_LIB_FUNCTION
520 gss_krb5_ccache_name(OM_uint32 *minor_status, 
521                      const char *name,
522                      const char **out_name)
523 {
524     struct _gss_mech_switch *m;
525     gss_buffer_desc buffer;
526     OM_uint32 junk;
527
528     _gss_load_mech();
529
530     if (out_name)
531         *out_name = NULL;
532
533     buffer.value = rk_UNCONST(name);
534     buffer.length = strlen(name);
535
536     SLIST_FOREACH(m, &_gss_mechs, gm_link) {
537         if (m->gm_mech.gm_set_sec_context_option == NULL)
538             continue;
539         m->gm_mech.gm_set_sec_context_option(&junk, NULL,
540             GSS_KRB5_CCACHE_NAME_X, &buffer);
541     }
542
543     return (GSS_S_COMPLETE);
544 }
545
546
547 /*
548  *
549  */
550
551 OM_uint32 GSSAPI_LIB_FUNCTION
552 gsskrb5_extract_authtime_from_sec_context(OM_uint32 *minor_status,
553                                           gss_ctx_id_t context_handle,
554                                           time_t *authtime)
555 {
556     gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
557     OM_uint32 maj_stat;
558
559     if (context_handle == GSS_C_NO_CONTEXT) {
560         *minor_status = EINVAL;
561         return GSS_S_FAILURE;
562     }
563     
564     maj_stat =
565         gss_inquire_sec_context_by_oid (minor_status,
566                                         context_handle,
567                                         GSS_KRB5_GET_AUTHTIME_X,
568                                         &data_set);
569     if (maj_stat)
570         return maj_stat;
571     
572     if (data_set == GSS_C_NO_BUFFER_SET) {
573         gss_release_buffer_set(minor_status, &data_set);
574         *minor_status = EINVAL;
575         return GSS_S_FAILURE;
576     }
577
578     if (data_set->count != 1) {
579         gss_release_buffer_set(minor_status, &data_set);
580         *minor_status = EINVAL;
581         return GSS_S_FAILURE;
582     }
583
584     if (data_set->elements[0].length != 4) {
585         gss_release_buffer_set(minor_status, &data_set);
586         *minor_status = EINVAL;
587         return GSS_S_FAILURE;
588     }
589
590     {
591         unsigned char *buf = data_set->elements[0].value;
592         *authtime = (buf[3] <<24) | (buf[2] << 16) | 
593             (buf[1] << 8) | (buf[0] << 0);
594     }
595
596     gss_release_buffer_set(minor_status, &data_set);
597
598     *minor_status = 0;
599     return GSS_S_COMPLETE;
600 }
601
602 /*
603  *
604  */
605
606 OM_uint32 GSSAPI_LIB_FUNCTION
607 gsskrb5_extract_authz_data_from_sec_context(OM_uint32 *minor_status,
608                                             gss_ctx_id_t context_handle,
609                                             int ad_type,
610                                             gss_buffer_t ad_data)
611 {
612     gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
613     OM_uint32 maj_stat;
614     gss_OID_desc oid_flat;
615     heim_oid baseoid, oid;
616     size_t size;
617
618     if (context_handle == GSS_C_NO_CONTEXT) {
619         *minor_status = EINVAL;
620         return GSS_S_FAILURE;
621     }
622
623     /* All this to append an integer to an oid... */
624
625     if (der_get_oid(GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X->elements,
626                     GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X->length,
627                     &baseoid, NULL) != 0) {
628         *minor_status = EINVAL;
629         return GSS_S_FAILURE;
630     }
631     
632     oid.length = baseoid.length + 1;
633     oid.components = calloc(oid.length, sizeof(*oid.components));
634     if (oid.components == NULL) {
635         der_free_oid(&baseoid);
636
637         *minor_status = ENOMEM;
638         return GSS_S_FAILURE;
639     }
640
641     memcpy(oid.components, baseoid.components, 
642            baseoid.length * sizeof(*baseoid.components));
643     
644     der_free_oid(&baseoid);
645
646     oid.components[oid.length - 1] = ad_type;
647
648     oid_flat.length = der_length_oid(&oid);
649     oid_flat.elements = malloc(oid_flat.length);
650     if (oid_flat.elements == NULL) {
651         free(oid.components);
652         *minor_status = ENOMEM;
653         return GSS_S_FAILURE;
654     }
655
656     if (der_put_oid((unsigned char *)oid_flat.elements + oid_flat.length - 1, 
657                     oid_flat.length, &oid, &size) != 0) {
658         free(oid.components);
659         free(oid_flat.elements);
660         *minor_status = EINVAL;
661         return GSS_S_FAILURE;
662     }
663     if (oid_flat.length != size)
664         abort();
665
666     free(oid.components);
667
668     /* FINALLY, we have the OID */
669
670     maj_stat = gss_inquire_sec_context_by_oid (minor_status,
671                                                context_handle,
672                                                &oid_flat,
673                                                &data_set);
674
675     free(oid_flat.elements);
676
677     if (maj_stat)
678         return maj_stat;
679     
680     if (data_set == GSS_C_NO_BUFFER_SET || data_set->count != 1) {
681         gss_release_buffer_set(minor_status, &data_set);
682         *minor_status = EINVAL;
683         return GSS_S_FAILURE;
684     }
685
686     ad_data->value = malloc(data_set->elements[0].length);
687     if (ad_data->value == NULL) {
688         gss_release_buffer_set(minor_status, &data_set);
689         *minor_status = ENOMEM;
690         return GSS_S_FAILURE;
691     }
692
693     ad_data->length = data_set->elements[0].length;
694     memcpy(ad_data->value, data_set->elements[0].value, ad_data->length);
695     gss_release_buffer_set(minor_status, &data_set);
696     
697     *minor_status = 0;
698     return GSS_S_COMPLETE;
699 }
700
701 /*
702  *
703  */
704
705 static OM_uint32
706 gsskrb5_extract_key(OM_uint32 *minor_status,
707                     gss_ctx_id_t context_handle,
708                     const gss_OID oid, 
709                     krb5_keyblock **keyblock)
710 {
711     krb5_error_code ret;
712     gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
713     OM_uint32 major_status;
714     krb5_context context = NULL;
715     krb5_storage *sp = NULL;
716
717     if (context_handle == GSS_C_NO_CONTEXT) {
718         ret = EINVAL;
719         return GSS_S_FAILURE;
720     }
721     
722     ret = krb5_init_context(&context);
723     if(ret) {
724         *minor_status = ret;
725         return GSS_S_FAILURE;
726     }
727
728     major_status =
729         gss_inquire_sec_context_by_oid (minor_status,
730                                         context_handle,
731                                         oid,
732                                         &data_set);
733     if (major_status)
734         return major_status;
735     
736     if (data_set == GSS_C_NO_BUFFER_SET || data_set->count != 1) {
737         gss_release_buffer_set(minor_status, &data_set);
738         *minor_status = EINVAL;
739         return GSS_S_FAILURE;
740     }
741
742     sp = krb5_storage_from_mem(data_set->elements[0].value,
743                                data_set->elements[0].length);
744     if (sp == NULL) {
745         ret = ENOMEM;
746         goto out;
747     }
748     
749     *keyblock = calloc(1, sizeof(**keyblock));
750     if (keyblock == NULL) {
751         ret = ENOMEM;
752         goto out;
753     }
754
755     ret = krb5_ret_keyblock(sp, *keyblock);
756
757 out: 
758     gss_release_buffer_set(minor_status, &data_set);
759     if (sp)
760         krb5_storage_free(sp);
761     if (ret && keyblock) {
762         krb5_free_keyblock(context, *keyblock);
763         *keyblock = NULL;
764     }
765     if (context)
766         krb5_free_context(context);
767
768     *minor_status = ret;
769     if (ret)
770         return GSS_S_FAILURE;
771
772     return GSS_S_COMPLETE;
773 }
774
775 /*
776  *
777  */
778
779 OM_uint32 GSSAPI_LIB_FUNCTION
780 gsskrb5_extract_service_keyblock(OM_uint32 *minor_status,
781                                  gss_ctx_id_t context_handle,
782                                  krb5_keyblock **keyblock)
783 {
784     return gsskrb5_extract_key(minor_status,
785                                context_handle,
786                                GSS_KRB5_GET_SERVICE_KEYBLOCK_X,
787                                keyblock);
788 }
789
790 OM_uint32 GSSAPI_LIB_FUNCTION
791 gsskrb5_get_initiator_subkey(OM_uint32 *minor_status,
792                              gss_ctx_id_t context_handle,
793                              krb5_keyblock **keyblock)
794 {
795     return gsskrb5_extract_key(minor_status,
796                                context_handle,
797                                GSS_KRB5_GET_INITIATOR_SUBKEY_X,
798                                keyblock);
799 }
800
801 OM_uint32 GSSAPI_LIB_FUNCTION
802 gsskrb5_get_subkey(OM_uint32 *minor_status,
803                    gss_ctx_id_t context_handle,
804                    krb5_keyblock **keyblock)
805 {
806     return gsskrb5_extract_key(minor_status,
807                                context_handle,
808                                GSS_KRB5_GET_SUBKEY_X,
809                                keyblock);
810 }
811
812 OM_uint32 GSSAPI_LIB_FUNCTION
813 gsskrb5_set_default_realm(const char *realm)
814 {
815         struct _gss_mech_switch *m;
816         gss_buffer_desc buffer;
817         OM_uint32 junk;
818
819         _gss_load_mech();
820
821         buffer.value = rk_UNCONST(realm);
822         buffer.length = strlen(realm);
823
824         SLIST_FOREACH(m, &_gss_mechs, gm_link) {
825                 if (m->gm_mech.gm_set_sec_context_option == NULL)
826                         continue;
827                 m->gm_mech.gm_set_sec_context_option(&junk, NULL,
828                     GSS_KRB5_SET_DEFAULT_REALM_X, &buffer);
829         }
830
831         return (GSS_S_COMPLETE);
832 }
833
834 OM_uint32 GSSAPI_LIB_FUNCTION
835 gss_krb5_get_tkt_flags(OM_uint32 *minor_status,
836                        gss_ctx_id_t context_handle,
837                        OM_uint32 *tkt_flags)
838 {
839
840     OM_uint32 major_status;
841     gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
842
843     if (context_handle == GSS_C_NO_CONTEXT) {
844         *minor_status = EINVAL;
845         return GSS_S_FAILURE;
846     }
847     
848     major_status =
849         gss_inquire_sec_context_by_oid (minor_status,
850                                         context_handle,
851                                         GSS_KRB5_GET_TKT_FLAGS_X,
852                                         &data_set);
853     if (major_status)
854         return major_status;
855     
856     if (data_set == GSS_C_NO_BUFFER_SET || 
857         data_set->count != 1 ||
858         data_set->elements[0].length < 4) {
859         gss_release_buffer_set(minor_status, &data_set);
860         *minor_status = EINVAL;
861         return GSS_S_FAILURE;
862     }
863
864     {
865         const u_char *p = data_set->elements[0].value;
866         *tkt_flags = (p[0] << 0) | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
867     }
868
869     gss_release_buffer_set(minor_status, &data_set);
870     return GSS_S_COMPLETE;
871 }
872
873 OM_uint32 GSSAPI_LIB_FUNCTION
874 gsskrb5_set_time_offset(int offset)
875 {
876         struct _gss_mech_switch *m;
877         gss_buffer_desc buffer;
878         OM_uint32 junk;
879         int32_t o = offset;
880
881         _gss_load_mech();
882
883         buffer.value = &o;
884         buffer.length = sizeof(o);
885
886         SLIST_FOREACH(m, &_gss_mechs, gm_link) {
887                 if (m->gm_mech.gm_set_sec_context_option == NULL)
888                         continue;
889                 m->gm_mech.gm_set_sec_context_option(&junk, NULL,
890                     GSS_KRB5_SET_TIME_OFFSET_X, &buffer);
891         }
892
893         return (GSS_S_COMPLETE);
894 }
895
896 OM_uint32 GSSAPI_LIB_FUNCTION
897 gsskrb5_get_time_offset(int *offset)
898 {
899         struct _gss_mech_switch *m;
900         gss_buffer_desc buffer;
901         OM_uint32 maj_stat, junk;
902         int32_t o;
903
904         _gss_load_mech();
905
906         buffer.value = &o;
907         buffer.length = sizeof(o);
908
909         SLIST_FOREACH(m, &_gss_mechs, gm_link) {
910                 if (m->gm_mech.gm_set_sec_context_option == NULL)
911                         continue;
912                 maj_stat = m->gm_mech.gm_set_sec_context_option(&junk, NULL,
913                     GSS_KRB5_GET_TIME_OFFSET_X, &buffer);
914
915                 if (maj_stat == GSS_S_COMPLETE) {
916                         *offset = o;
917                         return maj_stat;
918                 }
919         }
920
921         return (GSS_S_UNAVAILABLE);
922 }