2 * Copyright (c) 2005 Doug Rabson
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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
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 21889 2007-08-09 07:43:24Z lha $");
37 gss_krb5_copy_ccache(OM_uint32 *minor_status,
41 gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
48 ret = gss_inquire_cred_by_oid(minor_status,
50 GSS_KRB5_COPY_CCACHE_X,
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;
61 kret = krb5_init_context(&context);
64 gss_release_buffer_set(minor_status, &data_set);
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);
72 *minor_status = ENOMEM;
76 kret = krb5_cc_resolve(context, str, &id);
83 kret = krb5_cc_copy_cache(context, id, out);
84 krb5_cc_close(context, id);
85 krb5_free_context(context);
95 gss_krb5_import_cred(OM_uint32 *minor_status,
97 krb5_principal keytab_principal,
101 gss_buffer_desc buffer;
102 OM_uint32 major_status;
103 krb5_context context;
109 *cred = GSS_C_NO_CREDENTIAL;
111 ret = krb5_init_context(&context);
114 return GSS_S_FAILURE;
117 sp = krb5_storage_emem();
119 *minor_status = ENOMEM;
120 major_status = GSS_S_FAILURE;
125 ret = krb5_cc_get_full_name(context, id, &str);
127 ret = krb5_store_string(sp, str);
131 ret = krb5_store_string(sp, "");
134 major_status = GSS_S_FAILURE;
138 if (keytab_principal) {
139 ret = krb5_unparse_name(context, keytab_principal, &str);
141 ret = krb5_store_string(sp, str);
145 krb5_store_string(sp, "");
148 major_status = GSS_S_FAILURE;
154 ret = krb5_kt_get_full_name(context, keytab, &str);
156 ret = krb5_store_string(sp, str);
160 krb5_store_string(sp, "");
163 major_status = GSS_S_FAILURE;
167 ret = krb5_storage_to_data(sp, &data);
170 major_status = GSS_S_FAILURE;
174 buffer.value = data.data;
175 buffer.length = data.length;
177 major_status = gss_set_cred_option(minor_status,
179 GSS_KRB5_IMPORT_CRED_X,
181 krb5_data_free(&data);
184 krb5_storage_free(sp);
185 krb5_free_context(context);
190 gsskrb5_register_acceptor_identity(const char *identity)
192 struct _gss_mech_switch *m;
193 gss_buffer_desc buffer;
198 buffer.value = rk_UNCONST(identity);
199 buffer.length = strlen(identity);
201 SLIST_FOREACH(m, &_gss_mechs, gm_link) {
202 if (m->gm_mech.gm_set_sec_context_option == NULL)
204 m->gm_mech.gm_set_sec_context_option(&junk, NULL,
205 GSS_KRB5_REGISTER_ACCEPTOR_IDENTITY_X, &buffer);
208 return (GSS_S_COMPLETE);
212 gsskrb5_set_dns_canonicalize(int flag)
214 struct _gss_mech_switch *m;
215 gss_buffer_desc buffer;
217 char b = (flag != 0);
222 buffer.length = sizeof(b);
224 SLIST_FOREACH(m, &_gss_mechs, gm_link) {
225 if (m->gm_mech.gm_set_sec_context_option == NULL)
227 m->gm_mech.gm_set_sec_context_option(&junk, NULL,
228 GSS_KRB5_SET_DNS_CANONICALIZE_X, &buffer);
231 return (GSS_S_COMPLETE);
236 static krb5_error_code
237 set_key(krb5_keyblock *keyblock, gss_krb5_lucid_key_t *key)
239 key->type = keyblock->keytype;
240 key->length = keyblock->keyvalue.length;
241 key->data = malloc(key->length);
242 if (key->data == NULL && key->length != 0)
244 memcpy(key->data, keyblock->keyvalue.data, key->length);
249 free_key(gss_krb5_lucid_key_t *key)
251 memset(key->data, 0, key->length);
253 memset(key, 0, sizeof(*key));
257 gss_krb5_export_lucid_sec_context(OM_uint32 *minor_status,
258 gss_ctx_id_t *context_handle,
262 krb5_context context = NULL;
264 gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
265 OM_uint32 major_status;
266 gss_krb5_lucid_context_v1_t *ctx = NULL;
267 krb5_storage *sp = NULL;
270 if (context_handle == NULL
271 || *context_handle == GSS_C_NO_CONTEXT
275 return GSS_S_FAILURE;
279 gss_inquire_sec_context_by_oid (minor_status,
281 GSS_KRB5_EXPORT_LUCID_CONTEXT_V1_X,
286 if (data_set == GSS_C_NO_BUFFER_SET || data_set->count != 1) {
287 gss_release_buffer_set(minor_status, &data_set);
288 *minor_status = EINVAL;
289 return GSS_S_FAILURE;
292 ret = krb5_init_context(&context);
296 ctx = calloc(1, sizeof(*ctx));
302 sp = krb5_storage_from_mem(data_set->elements[0].value,
303 data_set->elements[0].length);
309 ret = krb5_ret_uint32(sp, &num);
317 ret = krb5_ret_uint32(sp, &ctx->initiate);
320 ret = krb5_ret_uint32(sp, &ctx->endtime);
323 ret = krb5_ret_uint32(sp, &num);
325 ctx->send_seq = ((uint64_t)num) << 32;
326 ret = krb5_ret_uint32(sp, &num);
328 ctx->send_seq |= num;
330 ret = krb5_ret_uint32(sp, &num);
332 ctx->recv_seq = ((uint64_t)num) << 32;
333 ret = krb5_ret_uint32(sp, &num);
335 ctx->recv_seq |= num;
337 ret = krb5_ret_uint32(sp, &ctx->protocol);
339 if (ctx->protocol == 0) {
343 ret = krb5_ret_uint32(sp, &ctx->rfc1964_kd.sign_alg);
346 ret = krb5_ret_uint32(sp, &ctx->rfc1964_kd.seal_alg);
349 ret = krb5_ret_keyblock(sp, &key);
351 ret = set_key(&key, &ctx->rfc1964_kd.ctx_key);
352 krb5_free_keyblock_contents(context, &key);
354 } else if (ctx->protocol == 1) {
357 /* acceptor_subkey */
358 ret = krb5_ret_uint32(sp, &ctx->cfx_kd.have_acceptor_subkey);
361 ret = krb5_ret_keyblock(sp, &key);
363 ret = set_key(&key, &ctx->cfx_kd.ctx_key);
364 krb5_free_keyblock_contents(context, &key);
366 /* acceptor_subkey */
367 if (ctx->cfx_kd.have_acceptor_subkey) {
368 ret = krb5_ret_keyblock(sp, &key);
370 ret = set_key(&key, &ctx->cfx_kd.acceptor_subkey);
371 krb5_free_keyblock_contents(context, &key);
382 gss_release_buffer_set(minor_status, &data_set);
384 krb5_storage_free(sp);
386 krb5_free_context(context);
390 gss_krb5_free_lucid_sec_context(NULL, ctx);
393 return GSS_S_FAILURE;
396 return GSS_S_COMPLETE;
400 gss_krb5_free_lucid_sec_context(OM_uint32 *minor_status, void *c)
402 gss_krb5_lucid_context_v1_t *ctx = c;
404 if (ctx->version != 1) {
407 return GSS_S_FAILURE;
410 if (ctx->protocol == 0) {
411 free_key(&ctx->rfc1964_kd.ctx_key);
412 } else if (ctx->protocol == 1) {
413 free_key(&ctx->cfx_kd.ctx_key);
414 if (ctx->cfx_kd.have_acceptor_subkey)
415 free_key(&ctx->cfx_kd.acceptor_subkey);
420 return GSS_S_COMPLETE;
428 gss_krb5_set_allowable_enctypes(OM_uint32 *minor_status,
430 OM_uint32 num_enctypes,
434 OM_uint32 maj_status;
435 gss_buffer_desc buffer;
440 sp = krb5_storage_emem();
442 *minor_status = ENOMEM;
443 maj_status = GSS_S_FAILURE;
447 for (i = 0; i < num_enctypes; i++) {
448 ret = krb5_store_int32(sp, enctypes[i]);
451 maj_status = GSS_S_FAILURE;
456 ret = krb5_storage_to_data(sp, &data);
459 maj_status = GSS_S_FAILURE;
463 buffer.value = data.data;
464 buffer.length = data.length;
466 maj_status = gss_set_cred_option(minor_status,
468 GSS_KRB5_SET_ALLOWABLE_ENCTYPES_X,
470 krb5_data_free(&data);
473 krb5_storage_free(sp);
482 gsskrb5_set_send_to_kdc(struct gsskrb5_send_to_kdc *c)
484 struct _gss_mech_switch *m;
485 gss_buffer_desc buffer;
492 buffer.length = sizeof(*c);
498 SLIST_FOREACH(m, &_gss_mechs, gm_link) {
499 if (m->gm_mech.gm_set_sec_context_option == NULL)
501 m->gm_mech.gm_set_sec_context_option(&junk, NULL,
502 GSS_KRB5_SEND_TO_KDC_X, &buffer);
505 return (GSS_S_COMPLETE);
513 gss_krb5_ccache_name(OM_uint32 *minor_status,
515 const char **out_name)
517 struct _gss_mech_switch *m;
518 gss_buffer_desc buffer;
526 buffer.value = rk_UNCONST(name);
527 buffer.length = strlen(name);
529 SLIST_FOREACH(m, &_gss_mechs, gm_link) {
530 if (m->gm_mech.gm_set_sec_context_option == NULL)
532 m->gm_mech.gm_set_sec_context_option(&junk, NULL,
533 GSS_KRB5_CCACHE_NAME_X, &buffer);
536 return (GSS_S_COMPLETE);
545 gsskrb5_extract_authtime_from_sec_context(OM_uint32 *minor_status,
546 gss_ctx_id_t context_handle,
549 gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
552 if (context_handle == GSS_C_NO_CONTEXT) {
553 *minor_status = EINVAL;
554 return GSS_S_FAILURE;
558 gss_inquire_sec_context_by_oid (minor_status,
560 GSS_KRB5_GET_AUTHTIME_X,
565 if (data_set == GSS_C_NO_BUFFER_SET) {
566 gss_release_buffer_set(minor_status, &data_set);
567 *minor_status = EINVAL;
568 return GSS_S_FAILURE;
571 if (data_set->count != 1) {
572 gss_release_buffer_set(minor_status, &data_set);
573 *minor_status = EINVAL;
574 return GSS_S_FAILURE;
577 if (data_set->elements[0].length != 4) {
578 gss_release_buffer_set(minor_status, &data_set);
579 *minor_status = EINVAL;
580 return GSS_S_FAILURE;
584 unsigned char *buf = data_set->elements[0].value;
585 *authtime = (buf[3] <<24) | (buf[2] << 16) |
586 (buf[1] << 8) | (buf[0] << 0);
589 gss_release_buffer_set(minor_status, &data_set);
592 return GSS_S_COMPLETE;
600 gsskrb5_extract_authz_data_from_sec_context(OM_uint32 *minor_status,
601 gss_ctx_id_t context_handle,
603 gss_buffer_t ad_data)
605 gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
607 gss_OID_desc oid_flat;
608 heim_oid baseoid, oid;
611 if (context_handle == GSS_C_NO_CONTEXT) {
612 *minor_status = EINVAL;
613 return GSS_S_FAILURE;
616 /* All this to append an integer to an oid... */
618 if (der_get_oid(GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X->elements,
619 GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X->length,
620 &baseoid, NULL) != 0) {
621 *minor_status = EINVAL;
622 return GSS_S_FAILURE;
625 oid.length = baseoid.length + 1;
626 oid.components = calloc(oid.length, sizeof(*oid.components));
627 if (oid.components == NULL) {
628 der_free_oid(&baseoid);
630 *minor_status = ENOMEM;
631 return GSS_S_FAILURE;
634 memcpy(oid.components, baseoid.components,
635 baseoid.length * sizeof(*baseoid.components));
637 der_free_oid(&baseoid);
639 oid.components[oid.length - 1] = ad_type;
641 oid_flat.length = der_length_oid(&oid);
642 oid_flat.elements = malloc(oid_flat.length);
643 if (oid_flat.elements == NULL) {
644 free(oid.components);
645 *minor_status = ENOMEM;
646 return GSS_S_FAILURE;
649 if (der_put_oid((unsigned char *)oid_flat.elements + oid_flat.length - 1,
650 oid_flat.length, &oid, &size) != 0) {
651 free(oid.components);
652 free(oid_flat.elements);
653 *minor_status = EINVAL;
654 return GSS_S_FAILURE;
656 if (oid_flat.length != size)
659 free(oid.components);
661 /* FINALLY, we have the OID */
663 maj_stat = gss_inquire_sec_context_by_oid (minor_status,
668 free(oid_flat.elements);
673 if (data_set == GSS_C_NO_BUFFER_SET || data_set->count != 1) {
674 gss_release_buffer_set(minor_status, &data_set);
675 *minor_status = EINVAL;
676 return GSS_S_FAILURE;
679 ad_data->value = malloc(data_set->elements[0].length);
680 if (ad_data->value == NULL) {
681 gss_release_buffer_set(minor_status, &data_set);
682 *minor_status = ENOMEM;
683 return GSS_S_FAILURE;
686 ad_data->length = data_set->elements[0].length;
687 memcpy(ad_data->value, data_set->elements[0].value, ad_data->length);
688 gss_release_buffer_set(minor_status, &data_set);
691 return GSS_S_COMPLETE;
699 gsskrb5_extract_key(OM_uint32 *minor_status,
700 gss_ctx_id_t context_handle,
702 krb5_keyblock **keyblock)
705 gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
706 OM_uint32 major_status;
707 krb5_context context = NULL;
708 krb5_storage *sp = NULL;
710 if (context_handle == GSS_C_NO_CONTEXT) {
712 return GSS_S_FAILURE;
715 ret = krb5_init_context(&context);
718 return GSS_S_FAILURE;
722 gss_inquire_sec_context_by_oid (minor_status,
729 if (data_set == GSS_C_NO_BUFFER_SET || data_set->count != 1) {
730 gss_release_buffer_set(minor_status, &data_set);
731 *minor_status = EINVAL;
732 return GSS_S_FAILURE;
735 sp = krb5_storage_from_mem(data_set->elements[0].value,
736 data_set->elements[0].length);
742 *keyblock = calloc(1, sizeof(**keyblock));
743 if (keyblock == NULL) {
748 ret = krb5_ret_keyblock(sp, *keyblock);
751 gss_release_buffer_set(minor_status, &data_set);
753 krb5_storage_free(sp);
754 if (ret && keyblock) {
755 krb5_free_keyblock(context, *keyblock);
759 krb5_free_context(context);
763 return GSS_S_FAILURE;
765 return GSS_S_COMPLETE;
773 gsskrb5_extract_service_keyblock(OM_uint32 *minor_status,
774 gss_ctx_id_t context_handle,
775 krb5_keyblock **keyblock)
777 return gsskrb5_extract_key(minor_status,
779 GSS_KRB5_GET_SERVICE_KEYBLOCK_X,
784 gsskrb5_get_initiator_subkey(OM_uint32 *minor_status,
785 gss_ctx_id_t context_handle,
786 krb5_keyblock **keyblock)
788 return gsskrb5_extract_key(minor_status,
790 GSS_KRB5_GET_INITIATOR_SUBKEY_X,
795 gsskrb5_get_subkey(OM_uint32 *minor_status,
796 gss_ctx_id_t context_handle,
797 krb5_keyblock **keyblock)
799 return gsskrb5_extract_key(minor_status,
801 GSS_KRB5_GET_SUBKEY_X,
806 gsskrb5_set_default_realm(const char *realm)
808 struct _gss_mech_switch *m;
809 gss_buffer_desc buffer;
814 buffer.value = rk_UNCONST(realm);
815 buffer.length = strlen(realm);
817 SLIST_FOREACH(m, &_gss_mechs, gm_link) {
818 if (m->gm_mech.gm_set_sec_context_option == NULL)
820 m->gm_mech.gm_set_sec_context_option(&junk, NULL,
821 GSS_KRB5_SET_DEFAULT_REALM_X, &buffer);
824 return (GSS_S_COMPLETE);
828 gss_krb5_get_tkt_flags(OM_uint32 *minor_status,
829 gss_ctx_id_t context_handle,
830 OM_uint32 *tkt_flags)
833 OM_uint32 major_status;
834 gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
836 if (context_handle == GSS_C_NO_CONTEXT) {
837 *minor_status = EINVAL;
838 return GSS_S_FAILURE;
842 gss_inquire_sec_context_by_oid (minor_status,
844 GSS_KRB5_GET_TKT_FLAGS_X,
849 if (data_set == GSS_C_NO_BUFFER_SET ||
850 data_set->count != 1 ||
851 data_set->elements[0].length < 4) {
852 gss_release_buffer_set(minor_status, &data_set);
853 *minor_status = EINVAL;
854 return GSS_S_FAILURE;
858 const u_char *p = data_set->elements[0].value;
859 *tkt_flags = (p[0] << 0) | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
862 gss_release_buffer_set(minor_status, &data_set);
863 return GSS_S_COMPLETE;