r19604: This is a massive commit, and I appologise in advance for it's size.
[Samba.git] / source / heimdal / lib / gssapi / mech / gss_krb5.c
blobc6ea3cecb7fadf5e28b99936a3a651bebb73aa56
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 #include "krb5/gsskrb5_locl.h"
31 RCSID("$Id: gss_krb5.c,v 1.13 2006/10/20 22:05:02 lha Exp $");
33 #include <krb5.h>
34 #include <roken.h>
37 OM_uint32
38 gss_krb5_copy_ccache(OM_uint32 *minor_status,
39 gss_cred_id_t cred,
40 krb5_ccache out)
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;
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;
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;
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;
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 == -1) {
73 *minor_status = ENOMEM;
74 return GSS_S_FAILURE;
77 kret = krb5_cc_resolve(context, str, &id);
78 free(str);
79 if (kret) {
80 *minor_status = kret;
81 return GSS_S_FAILURE;
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;
92 return ret;
95 OM_uint32
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)
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;
110 *cred = GSS_C_NO_CREDENTIAL;
112 ret = krb5_init_context(&context);
113 if (ret) {
114 *minor_status = ret;
115 return GSS_S_FAILURE;
118 sp = krb5_storage_emem();
119 if (sp == NULL) {
120 *minor_status = ENOMEM;
121 major_status = GSS_S_FAILURE;
122 goto out;
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);
131 } else
132 ret = krb5_store_string(sp, "");
133 if (ret) {
134 *minor_status = ret;
135 major_status = GSS_S_FAILURE;
136 goto out;
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);
145 } else
146 krb5_store_string(sp, "");
147 if (ret) {
148 *minor_status = ret;
149 major_status = GSS_S_FAILURE;
150 goto out;
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);
160 } else
161 krb5_store_string(sp, "");
162 if (ret) {
163 *minor_status = ret;
164 major_status = GSS_S_FAILURE;
165 goto out;
168 krb5_storage_to_data(sp, &data);
170 buffer.value = data.data;
171 buffer.length = data.length;
173 major_status = gss_set_cred_option(minor_status,
174 cred,
175 GSS_KRB5_IMPORT_CRED_X,
176 &buffer);
177 krb5_data_free(&data);
178 out:
179 if (sp)
180 krb5_storage_free(sp);
181 krb5_free_context(context);
182 return major_status;
185 OM_uint32
186 gsskrb5_register_acceptor_identity(const char *identity)
188 struct _gss_mech_switch *m;
189 gss_buffer_desc buffer;
190 OM_uint32 junk;
192 _gss_load_mech();
194 buffer.value = rk_UNCONST(identity);
195 buffer.length = strlen(identity);
197 SLIST_FOREACH(m, &_gss_mechs, gm_link) {
198 if (m->gm_mech.gm_set_sec_context_option == NULL)
199 continue;
200 m->gm_mech.gm_set_sec_context_option(&junk, NULL,
201 GSS_KRB5_REGISTER_ACCEPTOR_IDENTITY_X, &buffer);
204 return (GSS_S_COMPLETE);
207 OM_uint32
208 gsskrb5_set_dns_canonicalize(int flag)
210 struct _gss_mech_switch *m;
211 gss_buffer_desc buffer;
212 OM_uint32 junk;
213 char b = (flag != 0);
215 _gss_load_mech();
217 buffer.value = &b;
218 buffer.length = sizeof(b);
220 SLIST_FOREACH(m, &_gss_mechs, gm_link) {
221 if (m->gm_mech.gm_set_sec_context_option == NULL)
222 continue;
223 m->gm_mech.gm_set_sec_context_option(&junk, NULL,
224 GSS_KRB5_SET_DNS_CANONICALIZE_X, &buffer);
227 return (GSS_S_COMPLETE);
232 static krb5_error_code
233 set_key(krb5_keyblock *keyblock, gss_krb5_lucid_key_t *key)
235 key->type = keyblock->keytype;
236 key->length = keyblock->keyvalue.length;
237 key->data = malloc(key->length);
238 if (key->data == NULL && key->length != 0)
239 return ENOMEM;
240 memcpy(key->data, keyblock->keyvalue.data, key->length);
241 return 0;
244 static void
245 free_key(gss_krb5_lucid_key_t *key)
247 memset(key->data, 0, key->length);
248 free(key->data);
249 memset(key, 0, sizeof(*key));
253 OM_uint32
254 gss_krb5_export_lucid_sec_context(OM_uint32 *minor_status,
255 gss_ctx_id_t *context_handle,
256 OM_uint32 version,
257 void **rctx)
259 krb5_context context = NULL;
260 krb5_error_code ret;
261 gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
262 OM_uint32 major_status;
263 gss_krb5_lucid_context_v1_t *ctx = NULL;
264 krb5_storage *sp = NULL;
265 uint32_t num;
267 if (context_handle == NULL || *context_handle == GSS_C_NO_CONTEXT || version != 1) {
268 ret = EINVAL;
269 return GSS_S_FAILURE;
272 major_status =
273 gss_inquire_sec_context_by_oid (minor_status,
274 *context_handle,
275 GSS_KRB5_EXPORT_LUCID_CONTEXT_V1_X,
276 &data_set);
277 if (major_status)
278 return major_status;
280 if (data_set == GSS_C_NO_BUFFER_SET || data_set->count != 1) {
281 gss_release_buffer_set(minor_status, &data_set);
282 *minor_status = EINVAL;
283 return GSS_S_FAILURE;
286 ret = krb5_init_context(&context);
287 if (ret)
288 goto out;
290 ctx = calloc(1, sizeof(*ctx));
291 if (ctx == NULL) {
292 ret = ENOMEM;
293 goto out;
296 sp = krb5_storage_from_mem(data_set->elements[0].value,
297 data_set->elements[0].length);
298 if (sp == NULL) {
299 ret = ENOMEM;
300 goto out;
303 ret = krb5_ret_uint32(sp, &num);
304 if (ret) goto out;
305 if (num != 1) {
306 ret = EINVAL;
307 goto out;
309 ctx->version = 1;
310 /* initiator */
311 ret = krb5_ret_uint32(sp, &ctx->initiate);
312 if (ret) goto out;
313 /* endtime */
314 ret = krb5_ret_uint32(sp, &ctx->endtime);
315 if (ret) goto out;
316 /* send_seq */
317 ret = krb5_ret_uint32(sp, &num);
318 if (ret) goto out;
319 ctx->send_seq = ((uint64_t)num) << 32;
320 ret = krb5_ret_uint32(sp, &num);
321 if (ret) goto out;
322 ctx->send_seq |= num;
323 /* recv_seq */
324 ret = krb5_ret_uint32(sp, &num);
325 if (ret) goto out;
326 ctx->recv_seq = ((uint64_t)num) << 32;
327 ret = krb5_ret_uint32(sp, &num);
328 if (ret) goto out;
329 ctx->recv_seq |= num;
330 /* protocol */
331 ret = krb5_ret_uint32(sp, &ctx->protocol);
332 if (ret) goto out;
333 if (ctx->protocol == 0) {
334 krb5_keyblock key;
336 /* sign_alg */
337 ret = krb5_ret_uint32(sp, &ctx->rfc1964_kd.sign_alg);
338 if (ret) goto out;
339 /* seal_alg */
340 ret = krb5_ret_uint32(sp, &ctx->rfc1964_kd.seal_alg);
341 if (ret) goto out;
342 /* ctx_key */
343 ret = krb5_ret_keyblock(sp, &key);
344 if (ret) goto out;
345 ret = set_key(&key, &ctx->rfc1964_kd.ctx_key);
346 krb5_free_keyblock_contents(context, &key);
347 if (ret) goto out;
348 } else if (ctx->protocol == 1) {
349 krb5_keyblock key;
351 /* acceptor_subkey */
352 ret = krb5_ret_uint32(sp, &ctx->cfx_kd.have_acceptor_subkey);
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->cfx_kd.ctx_key);
358 krb5_free_keyblock_contents(context, &key);
359 if (ret) goto out;
360 /* acceptor_subkey */
361 if (ctx->cfx_kd.have_acceptor_subkey) {
362 ret = krb5_ret_keyblock(sp, &key);
363 if (ret) goto out;
364 ret = set_key(&key, &ctx->cfx_kd.acceptor_subkey);
365 krb5_free_keyblock_contents(context, &key);
366 if (ret) goto out;
368 } else {
369 ret = EINVAL;
370 goto out;
373 *rctx = ctx;
375 out:
376 gss_release_buffer_set(minor_status, &data_set);
377 if (sp)
378 krb5_storage_free(sp);
379 if (context)
380 krb5_free_context(context);
382 if (ret) {
383 if (ctx)
384 gss_krb5_free_lucid_sec_context(NULL, ctx);
386 *minor_status = ret;
387 return GSS_S_FAILURE;
389 *minor_status = 0;
390 return GSS_S_COMPLETE;
393 OM_uint32
394 gss_krb5_free_lucid_sec_context(OM_uint32 *minor_status, void *c)
396 gss_krb5_lucid_context_v1_t *ctx = c;
398 if (ctx->version != 1) {
399 if (minor_status)
400 *minor_status = 0;
401 return GSS_S_FAILURE;
404 if (ctx->protocol == 0) {
405 free_key(&ctx->rfc1964_kd.ctx_key);
406 } else if (ctx->protocol == 1) {
407 free_key(&ctx->cfx_kd.ctx_key);
408 if (ctx->cfx_kd.have_acceptor_subkey)
409 free_key(&ctx->cfx_kd.acceptor_subkey);
411 free(ctx);
412 if (minor_status)
413 *minor_status = 0;
414 return GSS_S_COMPLETE;
417 OM_uint32
418 gsskrb5_set_send_to_kdc(struct gsskrb5_send_to_kdc *c)
420 struct _gss_mech_switch *m;
421 gss_buffer_desc buffer;
422 OM_uint32 junk;
424 _gss_load_mech();
426 if (c) {
427 buffer.value = c;
428 buffer.length = sizeof(*c);
429 } else {
430 buffer.value = NULL;
431 buffer.length = 0;
434 SLIST_FOREACH(m, &_gss_mechs, gm_link) {
435 if (m->gm_mech.gm_set_sec_context_option == NULL)
436 continue;
437 m->gm_mech.gm_set_sec_context_option(&junk, NULL,
438 GSS_KRB5_SEND_TO_KDC_X, &buffer);
441 return (GSS_S_COMPLETE);
444 OM_uint32
445 gsskrb5_extract_authtime_from_sec_context(OM_uint32 *minor_status,
446 gss_ctx_id_t context_handle,
447 time_t *authtime)
449 gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
450 OM_uint32 maj_stat;
451 krb5_error_code ret;
452 OM_uint32 time32;
454 if (context_handle == GSS_C_NO_CONTEXT) {
455 _gsskrb5_set_status("no context handle");
456 *minor_status = EINVAL;
457 return GSS_S_FAILURE;
460 maj_stat =
461 gss_inquire_sec_context_by_oid (minor_status,
462 context_handle,
463 GSS_KRB5_GET_AUTHTIME_X,
464 &data_set);
465 if (maj_stat)
466 return maj_stat;
468 if (data_set == GSS_C_NO_BUFFER_SET) {
469 _gsskrb5_set_status("no buffers returned");
470 gss_release_buffer_set(minor_status, &data_set);
471 *minor_status = EINVAL;
472 return GSS_S_FAILURE;
475 if (data_set->count != 1) {
476 _gsskrb5_set_status("%d != 1 buffers returned", data_set->count);
477 gss_release_buffer_set(minor_status, &data_set);
478 *minor_status = EINVAL;
479 return GSS_S_FAILURE;
482 if (data_set->elements[0].length != 4) {
483 gss_release_buffer_set(minor_status, &data_set);
484 _gsskrb5_set_status("Error extracting authtime from security context: only got %d < 4 bytes",
485 data_set->elements[0].length);
486 *minor_status = EINVAL;
487 return GSS_S_FAILURE;
490 ret = _gsskrb5_decode_om_uint32(data_set->elements[0].value, &time32);
491 if (ret) {
492 gss_release_buffer_set(minor_status, &data_set);
493 *minor_status = ret;
494 return GSS_S_FAILURE;
496 *authtime = time32;
498 gss_release_buffer_set(minor_status, &data_set);
500 *minor_status = 0;
501 return GSS_S_COMPLETE;
504 OM_uint32
505 gsskrb5_extract_authz_data_from_sec_context(OM_uint32 *minor_status,
506 gss_ctx_id_t context_handle,
507 int ad_type,
508 gss_buffer_t ad_data)
510 gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
511 OM_uint32 maj_stat;
512 gss_OID_desc authz_oid_flat;
513 heim_oid authz_oid;
514 heim_oid new_authz_oid;
515 size_t size;
517 if (context_handle == GSS_C_NO_CONTEXT) {
518 *minor_status = EINVAL;
519 return GSS_S_FAILURE;
522 /* All this to append an integer to an oid... */
524 if (der_get_oid(GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X->elements,
525 GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X->length,
526 &authz_oid, NULL) != 0) {
527 *minor_status = EINVAL;
528 return GSS_S_FAILURE;
531 new_authz_oid.length = authz_oid.length + 1;
532 new_authz_oid.components = malloc(new_authz_oid.length * sizeof(*new_authz_oid.components));
533 if (!new_authz_oid.components) {
534 free(authz_oid.components);
536 *minor_status = ENOMEM;
537 return GSS_S_FAILURE;
540 memcpy(new_authz_oid.components, authz_oid.components,
541 authz_oid.length * sizeof(*authz_oid.components));
543 free(authz_oid.components);
545 new_authz_oid.components[new_authz_oid.length - 1] = ad_type;
547 authz_oid_flat.length = der_length_oid(&new_authz_oid);
548 authz_oid_flat.elements = malloc(authz_oid_flat.length);
550 if (!authz_oid_flat.elements) {
551 free(new_authz_oid.components);
553 *minor_status = ENOMEM;
554 return GSS_S_FAILURE;
557 if (der_put_oid((unsigned char *)authz_oid_flat.elements + authz_oid_flat.length - 1,
558 authz_oid_flat.length,
559 &new_authz_oid, &size) != 0) {
560 free(new_authz_oid.components);
562 *minor_status = EINVAL;
563 return GSS_S_FAILURE;
566 free(new_authz_oid.components);
568 /* FINALLY, we have the OID */
570 maj_stat =
571 gss_inquire_sec_context_by_oid (minor_status,
572 context_handle,
573 &authz_oid_flat,
574 &data_set);
576 free(authz_oid_flat.elements);
578 if (maj_stat)
579 return maj_stat;
581 if (data_set == GSS_C_NO_BUFFER_SET || data_set->count != 1) {
582 gss_release_buffer_set(minor_status, &data_set);
583 *minor_status = EINVAL;
584 return GSS_S_FAILURE;
587 ad_data->value = malloc(data_set->elements[0].length);
588 if (ad_data->value == NULL) {
589 gss_release_buffer_set(minor_status, &data_set);
590 *minor_status = ENOMEM;
591 return GSS_S_FAILURE;
594 ad_data->length = data_set->elements[0].length;
595 memcpy(ad_data->value, data_set->elements[0].value, ad_data->length);
596 gss_release_buffer_set(minor_status, &data_set);
598 *minor_status = 0;
599 return GSS_S_COMPLETE;
602 static OM_uint32
603 gsskrb5_extract_key(OM_uint32 *minor_status,
604 gss_ctx_id_t context_handle,
605 const gss_OID oid,
606 krb5_keyblock **keyblock)
608 krb5_error_code ret;
609 gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
610 OM_uint32 major_status;
611 krb5_storage *sp = NULL;
613 ret = _gsskrb5_init();
614 if(ret) {
615 *minor_status = ret;
616 return GSS_S_FAILURE;
619 if (context_handle == GSS_C_NO_CONTEXT) {
620 _gsskrb5_set_status("no context handle");
621 *minor_status = EINVAL;
622 return GSS_S_FAILURE;
625 major_status =
626 gss_inquire_sec_context_by_oid (minor_status,
627 context_handle,
628 oid,
629 &data_set);
630 if (major_status)
631 return major_status;
633 if (data_set == GSS_C_NO_BUFFER_SET) {
634 _gsskrb5_set_status("no buffers returned");
635 gss_release_buffer_set(minor_status, &data_set);
636 *minor_status = EINVAL;
637 return GSS_S_FAILURE;
640 if (data_set->count != 1) {
641 _gsskrb5_set_status("%d != 1 buffers returned", data_set->count);
642 gss_release_buffer_set(minor_status, &data_set);
643 *minor_status = EINVAL;
644 return GSS_S_FAILURE;
647 sp = krb5_storage_from_mem(data_set->elements[0].value,
648 data_set->elements[0].length);
649 if (sp == NULL) {
650 ret = ENOMEM;
651 goto out;
654 *keyblock = calloc(1, sizeof(**keyblock));
655 if (keyblock == NULL) {
656 ret = ENOMEM;
657 goto out;
660 ret = krb5_ret_keyblock(sp, *keyblock);
662 out:
663 gss_release_buffer_set(minor_status, &data_set);
664 if (sp)
665 krb5_storage_free(sp);
666 if (ret) {
667 _gsskrb5_set_error_string();
668 if (keyblock) {
669 krb5_free_keyblock(_gsskrb5_context, *keyblock);
672 *minor_status = ret;
673 return GSS_S_FAILURE;
675 *minor_status = 0;
676 return GSS_S_COMPLETE;
679 OM_uint32
680 gsskrb5_extract_service_keyblock(OM_uint32 *minor_status,
681 gss_ctx_id_t context_handle,
682 krb5_keyblock **keyblock)
684 return gsskrb5_extract_key(minor_status,
685 context_handle,
686 GSS_KRB5_GET_SERVICE_KEYBLOCK_X,
687 keyblock);
690 OM_uint32
691 gsskrb5_get_initiator_subkey(OM_uint32 *minor_status,
692 gss_ctx_id_t context_handle,
693 krb5_keyblock **keyblock)
695 return gsskrb5_extract_key(minor_status,
696 context_handle,
697 GSS_KRB5_GET_INITIATOR_SUBKEY_X,
698 keyblock);
701 OM_uint32
702 gsskrb5_get_subkey(OM_uint32 *minor_status,
703 gss_ctx_id_t context_handle,
704 krb5_keyblock **keyblock)
706 return gsskrb5_extract_key(minor_status,
707 context_handle,
708 GSS_KRB5_GET_ACCEPTOR_SUBKEY_X,
709 keyblock);