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