1 /*	$NetBSD: gss_krb5.c,v 1.1.1.2 2014/04/24 12:45:29 pettai 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