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"
35 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
36 gss_krb5_copy_ccache(OM_uint32 *minor_status,
40 gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
47 ret = gss_inquire_cred_by_oid(minor_status,
49 GSS_KRB5_COPY_CCACHE_X,
54 if (data_set == GSS_C_NO_BUFFER_SET || data_set->count < 1) {
55 gss_release_buffer_set(minor_status, &data_set);
56 *minor_status = EINVAL;
60 kret = krb5_init_context(&context);
63 gss_release_buffer_set(minor_status, &data_set);
67 kret = asprintf(&str, "%.*s", (int)data_set->elements[0].length,
68 (char *)data_set->elements[0].value);
69 gss_release_buffer_set(minor_status, &data_set);
70 if (kret < 0 || str == NULL) {
71 *minor_status = ENOMEM;
75 kret = krb5_cc_resolve(context, str, &id);
82 kret = krb5_cc_copy_cache(context, id, out);
83 krb5_cc_close(context, id);
84 krb5_free_context(context);
93 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
94 gss_krb5_import_cred(OM_uint32 *minor_status,
96 krb5_principal keytab_principal,
100 gss_buffer_desc buffer;
101 OM_uint32 major_status;
102 krb5_context context;
108 *cred = GSS_C_NO_CREDENTIAL;
110 ret = krb5_init_context(&context);
113 return GSS_S_FAILURE;
116 sp = krb5_storage_emem();
118 *minor_status = ENOMEM;
119 major_status = GSS_S_FAILURE;
124 ret = krb5_cc_get_full_name(context, id, &str);
126 ret = krb5_store_string(sp, str);
130 ret = krb5_store_string(sp, "");
133 major_status = GSS_S_FAILURE;
137 if (keytab_principal) {
138 ret = krb5_unparse_name(context, keytab_principal, &str);
140 ret = krb5_store_string(sp, str);
144 krb5_store_string(sp, "");
147 major_status = GSS_S_FAILURE;
153 ret = krb5_kt_get_full_name(context, keytab, &str);
155 ret = krb5_store_string(sp, str);
159 krb5_store_string(sp, "");
162 major_status = GSS_S_FAILURE;
166 ret = krb5_storage_to_data(sp, &data);
169 major_status = GSS_S_FAILURE;
173 buffer.value = data.data;
174 buffer.length = data.length;
176 major_status = gss_set_cred_option(minor_status,
178 GSS_KRB5_IMPORT_CRED_X,
180 krb5_data_free(&data);
183 krb5_storage_free(sp);
184 krb5_free_context(context);
188 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
189 gsskrb5_register_acceptor_identity(const char *identity)
191 gssapi_mech_interface m;
192 gss_buffer_desc buffer;
197 buffer.value = rk_UNCONST(identity);
198 buffer.length = strlen(identity);
200 m = __gss_get_mechanism(GSS_KRB5_MECHANISM);
201 if (m == NULL || m->gm_set_sec_context_option == NULL)
202 return GSS_S_FAILURE;
204 return m->gm_set_sec_context_option(&junk, NULL,
205 GSS_KRB5_REGISTER_ACCEPTOR_IDENTITY_X, &buffer);
208 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
209 krb5_gss_register_acceptor_identity(const char *identity)
211 return gsskrb5_register_acceptor_identity(identity);
215 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
216 gsskrb5_set_dns_canonicalize(int flag)
218 struct _gss_mech_switch *m;
219 gss_buffer_desc buffer;
221 char b = (flag != 0);
226 buffer.length = sizeof(b);
228 HEIM_SLIST_FOREACH(m, &_gss_mechs, gm_link) {
229 if (m->gm_mech.gm_set_sec_context_option == NULL)
231 m->gm_mech.gm_set_sec_context_option(&junk, NULL,
232 GSS_KRB5_SET_DNS_CANONICALIZE_X, &buffer);
235 return (GSS_S_COMPLETE);
240 static krb5_error_code
241 set_key(krb5_keyblock *keyblock, gss_krb5_lucid_key_t *key)
243 key->type = keyblock->keytype;
244 key->length = keyblock->keyvalue.length;
245 key->data = malloc(key->length);
246 if (key->data == NULL && key->length != 0)
248 memcpy(key->data, keyblock->keyvalue.data, key->length);
253 free_key(gss_krb5_lucid_key_t *key)
255 memset(key->data, 0, key->length);
257 memset(key, 0, sizeof(*key));
260 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
261 gss_krb5_export_lucid_sec_context(OM_uint32 *minor_status,
262 gss_ctx_id_t *context_handle,
266 krb5_context context = NULL;
268 gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
269 OM_uint32 major_status;
270 gss_krb5_lucid_context_v1_t *ctx = NULL;
271 krb5_storage *sp = NULL;
274 if (context_handle == NULL
275 || *context_handle == GSS_C_NO_CONTEXT
278 *minor_status = EINVAL;
279 return GSS_S_FAILURE;
283 gss_inquire_sec_context_by_oid (minor_status,
285 GSS_KRB5_EXPORT_LUCID_CONTEXT_V1_X,
290 if (data_set == GSS_C_NO_BUFFER_SET || data_set->count != 1) {
291 gss_release_buffer_set(minor_status, &data_set);
292 *minor_status = EINVAL;
293 return GSS_S_FAILURE;
296 ret = krb5_init_context(&context);
300 ctx = calloc(1, sizeof(*ctx));
306 sp = krb5_storage_from_mem(data_set->elements[0].value,
307 data_set->elements[0].length);
313 ret = krb5_ret_uint32(sp, &num);
321 ret = krb5_ret_uint32(sp, &ctx->initiate);
324 ret = krb5_ret_uint32(sp, &ctx->endtime);
327 ret = krb5_ret_uint32(sp, &num);
329 ctx->send_seq = ((uint64_t)num) << 32;
330 ret = krb5_ret_uint32(sp, &num);
332 ctx->send_seq |= num;
334 ret = krb5_ret_uint32(sp, &num);
336 ctx->recv_seq = ((uint64_t)num) << 32;
337 ret = krb5_ret_uint32(sp, &num);
339 ctx->recv_seq |= num;
341 ret = krb5_ret_uint32(sp, &ctx->protocol);
343 if (ctx->protocol == 0) {
347 ret = krb5_ret_uint32(sp, &ctx->rfc1964_kd.sign_alg);
350 ret = krb5_ret_uint32(sp, &ctx->rfc1964_kd.seal_alg);
353 ret = krb5_ret_keyblock(sp, &key);
355 ret = set_key(&key, &ctx->rfc1964_kd.ctx_key);
356 krb5_free_keyblock_contents(context, &key);
358 } else if (ctx->protocol == 1) {
361 /* acceptor_subkey */
362 ret = krb5_ret_uint32(sp, &ctx->cfx_kd.have_acceptor_subkey);
365 ret = krb5_ret_keyblock(sp, &key);
367 ret = set_key(&key, &ctx->cfx_kd.ctx_key);
368 krb5_free_keyblock_contents(context, &key);
370 /* acceptor_subkey */
371 if (ctx->cfx_kd.have_acceptor_subkey) {
372 ret = krb5_ret_keyblock(sp, &key);
374 ret = set_key(&key, &ctx->cfx_kd.acceptor_subkey);
375 krb5_free_keyblock_contents(context, &key);
386 gss_release_buffer_set(minor_status, &data_set);
388 krb5_storage_free(sp);
390 krb5_free_context(context);
394 gss_krb5_free_lucid_sec_context(NULL, ctx);
397 return GSS_S_FAILURE;
400 return GSS_S_COMPLETE;
403 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
404 gss_krb5_free_lucid_sec_context(OM_uint32 *minor_status, void *c)
406 gss_krb5_lucid_context_v1_t *ctx = c;
408 if (ctx->version != 1) {
411 return GSS_S_FAILURE;
414 if (ctx->protocol == 0) {
415 free_key(&ctx->rfc1964_kd.ctx_key);
416 } else if (ctx->protocol == 1) {
417 free_key(&ctx->cfx_kd.ctx_key);
418 if (ctx->cfx_kd.have_acceptor_subkey)
419 free_key(&ctx->cfx_kd.acceptor_subkey);
424 return GSS_S_COMPLETE;
431 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
432 gss_krb5_set_allowable_enctypes(OM_uint32 *minor_status,
434 OM_uint32 num_enctypes,
438 OM_uint32 maj_status;
439 gss_buffer_desc buffer;
444 sp = krb5_storage_emem();
446 *minor_status = ENOMEM;
447 maj_status = GSS_S_FAILURE;
451 for (i = 0; i < num_enctypes; i++) {
452 ret = krb5_store_int32(sp, enctypes[i]);
455 maj_status = GSS_S_FAILURE;
460 ret = krb5_storage_to_data(sp, &data);
463 maj_status = GSS_S_FAILURE;
467 buffer.value = data.data;
468 buffer.length = data.length;
470 maj_status = gss_set_cred_option(minor_status,
472 GSS_KRB5_SET_ALLOWABLE_ENCTYPES_X,
474 krb5_data_free(&data);
477 krb5_storage_free(sp);
485 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
486 gsskrb5_set_send_to_kdc(struct gsskrb5_send_to_kdc *c)
488 struct _gss_mech_switch *m;
489 gss_buffer_desc buffer;
496 buffer.length = sizeof(*c);
502 HEIM_SLIST_FOREACH(m, &_gss_mechs, gm_link) {
503 if (m->gm_mech.gm_set_sec_context_option == NULL)
505 m->gm_mech.gm_set_sec_context_option(&junk, NULL,
506 GSS_KRB5_SEND_TO_KDC_X, &buffer);
509 return (GSS_S_COMPLETE);
516 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
517 gss_krb5_ccache_name(OM_uint32 *minor_status,
519 const char **out_name)
521 struct _gss_mech_switch *m;
522 gss_buffer_desc buffer;
530 buffer.value = rk_UNCONST(name);
531 buffer.length = strlen(name);
533 HEIM_SLIST_FOREACH(m, &_gss_mechs, gm_link) {
534 if (m->gm_mech.gm_set_sec_context_option == NULL)
536 m->gm_mech.gm_set_sec_context_option(&junk, NULL,
537 GSS_KRB5_CCACHE_NAME_X, &buffer);
540 return (GSS_S_COMPLETE);
548 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
549 gsskrb5_extract_authtime_from_sec_context(OM_uint32 *minor_status,
550 gss_ctx_id_t context_handle,
553 gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
556 if (context_handle == GSS_C_NO_CONTEXT) {
557 *minor_status = EINVAL;
558 return GSS_S_FAILURE;
562 gss_inquire_sec_context_by_oid (minor_status,
564 GSS_KRB5_GET_AUTHTIME_X,
569 if (data_set == GSS_C_NO_BUFFER_SET) {
570 gss_release_buffer_set(minor_status, &data_set);
571 *minor_status = EINVAL;
572 return GSS_S_FAILURE;
575 if (data_set->count != 1) {
576 gss_release_buffer_set(minor_status, &data_set);
577 *minor_status = EINVAL;
578 return GSS_S_FAILURE;
581 if (data_set->elements[0].length != 4) {
582 gss_release_buffer_set(minor_status, &data_set);
583 *minor_status = EINVAL;
584 return GSS_S_FAILURE;
588 unsigned char *buf = data_set->elements[0].value;
589 *authtime = (buf[3] <<24) | (buf[2] << 16) |
590 (buf[1] << 8) | (buf[0] << 0);
593 gss_release_buffer_set(minor_status, &data_set);
596 return GSS_S_COMPLETE;
603 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
604 gsskrb5_extract_authz_data_from_sec_context(OM_uint32 *minor_status,
605 gss_ctx_id_t context_handle,
607 gss_buffer_t ad_data)
609 gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
611 gss_OID_desc oid_flat;
612 heim_oid baseoid, oid;
615 if (context_handle == GSS_C_NO_CONTEXT) {
616 *minor_status = EINVAL;
617 return GSS_S_FAILURE;
620 /* All this to append an integer to an oid... */
622 if (der_get_oid(GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X->elements,
623 GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X->length,
624 &baseoid, NULL) != 0) {
625 *minor_status = EINVAL;
626 return GSS_S_FAILURE;
629 oid.length = baseoid.length + 1;
630 oid.components = calloc(oid.length, sizeof(*oid.components));
631 if (oid.components == NULL) {
632 der_free_oid(&baseoid);
634 *minor_status = ENOMEM;
635 return GSS_S_FAILURE;
638 memcpy(oid.components, baseoid.components,
639 baseoid.length * sizeof(*baseoid.components));
641 der_free_oid(&baseoid);
643 oid.components[oid.length - 1] = ad_type;
645 oid_flat.length = der_length_oid(&oid);
646 oid_flat.elements = malloc(oid_flat.length);
647 if (oid_flat.elements == NULL) {
648 free(oid.components);
649 *minor_status = ENOMEM;
650 return GSS_S_FAILURE;
653 if (der_put_oid((unsigned char *)oid_flat.elements + oid_flat.length - 1,
654 oid_flat.length, &oid, &size) != 0) {
655 free(oid.components);
656 free(oid_flat.elements);
657 *minor_status = EINVAL;
658 return GSS_S_FAILURE;
660 if (oid_flat.length != size)
663 free(oid.components);
665 /* FINALLY, we have the OID */
667 maj_stat = gss_inquire_sec_context_by_oid (minor_status,
672 free(oid_flat.elements);
677 if (data_set == GSS_C_NO_BUFFER_SET || data_set->count != 1) {
678 gss_release_buffer_set(minor_status, &data_set);
679 *minor_status = EINVAL;
680 return GSS_S_FAILURE;
683 ad_data->value = malloc(data_set->elements[0].length);
684 if (ad_data->value == NULL) {
685 gss_release_buffer_set(minor_status, &data_set);
686 *minor_status = ENOMEM;
687 return GSS_S_FAILURE;
690 ad_data->length = data_set->elements[0].length;
691 memcpy(ad_data->value, data_set->elements[0].value, ad_data->length);
692 gss_release_buffer_set(minor_status, &data_set);
695 return GSS_S_COMPLETE;
703 gsskrb5_extract_key(OM_uint32 *minor_status,
704 gss_ctx_id_t context_handle,
706 krb5_keyblock **keyblock)
709 gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
710 OM_uint32 major_status;
711 krb5_context context = NULL;
712 krb5_storage *sp = NULL;
714 if (context_handle == GSS_C_NO_CONTEXT) {
715 *minor_status = EINVAL;
716 return GSS_S_FAILURE;
719 ret = krb5_init_context(&context);
722 return GSS_S_FAILURE;
726 gss_inquire_sec_context_by_oid (minor_status,
733 if (data_set == GSS_C_NO_BUFFER_SET || data_set->count != 1) {
734 gss_release_buffer_set(minor_status, &data_set);
735 *minor_status = EINVAL;
736 return GSS_S_FAILURE;
739 sp = krb5_storage_from_mem(data_set->elements[0].value,
740 data_set->elements[0].length);
746 *keyblock = calloc(1, sizeof(**keyblock));
747 if (keyblock == NULL) {
752 ret = krb5_ret_keyblock(sp, *keyblock);
755 gss_release_buffer_set(minor_status, &data_set);
757 krb5_storage_free(sp);
758 if (ret && keyblock) {
759 krb5_free_keyblock(context, *keyblock);
763 krb5_free_context(context);
767 return GSS_S_FAILURE;
769 return GSS_S_COMPLETE;
776 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
777 gsskrb5_extract_service_keyblock(OM_uint32 *minor_status,
778 gss_ctx_id_t context_handle,
779 krb5_keyblock **keyblock)
781 return gsskrb5_extract_key(minor_status,
783 GSS_KRB5_GET_SERVICE_KEYBLOCK_X,
787 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
788 gsskrb5_get_initiator_subkey(OM_uint32 *minor_status,
789 gss_ctx_id_t context_handle,
790 krb5_keyblock **keyblock)
792 return gsskrb5_extract_key(minor_status,
794 GSS_KRB5_GET_INITIATOR_SUBKEY_X,
798 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
799 gsskrb5_get_subkey(OM_uint32 *minor_status,
800 gss_ctx_id_t context_handle,
801 krb5_keyblock **keyblock)
803 return gsskrb5_extract_key(minor_status,
805 GSS_KRB5_GET_SUBKEY_X,
809 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
810 gsskrb5_set_default_realm(const char *realm)
812 struct _gss_mech_switch *m;
813 gss_buffer_desc buffer;
818 buffer.value = rk_UNCONST(realm);
819 buffer.length = strlen(realm);
821 HEIM_SLIST_FOREACH(m, &_gss_mechs, gm_link) {
822 if (m->gm_mech.gm_set_sec_context_option == NULL)
824 m->gm_mech.gm_set_sec_context_option(&junk, NULL,
825 GSS_KRB5_SET_DEFAULT_REALM_X, &buffer);
828 return (GSS_S_COMPLETE);
831 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
832 gss_krb5_get_tkt_flags(OM_uint32 *minor_status,
833 gss_ctx_id_t context_handle,
834 OM_uint32 *tkt_flags)
837 OM_uint32 major_status;
838 gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
840 if (context_handle == GSS_C_NO_CONTEXT) {
841 *minor_status = EINVAL;
842 return GSS_S_FAILURE;
846 gss_inquire_sec_context_by_oid (minor_status,
848 GSS_KRB5_GET_TKT_FLAGS_X,
853 if (data_set == GSS_C_NO_BUFFER_SET ||
854 data_set->count != 1 ||
855 data_set->elements[0].length < 4) {
856 gss_release_buffer_set(minor_status, &data_set);
857 *minor_status = EINVAL;
858 return GSS_S_FAILURE;
862 const u_char *p = data_set->elements[0].value;
863 *tkt_flags = (p[0] << 0) | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
866 gss_release_buffer_set(minor_status, &data_set);
867 return GSS_S_COMPLETE;
870 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
871 gsskrb5_set_time_offset(int offset)
873 struct _gss_mech_switch *m;
874 gss_buffer_desc buffer;
881 buffer.length = sizeof(o);
883 HEIM_SLIST_FOREACH(m, &_gss_mechs, gm_link) {
884 if (m->gm_mech.gm_set_sec_context_option == NULL)
886 m->gm_mech.gm_set_sec_context_option(&junk, NULL,
887 GSS_KRB5_SET_TIME_OFFSET_X, &buffer);
890 return (GSS_S_COMPLETE);
893 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
894 gsskrb5_get_time_offset(int *offset)
896 struct _gss_mech_switch *m;
897 gss_buffer_desc buffer;
898 OM_uint32 maj_stat, junk;
904 buffer.length = sizeof(o);
906 HEIM_SLIST_FOREACH(m, &_gss_mechs, gm_link) {
907 if (m->gm_mech.gm_set_sec_context_option == NULL)
909 maj_stat = m->gm_mech.gm_set_sec_context_option(&junk, NULL,
910 GSS_KRB5_GET_TIME_OFFSET_X, &buffer);
912 if (maj_stat == GSS_S_COMPLETE) {
918 return (GSS_S_UNAVAILABLE);
921 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
922 gsskrb5_plugin_register(struct gsskrb5_krb5_plugin *c)
924 struct _gss_mech_switch *m;
925 gss_buffer_desc buffer;
931 buffer.length = sizeof(*c);
933 HEIM_SLIST_FOREACH(m, &_gss_mechs, gm_link) {
934 if (m->gm_mech.gm_set_sec_context_option == NULL)
936 m->gm_mech.gm_set_sec_context_option(&junk, NULL,
937 GSS_KRB5_PLUGIN_REGISTER_X, &buffer);
940 return (GSS_S_COMPLETE);