r19633: Merge to lorikeet-heimdal, removing krb5_rd_req_return_keyblock in favour...
[samba.git] / source4 / heimdal / lib / gssapi / mech / gss_krb5.c
1 /*-
2  * Copyright (c) 2005 Doug Rabson
3  * All rights reserved.
4  *
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.
13  *
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.
25  *
26  *      $FreeBSD: src/lib/libgssapi/gss_krb5.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
27  */
28
29 #include "mech_locl.h"
30 RCSID("$Id: gss_krb5.c,v 1.16 2006/11/07 14:41:35 lha Exp $");
31
32 #include <krb5.h>
33 #include <roken.h>
34 #include "krb5/gsskrb5_locl.h"
35
36 OM_uint32
37 gss_krb5_copy_ccache(OM_uint32 *minor_status,
38                      gss_cred_id_t cred,
39                      krb5_ccache out)
40 {
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;
47
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;
54
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;
59     }
60
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;
66     }
67
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;
74     }
75
76     kret = krb5_cc_resolve(context, str, &id);
77     free(str);
78     if (kret) {
79         *minor_status = kret;
80         return GSS_S_FAILURE;
81     }
82
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;
89     }
90
91     return ret;
92 }
93
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)
100 {
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;
108
109     *cred = GSS_C_NO_CREDENTIAL;
110
111     ret = krb5_init_context(&context);
112     if (ret) {
113         *minor_status = ret;
114         return GSS_S_FAILURE;
115     }
116
117     sp = krb5_storage_emem();
118     if (sp == NULL) {
119         *minor_status = ENOMEM;
120         major_status = GSS_S_FAILURE;
121         goto out;
122     }
123
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);
129         }
130     } else
131         ret = krb5_store_string(sp, "");
132     if (ret) {
133         *minor_status = ret;
134         major_status = GSS_S_FAILURE;
135         goto out;
136     }
137
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);
143         }
144     } else
145         krb5_store_string(sp, "");
146     if (ret) {
147         *minor_status = ret;
148         major_status = GSS_S_FAILURE;
149         goto out;
150     }
151
152
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);
158         }
159     } else
160         krb5_store_string(sp, "");
161     if (ret) {
162         *minor_status = ret;
163         major_status = GSS_S_FAILURE;
164         goto out;
165     }
166
167     krb5_storage_to_data(sp, &data);
168
169     buffer.value = data.data;
170     buffer.length = data.length;
171     
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;
182 }
183
184 OM_uint32
185 gsskrb5_register_acceptor_identity(const char *identity)
186 {
187         struct _gss_mech_switch *m;
188         gss_buffer_desc buffer;
189         OM_uint32 junk;
190
191         _gss_load_mech();
192
193         buffer.value = rk_UNCONST(identity);
194         buffer.length = strlen(identity);
195
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);
201         }
202
203         return (GSS_S_COMPLETE);
204 }
205
206 OM_uint32
207 gsskrb5_set_dns_canonicalize(int flag)
208 {
209         struct _gss_mech_switch *m;
210         gss_buffer_desc buffer;
211         OM_uint32 junk;
212         char b = (flag != 0);
213
214         _gss_load_mech();
215
216         buffer.value = &b;
217         buffer.length = sizeof(b);
218
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);
224         }
225
226         return (GSS_S_COMPLETE);
227 }
228
229
230
231 static krb5_error_code
232 set_key(krb5_keyblock *keyblock, gss_krb5_lucid_key_t *key)
233 {
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;
241 }
242
243 static void
244 free_key(gss_krb5_lucid_key_t *key)
245 {
246     memset(key->data, 0, key->length);
247     free(key->data);
248     memset(key, 0, sizeof(*key));
249 }
250
251
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)
257 {
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;
265
266     if (context_handle == NULL
267         || *context_handle == GSS_C_NO_CONTEXT
268         || version != 1)
269     {
270         ret = EINVAL;
271         return GSS_S_FAILURE;
272     }
273     
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;
281     
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;
286     }
287
288     ret = krb5_init_context(&context);
289     if (ret)
290         goto out;
291
292     ctx = calloc(1, sizeof(*ctx));
293     if (ctx == NULL) {
294         ret = ENOMEM;
295         goto out;
296     }
297
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;
303     }
304     
305     ret = krb5_ret_uint32(sp, &num);
306     if (ret) goto out;
307     if (num != 1) {
308         ret = EINVAL;
309         goto out;
310     }
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;
337
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;
352
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;
369         }
370     } else {
371         ret = EINVAL;
372         goto out;
373     }
374
375     *rctx = ctx;
376
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);
383
384     if (ret) {
385         if (ctx)
386             gss_krb5_free_lucid_sec_context(NULL, ctx);
387
388         *minor_status = ret;
389         return GSS_S_FAILURE;
390     }
391     *minor_status = 0;
392     return GSS_S_COMPLETE;
393 }
394
395 OM_uint32
396 gss_krb5_free_lucid_sec_context(OM_uint32 *minor_status, void *c)
397 {
398     gss_krb5_lucid_context_v1_t *ctx = c;
399
400     if (ctx->version != 1) {
401         if (minor_status)
402             *minor_status = 0;
403         return GSS_S_FAILURE;
404     }
405
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);
412     }
413     free(ctx);
414     if (minor_status)
415         *minor_status = 0;
416     return GSS_S_COMPLETE;
417 }
418
419 OM_uint32
420 gsskrb5_set_send_to_kdc(struct gsskrb5_send_to_kdc *c)
421 {
422     struct _gss_mech_switch *m;
423     gss_buffer_desc buffer;
424     OM_uint32 junk;
425
426     _gss_load_mech();
427
428     if (c) {
429         buffer.value = c;
430         buffer.length = sizeof(*c);
431     } else {
432         buffer.value = NULL;
433         buffer.length = 0;
434     }
435
436     SLIST_FOREACH(m, &_gss_mechs, gm_link) {
437         if (m->gm_mech.gm_set_sec_context_option == NULL)
438             continue;
439         m->gm_mech.gm_set_sec_context_option(&junk, NULL,
440             GSS_KRB5_SEND_TO_KDC_X, &buffer);
441     }
442
443     return (GSS_S_COMPLETE);
444 }
445
446 OM_uint32
447 gsskrb5_extract_authtime_from_sec_context(OM_uint32 *minor_status,
448                                           gss_ctx_id_t context_handle,
449                                           time_t *authtime)
450 {
451     gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
452     OM_uint32 maj_stat;
453     krb5_error_code ret;
454     OM_uint32 time32;
455
456     if (context_handle == GSS_C_NO_CONTEXT) {
457         _gsskrb5_set_status("no context handle");
458         *minor_status = EINVAL;
459         return GSS_S_FAILURE;
460     }
461     
462     maj_stat =
463         gss_inquire_sec_context_by_oid (minor_status,
464                                         context_handle,
465                                         GSS_KRB5_GET_AUTHTIME_X,
466                                         &data_set);
467     if (maj_stat)
468         return maj_stat;
469     
470     if (data_set == GSS_C_NO_BUFFER_SET) {
471         _gsskrb5_set_status("no buffers returned");
472         gss_release_buffer_set(minor_status, &data_set);
473         *minor_status = EINVAL;
474         return GSS_S_FAILURE;
475     }
476
477     if (data_set->count != 1) {
478         _gsskrb5_set_status("%d != 1 buffers returned", data_set->count);
479         gss_release_buffer_set(minor_status, &data_set);
480         *minor_status = EINVAL;
481         return GSS_S_FAILURE;
482     }
483
484     if (data_set->elements[0].length != 4) {
485         gss_release_buffer_set(minor_status, &data_set);
486         _gsskrb5_set_status("Error extracting authtime from security context: only got %d < 4 bytes",
487                             data_set->elements[0].length);
488         *minor_status = EINVAL;
489         return GSS_S_FAILURE;
490     }
491
492     ret = _gsskrb5_decode_om_uint32(data_set->elements[0].value, &time32);
493     if (ret) {
494         gss_release_buffer_set(minor_status, &data_set);
495         *minor_status = ret;
496         return GSS_S_FAILURE;
497     }
498     *authtime = time32;
499
500     gss_release_buffer_set(minor_status, &data_set);
501     
502     *minor_status = 0;
503     return GSS_S_COMPLETE;
504 }
505
506 OM_uint32
507 gsskrb5_extract_authz_data_from_sec_context(OM_uint32 *minor_status,
508                                             gss_ctx_id_t context_handle,
509                                             int ad_type,
510                                             gss_buffer_t ad_data)
511 {
512     gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
513     OM_uint32 maj_stat;
514     gss_OID_desc oid_flat;
515     heim_oid baseoid, oid;
516     size_t size;
517
518     if (context_handle == GSS_C_NO_CONTEXT) {
519         *minor_status = EINVAL;
520         return GSS_S_FAILURE;
521     }
522
523     /* All this to append an integer to an oid... */
524
525     if (der_get_oid(GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X->elements,
526                     GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X->length,
527                     &baseoid, NULL) != 0) {
528         *minor_status = EINVAL;
529         return GSS_S_FAILURE;
530     }
531     
532     oid.length = baseoid.length + 1;
533     oid.components = calloc(oid.length, sizeof(*oid.components));
534     if (oid.components == NULL) {
535         der_free_oid(&baseoid);
536
537         *minor_status = ENOMEM;
538         return GSS_S_FAILURE;
539     }
540
541     memcpy(oid.components, baseoid.components, 
542            baseoid.length * sizeof(*baseoid.components));
543     
544     der_free_oid(&baseoid);
545
546     oid.components[oid.length - 1] = ad_type;
547
548     oid_flat.length = der_length_oid(&oid);
549     oid_flat.elements = malloc(oid_flat.length);
550     if (oid_flat.elements == NULL) {
551         free(oid.components);
552         *minor_status = ENOMEM;
553         return GSS_S_FAILURE;
554     }
555
556     if (der_put_oid((unsigned char *)oid_flat.elements + oid_flat.length - 1, 
557                     oid_flat.length, &oid, &size) != 0) {
558         free(oid.components);
559
560         *minor_status = EINVAL;
561         return GSS_S_FAILURE;
562     }
563     if (oid_flat.length != size)
564         abort();
565
566     free(oid.components);
567
568     /* FINALLY, we have the OID */
569
570     maj_stat = gss_inquire_sec_context_by_oid (minor_status,
571                                                context_handle,
572                                                &oid_flat,
573                                                &data_set);
574
575     free(oid_flat.elements);
576
577     if (maj_stat)
578         return maj_stat;
579     
580     if (data_set == GSS_C_NO_BUFFER_SET || data_set->count != 1) {
581         gss_release_buffer_set(minor_status, &data_set);
582         *minor_status = EINVAL;
583         return GSS_S_FAILURE;
584     }
585
586     ad_data->value = malloc(data_set->elements[0].length);
587     if (ad_data->value == NULL) {
588         gss_release_buffer_set(minor_status, &data_set);
589         *minor_status = ENOMEM;
590         return GSS_S_FAILURE;
591     }
592
593     ad_data->length = data_set->elements[0].length;
594     memcpy(ad_data->value, data_set->elements[0].value, ad_data->length);
595     gss_release_buffer_set(minor_status, &data_set);
596     
597     *minor_status = 0;
598     return GSS_S_COMPLETE;
599 }
600
601 static OM_uint32
602 gsskrb5_extract_key(OM_uint32 *minor_status,
603                     gss_ctx_id_t context_handle,
604                     const gss_OID oid, 
605                     krb5_keyblock **keyblock)
606 {
607     krb5_error_code ret;
608     gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
609     OM_uint32 major_status;
610     krb5_context context = NULL;
611     krb5_storage *sp = NULL;
612
613     if (context_handle == GSS_C_NO_CONTEXT) {
614         ret = EINVAL;
615         return GSS_S_FAILURE;
616     }
617     
618     ret = krb5_init_context(&context);
619     if(ret) {
620         *minor_status = ret;
621         return GSS_S_FAILURE;
622     }
623
624     major_status =
625         gss_inquire_sec_context_by_oid (minor_status,
626                                         context_handle,
627                                         oid,
628                                         &data_set);
629     if (major_status)
630         return major_status;
631     
632     if (data_set == GSS_C_NO_BUFFER_SET || data_set->count != 1) {
633         gss_release_buffer_set(minor_status, &data_set);
634         *minor_status = EINVAL;
635         return GSS_S_FAILURE;
636     }
637
638     sp = krb5_storage_from_mem(data_set->elements[0].value,
639                                data_set->elements[0].length);
640     if (sp == NULL) {
641         ret = ENOMEM;
642         goto out;
643     }
644     
645     *keyblock = calloc(1, sizeof(**keyblock));
646     if (keyblock == NULL) {
647         ret = ENOMEM;
648         goto out;
649     }
650
651     ret = krb5_ret_keyblock(sp, *keyblock);
652
653 out: 
654     gss_release_buffer_set(minor_status, &data_set);
655     if (sp)
656         krb5_storage_free(sp);
657     if (ret && keyblock) {
658         krb5_free_keyblock(context, *keyblock);
659         *keyblock = NULL;
660     }
661     if (context)
662         krb5_free_context(context);
663
664     *minor_status = ret;
665     if (ret)
666         return GSS_S_FAILURE;
667
668     return GSS_S_COMPLETE;
669 }
670
671 OM_uint32
672 gsskrb5_extract_service_keyblock(OM_uint32 *minor_status,
673                                  gss_ctx_id_t context_handle,
674                                  krb5_keyblock **keyblock)
675 {
676     return gsskrb5_extract_key(minor_status,
677                                context_handle,
678                                GSS_KRB5_GET_SERVICE_KEYBLOCK_X,
679                                keyblock);
680 }
681
682 OM_uint32
683 gsskrb5_get_initiator_subkey(OM_uint32 *minor_status,
684                              gss_ctx_id_t context_handle,
685                              krb5_keyblock **keyblock)
686 {
687     return gsskrb5_extract_key(minor_status,
688                                context_handle,
689                                GSS_KRB5_GET_INITIATOR_SUBKEY_X,
690                                keyblock);
691 }
692
693 OM_uint32
694 gsskrb5_get_subkey(OM_uint32 *minor_status,
695                    gss_ctx_id_t context_handle,
696                    krb5_keyblock **keyblock)
697 {
698     return gsskrb5_extract_key(minor_status,
699                                context_handle,
700                                GSS_KRB5_GET_SUBKEY_X,
701                                keyblock);
702 }