r19681: Update to current lorikeet-heimdal. I'm looking at using the realm
[Samba.git] / source / heimdal / lib / gssapi / mech / gss_krb5.c
blob76a2c2b637b91ba222304b4c043f1b515118277c
1 /*-
2 * Copyright (c) 2005 Doug Rabson
3 * All rights reserved.
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.
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.
26 * $FreeBSD: src/lib/libgssapi/gss_krb5.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
29 #include "mech_locl.h"
30 RCSID("$Id: gss_krb5.c,v 1.21 2006/11/10 00:57:27 lha Exp $");
32 #include <krb5.h>
33 #include <roken.h>
36 OM_uint32
37 gss_krb5_copy_ccache(OM_uint32 *minor_status,
38 gss_cred_id_t cred,
39 krb5_ccache out)
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;
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;
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;
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;
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;
76 kret = krb5_cc_resolve(context, str, &id);
77 free(str);
78 if (kret) {
79 *minor_status = kret;
80 return GSS_S_FAILURE;
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;
91 return ret;
94 OM_uint32
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)
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;
109 *cred = GSS_C_NO_CREDENTIAL;
111 ret = krb5_init_context(&context);
112 if (ret) {
113 *minor_status = ret;
114 return GSS_S_FAILURE;
117 sp = krb5_storage_emem();
118 if (sp == NULL) {
119 *minor_status = ENOMEM;
120 major_status = GSS_S_FAILURE;
121 goto out;
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);
130 } else
131 ret = krb5_store_string(sp, "");
132 if (ret) {
133 *minor_status = ret;
134 major_status = GSS_S_FAILURE;
135 goto out;
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);
144 } else
145 krb5_store_string(sp, "");
146 if (ret) {
147 *minor_status = ret;
148 major_status = GSS_S_FAILURE;
149 goto out;
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);
159 } else
160 krb5_store_string(sp, "");
161 if (ret) {
162 *minor_status = ret;
163 major_status = GSS_S_FAILURE;
164 goto out;
167 krb5_storage_to_data(sp, &data);
169 buffer.value = data.data;
170 buffer.length = data.length;
172 major_status = gss_set_cred_option(minor_status,
173 cred,
174 GSS_KRB5_IMPORT_CRED_X,
175 &buffer);
176 krb5_data_free(&data);
177 out:
178 if (sp)
179 krb5_storage_free(sp);
180 krb5_free_context(context);
181 return major_status;
184 OM_uint32
185 gsskrb5_register_acceptor_identity(const char *identity)
187 struct _gss_mech_switch *m;
188 gss_buffer_desc buffer;
189 OM_uint32 junk;
191 _gss_load_mech();
193 buffer.value = rk_UNCONST(identity);
194 buffer.length = strlen(identity);
196 SLIST_FOREACH(m, &_gss_mechs, gm_link) {
197 if (m->gm_mech.gm_set_sec_context_option == NULL)
198 continue;
199 m->gm_mech.gm_set_sec_context_option(&junk, NULL,
200 GSS_KRB5_REGISTER_ACCEPTOR_IDENTITY_X, &buffer);
203 return (GSS_S_COMPLETE);
206 OM_uint32
207 gsskrb5_set_dns_canonicalize(int flag)
209 struct _gss_mech_switch *m;
210 gss_buffer_desc buffer;
211 OM_uint32 junk;
212 char b = (flag != 0);
214 _gss_load_mech();
216 buffer.value = &b;
217 buffer.length = sizeof(b);
219 SLIST_FOREACH(m, &_gss_mechs, gm_link) {
220 if (m->gm_mech.gm_set_sec_context_option == NULL)
221 continue;
222 m->gm_mech.gm_set_sec_context_option(&junk, NULL,
223 GSS_KRB5_SET_DNS_CANONICALIZE_X, &buffer);
226 return (GSS_S_COMPLETE);
231 static krb5_error_code
232 set_key(krb5_keyblock *keyblock, gss_krb5_lucid_key_t *key)
234 key->type = keyblock->keytype;
235 key->length = keyblock->keyvalue.length;
236 key->data = malloc(key->length);
237 if (key->data == NULL && key->length != 0)
238 return ENOMEM;
239 memcpy(key->data, keyblock->keyvalue.data, key->length);
240 return 0;
243 static void
244 free_key(gss_krb5_lucid_key_t *key)
246 memset(key->data, 0, key->length);
247 free(key->data);
248 memset(key, 0, sizeof(*key));
252 OM_uint32
253 gss_krb5_export_lucid_sec_context(OM_uint32 *minor_status,
254 gss_ctx_id_t *context_handle,
255 OM_uint32 version,
256 void **rctx)
258 krb5_context context = NULL;
259 krb5_error_code ret;
260 gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
261 OM_uint32 major_status;
262 gss_krb5_lucid_context_v1_t *ctx = NULL;
263 krb5_storage *sp = NULL;
264 uint32_t num;
266 if (context_handle == NULL
267 || *context_handle == GSS_C_NO_CONTEXT
268 || version != 1)
270 ret = EINVAL;
271 return GSS_S_FAILURE;
274 major_status =
275 gss_inquire_sec_context_by_oid (minor_status,
276 *context_handle,
277 GSS_KRB5_EXPORT_LUCID_CONTEXT_V1_X,
278 &data_set);
279 if (major_status)
280 return major_status;
282 if (data_set == GSS_C_NO_BUFFER_SET || data_set->count != 1) {
283 gss_release_buffer_set(minor_status, &data_set);
284 *minor_status = EINVAL;
285 return GSS_S_FAILURE;
288 ret = krb5_init_context(&context);
289 if (ret)
290 goto out;
292 ctx = calloc(1, sizeof(*ctx));
293 if (ctx == NULL) {
294 ret = ENOMEM;
295 goto out;
298 sp = krb5_storage_from_mem(data_set->elements[0].value,
299 data_set->elements[0].length);
300 if (sp == NULL) {
301 ret = ENOMEM;
302 goto out;
305 ret = krb5_ret_uint32(sp, &num);
306 if (ret) goto out;
307 if (num != 1) {
308 ret = EINVAL;
309 goto out;
311 ctx->version = 1;
312 /* initiator */
313 ret = krb5_ret_uint32(sp, &ctx->initiate);
314 if (ret) goto out;
315 /* endtime */
316 ret = krb5_ret_uint32(sp, &ctx->endtime);
317 if (ret) goto out;
318 /* send_seq */
319 ret = krb5_ret_uint32(sp, &num);
320 if (ret) goto out;
321 ctx->send_seq = ((uint64_t)num) << 32;
322 ret = krb5_ret_uint32(sp, &num);
323 if (ret) goto out;
324 ctx->send_seq |= num;
325 /* recv_seq */
326 ret = krb5_ret_uint32(sp, &num);
327 if (ret) goto out;
328 ctx->recv_seq = ((uint64_t)num) << 32;
329 ret = krb5_ret_uint32(sp, &num);
330 if (ret) goto out;
331 ctx->recv_seq |= num;
332 /* protocol */
333 ret = krb5_ret_uint32(sp, &ctx->protocol);
334 if (ret) goto out;
335 if (ctx->protocol == 0) {
336 krb5_keyblock key;
338 /* sign_alg */
339 ret = krb5_ret_uint32(sp, &ctx->rfc1964_kd.sign_alg);
340 if (ret) goto out;
341 /* seal_alg */
342 ret = krb5_ret_uint32(sp, &ctx->rfc1964_kd.seal_alg);
343 if (ret) goto out;
344 /* ctx_key */
345 ret = krb5_ret_keyblock(sp, &key);
346 if (ret) goto out;
347 ret = set_key(&key, &ctx->rfc1964_kd.ctx_key);
348 krb5_free_keyblock_contents(context, &key);
349 if (ret) goto out;
350 } else if (ctx->protocol == 1) {
351 krb5_keyblock key;
353 /* acceptor_subkey */
354 ret = krb5_ret_uint32(sp, &ctx->cfx_kd.have_acceptor_subkey);
355 if (ret) goto out;
356 /* ctx_key */
357 ret = krb5_ret_keyblock(sp, &key);
358 if (ret) goto out;
359 ret = set_key(&key, &ctx->cfx_kd.ctx_key);
360 krb5_free_keyblock_contents(context, &key);
361 if (ret) goto out;
362 /* acceptor_subkey */
363 if (ctx->cfx_kd.have_acceptor_subkey) {
364 ret = krb5_ret_keyblock(sp, &key);
365 if (ret) goto out;
366 ret = set_key(&key, &ctx->cfx_kd.acceptor_subkey);
367 krb5_free_keyblock_contents(context, &key);
368 if (ret) goto out;
370 } else {
371 ret = EINVAL;
372 goto out;
375 *rctx = ctx;
377 out:
378 gss_release_buffer_set(minor_status, &data_set);
379 if (sp)
380 krb5_storage_free(sp);
381 if (context)
382 krb5_free_context(context);
384 if (ret) {
385 if (ctx)
386 gss_krb5_free_lucid_sec_context(NULL, ctx);
388 *minor_status = ret;
389 return GSS_S_FAILURE;
391 *minor_status = 0;
392 return GSS_S_COMPLETE;
395 OM_uint32
396 gss_krb5_free_lucid_sec_context(OM_uint32 *minor_status, void *c)
398 gss_krb5_lucid_context_v1_t *ctx = c;
400 if (ctx->version != 1) {
401 if (minor_status)
402 *minor_status = 0;
403 return GSS_S_FAILURE;
406 if (ctx->protocol == 0) {
407 free_key(&ctx->rfc1964_kd.ctx_key);
408 } else if (ctx->protocol == 1) {
409 free_key(&ctx->cfx_kd.ctx_key);
410 if (ctx->cfx_kd.have_acceptor_subkey)
411 free_key(&ctx->cfx_kd.acceptor_subkey);
413 free(ctx);
414 if (minor_status)
415 *minor_status = 0;
416 return GSS_S_COMPLETE;
423 OM_uint32
424 gss_krb5_set_allowable_enctypes(OM_uint32 *min_status,
425 gss_cred_id_t cred,
426 OM_uint32 num_enctypes,
427 int32_t *enctypes)
429 OM_uint32 maj_status;
430 gss_buffer_desc buffer;
431 krb5_storage *sp;
432 krb5_data data;
434 sp = krb5_storage_emem();
435 if (sp == NULL) {
436 *min_status = ENOMEM;
437 maj_status = GSS_S_FAILURE;
438 goto out;
441 while(*enctypes) {
442 krb5_store_int32(sp, *enctypes);
443 enctypes++;
446 krb5_storage_to_data(sp, &data);
448 buffer.value = data.data;
449 buffer.length = data.length;
451 maj_status = gss_set_cred_option(min_status,
452 &cred,
453 GSS_KRB5_SET_ALLOWABLE_ENCTYPES_X,
454 &buffer);
455 out:
456 if (sp)
457 krb5_storage_free(sp);
458 return maj_status;
465 OM_uint32
466 gsskrb5_set_send_to_kdc(struct gsskrb5_send_to_kdc *c)
468 struct _gss_mech_switch *m;
469 gss_buffer_desc buffer;
470 OM_uint32 junk;
472 _gss_load_mech();
474 if (c) {
475 buffer.value = c;
476 buffer.length = sizeof(*c);
477 } else {
478 buffer.value = NULL;
479 buffer.length = 0;
482 SLIST_FOREACH(m, &_gss_mechs, gm_link) {
483 if (m->gm_mech.gm_set_sec_context_option == NULL)
484 continue;
485 m->gm_mech.gm_set_sec_context_option(&junk, NULL,
486 GSS_KRB5_SEND_TO_KDC_X, &buffer);
489 return (GSS_S_COMPLETE);
496 OM_uint32
497 gsskrb5_extract_authtime_from_sec_context(OM_uint32 *minor_status,
498 gss_ctx_id_t context_handle,
499 time_t *authtime)
501 gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
502 OM_uint32 maj_stat;
504 if (context_handle == GSS_C_NO_CONTEXT) {
505 *minor_status = EINVAL;
506 return GSS_S_FAILURE;
509 maj_stat =
510 gss_inquire_sec_context_by_oid (minor_status,
511 context_handle,
512 GSS_KRB5_GET_AUTHTIME_X,
513 &data_set);
514 if (maj_stat)
515 return maj_stat;
517 if (data_set == GSS_C_NO_BUFFER_SET) {
518 gss_release_buffer_set(minor_status, &data_set);
519 *minor_status = EINVAL;
520 return GSS_S_FAILURE;
523 if (data_set->count != 1) {
524 gss_release_buffer_set(minor_status, &data_set);
525 *minor_status = EINVAL;
526 return GSS_S_FAILURE;
529 if (data_set->elements[0].length != 4) {
530 gss_release_buffer_set(minor_status, &data_set);
531 *minor_status = EINVAL;
532 return GSS_S_FAILURE;
536 unsigned char *buf = data_set->elements[0].value;
537 *authtime = (buf[3] <<24) | (buf[2] << 16) |
538 (buf[1] << 8) | (buf[0] << 0);
541 gss_release_buffer_set(minor_status, &data_set);
543 *minor_status = 0;
544 return GSS_S_COMPLETE;
551 OM_uint32
552 gsskrb5_extract_authz_data_from_sec_context(OM_uint32 *minor_status,
553 gss_ctx_id_t context_handle,
554 int ad_type,
555 gss_buffer_t ad_data)
557 gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
558 OM_uint32 maj_stat;
559 gss_OID_desc oid_flat;
560 heim_oid baseoid, oid;
561 size_t size;
563 if (context_handle == GSS_C_NO_CONTEXT) {
564 *minor_status = EINVAL;
565 return GSS_S_FAILURE;
568 /* All this to append an integer to an oid... */
570 if (der_get_oid(GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X->elements,
571 GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X->length,
572 &baseoid, NULL) != 0) {
573 *minor_status = EINVAL;
574 return GSS_S_FAILURE;
577 oid.length = baseoid.length + 1;
578 oid.components = calloc(oid.length, sizeof(*oid.components));
579 if (oid.components == NULL) {
580 der_free_oid(&baseoid);
582 *minor_status = ENOMEM;
583 return GSS_S_FAILURE;
586 memcpy(oid.components, baseoid.components,
587 baseoid.length * sizeof(*baseoid.components));
589 der_free_oid(&baseoid);
591 oid.components[oid.length - 1] = ad_type;
593 oid_flat.length = der_length_oid(&oid);
594 oid_flat.elements = malloc(oid_flat.length);
595 if (oid_flat.elements == NULL) {
596 free(oid.components);
597 *minor_status = ENOMEM;
598 return GSS_S_FAILURE;
601 if (der_put_oid((unsigned char *)oid_flat.elements + oid_flat.length - 1,
602 oid_flat.length, &oid, &size) != 0) {
603 free(oid.components);
605 *minor_status = EINVAL;
606 return GSS_S_FAILURE;
608 if (oid_flat.length != size)
609 abort();
611 free(oid.components);
613 /* FINALLY, we have the OID */
615 maj_stat = gss_inquire_sec_context_by_oid (minor_status,
616 context_handle,
617 &oid_flat,
618 &data_set);
620 free(oid_flat.elements);
622 if (maj_stat)
623 return maj_stat;
625 if (data_set == GSS_C_NO_BUFFER_SET || data_set->count != 1) {
626 gss_release_buffer_set(minor_status, &data_set);
627 *minor_status = EINVAL;
628 return GSS_S_FAILURE;
631 ad_data->value = malloc(data_set->elements[0].length);
632 if (ad_data->value == NULL) {
633 gss_release_buffer_set(minor_status, &data_set);
634 *minor_status = ENOMEM;
635 return GSS_S_FAILURE;
638 ad_data->length = data_set->elements[0].length;
639 memcpy(ad_data->value, data_set->elements[0].value, ad_data->length);
640 gss_release_buffer_set(minor_status, &data_set);
642 *minor_status = 0;
643 return GSS_S_COMPLETE;
650 static OM_uint32
651 gsskrb5_extract_key(OM_uint32 *minor_status,
652 gss_ctx_id_t context_handle,
653 const gss_OID oid,
654 krb5_keyblock **keyblock)
656 krb5_error_code ret;
657 gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
658 OM_uint32 major_status;
659 krb5_context context = NULL;
660 krb5_storage *sp = NULL;
662 if (context_handle == GSS_C_NO_CONTEXT) {
663 ret = EINVAL;
664 return GSS_S_FAILURE;
667 ret = krb5_init_context(&context);
668 if(ret) {
669 *minor_status = ret;
670 return GSS_S_FAILURE;
673 major_status =
674 gss_inquire_sec_context_by_oid (minor_status,
675 context_handle,
676 oid,
677 &data_set);
678 if (major_status)
679 return major_status;
681 if (data_set == GSS_C_NO_BUFFER_SET || data_set->count != 1) {
682 gss_release_buffer_set(minor_status, &data_set);
683 *minor_status = EINVAL;
684 return GSS_S_FAILURE;
687 sp = krb5_storage_from_mem(data_set->elements[0].value,
688 data_set->elements[0].length);
689 if (sp == NULL) {
690 ret = ENOMEM;
691 goto out;
694 *keyblock = calloc(1, sizeof(**keyblock));
695 if (keyblock == NULL) {
696 ret = ENOMEM;
697 goto out;
700 ret = krb5_ret_keyblock(sp, *keyblock);
702 out:
703 gss_release_buffer_set(minor_status, &data_set);
704 if (sp)
705 krb5_storage_free(sp);
706 if (ret && keyblock) {
707 krb5_free_keyblock(context, *keyblock);
708 *keyblock = NULL;
710 if (context)
711 krb5_free_context(context);
713 *minor_status = ret;
714 if (ret)
715 return GSS_S_FAILURE;
717 return GSS_S_COMPLETE;
724 OM_uint32
725 gsskrb5_extract_service_keyblock(OM_uint32 *minor_status,
726 gss_ctx_id_t context_handle,
727 krb5_keyblock **keyblock)
729 return gsskrb5_extract_key(minor_status,
730 context_handle,
731 GSS_KRB5_GET_SERVICE_KEYBLOCK_X,
732 keyblock);
735 OM_uint32
736 gsskrb5_get_initiator_subkey(OM_uint32 *minor_status,
737 gss_ctx_id_t context_handle,
738 krb5_keyblock **keyblock)
740 return gsskrb5_extract_key(minor_status,
741 context_handle,
742 GSS_KRB5_GET_INITIATOR_SUBKEY_X,
743 keyblock);
746 OM_uint32
747 gsskrb5_get_subkey(OM_uint32 *minor_status,
748 gss_ctx_id_t context_handle,
749 krb5_keyblock **keyblock)
751 return gsskrb5_extract_key(minor_status,
752 context_handle,
753 GSS_KRB5_GET_SUBKEY_X,
754 keyblock);
757 OM_uint32
758 gsskrb5_set_default_realm(const char *realm)
760 struct _gss_mech_switch *m;
761 gss_buffer_desc buffer;
762 OM_uint32 junk;
764 _gss_load_mech();
766 buffer.value = rk_UNCONST(realm);
767 buffer.length = strlen(realm);
769 SLIST_FOREACH(m, &_gss_mechs, gm_link) {
770 if (m->gm_mech.gm_set_sec_context_option == NULL)
771 continue;
772 m->gm_mech.gm_set_sec_context_option(&junk, NULL,
773 GSS_KRB5_SET_DEFAULT_REALM_X, &buffer);
776 return (GSS_S_COMPLETE);