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_TAILQ_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_secure_release_buffer_set(minor_status, &data_set);
388 krb5_storage_free(sp);
390 krb5_free_context(context);
395 gss_krb5_free_lucid_sec_context(&junk, ctx);
398 return GSS_S_FAILURE;
401 return GSS_S_COMPLETE;
404 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
405 gss_krb5_free_lucid_sec_context(OM_uint32 *minor_status, void *c)
407 gss_krb5_lucid_context_v1_t *ctx = c;
409 if (ctx->version != 1) {
412 return GSS_S_FAILURE;
415 if (ctx->protocol == 0) {
416 free_key(&ctx->rfc1964_kd.ctx_key);
417 } else if (ctx->protocol == 1) {
418 free_key(&ctx->cfx_kd.ctx_key);
419 if (ctx->cfx_kd.have_acceptor_subkey)
420 free_key(&ctx->cfx_kd.acceptor_subkey);
425 return GSS_S_COMPLETE;
432 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
433 gss_krb5_set_allowable_enctypes(OM_uint32 *minor_status,
435 OM_uint32 num_enctypes,
439 OM_uint32 maj_status;
440 gss_buffer_desc buffer;
445 sp = krb5_storage_emem();
447 *minor_status = ENOMEM;
448 maj_status = GSS_S_FAILURE;
452 for (i = 0; i < num_enctypes; i++) {
453 ret = krb5_store_int32(sp, enctypes[i]);
456 maj_status = GSS_S_FAILURE;
461 ret = krb5_storage_to_data(sp, &data);
464 maj_status = GSS_S_FAILURE;
468 buffer.value = data.data;
469 buffer.length = data.length;
471 maj_status = gss_set_cred_option(minor_status,
473 GSS_KRB5_SET_ALLOWABLE_ENCTYPES_X,
475 krb5_data_free(&data);
478 krb5_storage_free(sp);
486 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
487 gsskrb5_set_send_to_kdc(struct gsskrb5_send_to_kdc *c)
489 struct _gss_mech_switch *m;
490 gss_buffer_desc buffer;
497 buffer.length = sizeof(*c);
503 HEIM_TAILQ_FOREACH(m, &_gss_mechs, gm_link) {
504 if (m->gm_mech.gm_set_sec_context_option == NULL)
506 m->gm_mech.gm_set_sec_context_option(&junk, NULL,
507 GSS_KRB5_SEND_TO_KDC_X, &buffer);
510 return (GSS_S_COMPLETE);
517 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
518 gss_krb5_ccache_name(OM_uint32 *minor_status,
520 const char **out_name)
522 struct _gss_mech_switch *m;
523 gss_buffer_desc buffer;
532 buffer.value = rk_UNCONST(name);
533 buffer.length = strlen(name);
535 _mg_buffer_zero(&buffer);
538 HEIM_TAILQ_FOREACH(m, &_gss_mechs, gm_link) {
539 if (m->gm_mech.gm_set_sec_context_option == NULL)
541 m->gm_mech.gm_set_sec_context_option(&junk, NULL,
542 GSS_KRB5_CCACHE_NAME_X, &buffer);
545 return (GSS_S_COMPLETE);
553 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
554 gsskrb5_extract_authtime_from_sec_context(OM_uint32 *minor_status,
555 gss_ctx_id_t context_handle,
558 gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
561 if (context_handle == GSS_C_NO_CONTEXT) {
562 *minor_status = EINVAL;
563 return GSS_S_FAILURE;
567 gss_inquire_sec_context_by_oid (minor_status,
569 GSS_KRB5_GET_AUTHTIME_X,
574 if (data_set == GSS_C_NO_BUFFER_SET) {
575 gss_release_buffer_set(minor_status, &data_set);
576 *minor_status = EINVAL;
577 return GSS_S_FAILURE;
580 if (data_set->count != 1) {
581 gss_release_buffer_set(minor_status, &data_set);
582 *minor_status = EINVAL;
583 return GSS_S_FAILURE;
586 if (data_set->elements[0].length != 4) {
587 gss_release_buffer_set(minor_status, &data_set);
588 *minor_status = EINVAL;
589 return GSS_S_FAILURE;
593 unsigned char *buf = data_set->elements[0].value;
594 *authtime = (buf[3] <<24) | (buf[2] << 16) |
595 (buf[1] << 8) | (buf[0] << 0);
598 gss_release_buffer_set(minor_status, &data_set);
601 return GSS_S_COMPLETE;
608 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
609 gsskrb5_extract_authz_data_from_sec_context(OM_uint32 *minor_status,
610 gss_ctx_id_t context_handle,
612 gss_buffer_t ad_data)
614 gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
615 OM_uint32 maj_stat, tmp;
616 gss_OID_desc oid_flat;
617 heim_oid baseoid, oid;
620 if (context_handle == GSS_C_NO_CONTEXT) {
621 *minor_status = EINVAL;
622 return GSS_S_FAILURE;
625 /* All this to append an integer to an oid... */
627 if (der_get_oid(GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X->elements,
628 GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X->length,
629 &baseoid, NULL) != 0) {
630 *minor_status = EINVAL;
631 return GSS_S_FAILURE;
634 oid.length = baseoid.length + 1;
635 oid.components = calloc(oid.length, sizeof(*oid.components));
636 if (oid.components == NULL) {
637 der_free_oid(&baseoid);
639 *minor_status = ENOMEM;
640 return GSS_S_FAILURE;
643 memcpy(oid.components, baseoid.components,
644 baseoid.length * sizeof(*baseoid.components));
646 der_free_oid(&baseoid);
648 oid.components[oid.length - 1] = ad_type;
650 oid_flat.length = (OM_uint32)der_length_oid(&oid);
651 oid_flat.elements = malloc(oid_flat.length);
652 if (oid_flat.elements == NULL) {
653 free(oid.components);
654 *minor_status = ENOMEM;
655 return GSS_S_FAILURE;
658 if (der_put_oid((unsigned char *)oid_flat.elements + oid_flat.length - 1,
659 oid_flat.length, &oid, &size) != 0) {
660 free(oid.components);
661 _gss_free_oid(&tmp, &oid_flat);
662 *minor_status = EINVAL;
663 return GSS_S_FAILURE;
665 if (oid_flat.length != size)
668 free(oid.components);
670 /* FINALLY, we have the OID */
672 maj_stat = gss_inquire_sec_context_by_oid (minor_status,
677 _gss_free_oid(&tmp, &oid_flat);
682 if (data_set == GSS_C_NO_BUFFER_SET || data_set->count != 1) {
683 gss_release_buffer_set(minor_status, &data_set);
684 *minor_status = EINVAL;
685 return GSS_S_FAILURE;
688 ad_data->value = malloc(data_set->elements[0].length);
689 if (ad_data->value == NULL) {
690 gss_release_buffer_set(minor_status, &data_set);
691 *minor_status = ENOMEM;
692 return GSS_S_FAILURE;
695 ad_data->length = data_set->elements[0].length;
696 memcpy(ad_data->value, data_set->elements[0].value, ad_data->length);
697 gss_release_buffer_set(minor_status, &data_set);
700 return GSS_S_COMPLETE;
708 gsskrb5_extract_key(OM_uint32 *minor_status,
709 gss_ctx_id_t context_handle,
711 krb5_keyblock **keyblock)
714 gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
715 OM_uint32 major_status;
716 krb5_context context = NULL;
717 krb5_storage *sp = NULL;
719 if (context_handle == GSS_C_NO_CONTEXT) {
720 *minor_status = EINVAL;
721 return GSS_S_FAILURE;
724 ret = krb5_init_context(&context);
727 return GSS_S_FAILURE;
731 gss_inquire_sec_context_by_oid (minor_status,
738 if (data_set == GSS_C_NO_BUFFER_SET || data_set->count != 1) {
739 _gss_secure_release_buffer_set(minor_status, &data_set);
740 *minor_status = EINVAL;
741 return GSS_S_FAILURE;
744 sp = krb5_storage_from_mem(data_set->elements[0].value,
745 data_set->elements[0].length);
751 *keyblock = calloc(1, sizeof(**keyblock));
752 if (keyblock == NULL) {
757 ret = krb5_ret_keyblock(sp, *keyblock);
760 _gss_secure_release_buffer_set(minor_status, &data_set);
762 krb5_storage_free(sp);
763 if (ret && keyblock) {
764 krb5_free_keyblock(context, *keyblock);
768 krb5_free_context(context);
772 return GSS_S_FAILURE;
774 return GSS_S_COMPLETE;
781 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
782 gsskrb5_extract_service_keyblock(OM_uint32 *minor_status,
783 gss_ctx_id_t context_handle,
784 krb5_keyblock **keyblock)
786 return gsskrb5_extract_key(minor_status,
788 GSS_KRB5_GET_SERVICE_KEYBLOCK_X,
792 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
793 gsskrb5_get_initiator_subkey(OM_uint32 *minor_status,
794 gss_ctx_id_t context_handle,
795 krb5_keyblock **keyblock)
797 return gsskrb5_extract_key(minor_status,
799 GSS_KRB5_GET_INITIATOR_SUBKEY_X,
803 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
804 gsskrb5_get_subkey(OM_uint32 *minor_status,
805 gss_ctx_id_t context_handle,
806 krb5_keyblock **keyblock)
808 return gsskrb5_extract_key(minor_status,
810 GSS_KRB5_GET_SUBKEY_X,
814 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
815 gsskrb5_set_default_realm(const char *realm)
817 struct _gss_mech_switch *m;
818 gss_buffer_desc buffer;
823 buffer.value = rk_UNCONST(realm);
824 buffer.length = strlen(realm);
826 HEIM_TAILQ_FOREACH(m, &_gss_mechs, gm_link) {
827 if (m->gm_mech.gm_set_sec_context_option == NULL)
829 m->gm_mech.gm_set_sec_context_option(&junk, NULL,
830 GSS_KRB5_SET_DEFAULT_REALM_X, &buffer);
833 return (GSS_S_COMPLETE);
836 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
837 gss_krb5_get_tkt_flags(OM_uint32 *minor_status,
838 gss_ctx_id_t context_handle,
839 OM_uint32 *tkt_flags)
842 OM_uint32 major_status;
843 gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
845 if (context_handle == GSS_C_NO_CONTEXT) {
846 *minor_status = EINVAL;
847 return GSS_S_FAILURE;
851 gss_inquire_sec_context_by_oid (minor_status,
853 GSS_KRB5_GET_TKT_FLAGS_X,
858 if (data_set == GSS_C_NO_BUFFER_SET ||
859 data_set->count != 1 ||
860 data_set->elements[0].length < 4) {
861 gss_release_buffer_set(minor_status, &data_set);
862 *minor_status = EINVAL;
863 return GSS_S_FAILURE;
867 const u_char *p = data_set->elements[0].value;
868 *tkt_flags = (p[0] << 0) | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
871 gss_release_buffer_set(minor_status, &data_set);
872 return GSS_S_COMPLETE;
875 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
876 gsskrb5_set_time_offset(int offset)
878 struct _gss_mech_switch *m;
879 gss_buffer_desc buffer;
886 buffer.length = sizeof(o);
888 HEIM_TAILQ_FOREACH(m, &_gss_mechs, gm_link) {
889 if (m->gm_mech.gm_set_sec_context_option == NULL)
891 m->gm_mech.gm_set_sec_context_option(&junk, NULL,
892 GSS_KRB5_SET_TIME_OFFSET_X, &buffer);
895 return (GSS_S_COMPLETE);
898 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
899 gsskrb5_get_time_offset(int *offset)
901 struct _gss_mech_switch *m;
902 gss_buffer_desc buffer;
903 OM_uint32 maj_stat, junk;
909 buffer.length = sizeof(o);
911 HEIM_TAILQ_FOREACH(m, &_gss_mechs, gm_link) {
912 if (m->gm_mech.gm_set_sec_context_option == NULL)
914 maj_stat = m->gm_mech.gm_set_sec_context_option(&junk, NULL,
915 GSS_KRB5_GET_TIME_OFFSET_X, &buffer);
917 if (maj_stat == GSS_S_COMPLETE) {
923 return (GSS_S_UNAVAILABLE);
926 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
927 gsskrb5_plugin_register(struct gsskrb5_krb5_plugin *c)
929 struct _gss_mech_switch *m;
930 gss_buffer_desc buffer;
936 buffer.length = sizeof(*c);
938 HEIM_TAILQ_FOREACH(m, &_gss_mechs, gm_link) {
939 if (m->gm_mech.gm_set_sec_context_option == NULL)
941 m->gm_mech.gm_set_sec_context_option(&junk, NULL,
942 GSS_KRB5_PLUGIN_REGISTER_X, &buffer);
945 return (GSS_S_COMPLETE);