1/*
2 * Copyright (c) 2005, PADL Software Pty Ltd.
3 * All rights reserved.
4 *
5 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * 3. Neither the name of PADL Software nor the names of its contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#include "krb5_locl.h"
36
37#ifdef HAVE_KCM
38/*
39 * Client library for Kerberos Credentials Manager (KCM) daemon
40 */
41
42#include "kcm.h"
43#include <heim-ipc.h>
44
45static krb5_error_code
46kcm_set_kdc_offset(krb5_context, krb5_ccache, krb5_deltat);
47
48static const char *kcm_ipc_name = "ANY:org.h5l.kcm";
49
50typedef struct krb5_kcmcache {
51    char *name;
52} krb5_kcmcache;
53
54typedef struct krb5_kcm_cursor {
55    unsigned long offset;
56    unsigned long length;
57    kcmuuid_t *uuids;
58} *krb5_kcm_cursor;
59
60
61#define KCMCACHE(X)	((krb5_kcmcache *)(X)->data.data)
62#define CACHENAME(X)	(KCMCACHE(X)->name)
63#define KCMCURSOR(C)	((krb5_kcm_cursor)(C))
64
65static HEIMDAL_MUTEX kcm_mutex = HEIMDAL_MUTEX_INITIALIZER;
66static heim_ipc kcm_ipc = NULL;
67
68static krb5_error_code
69kcm_send_request(krb5_context context,
70		 krb5_storage *request,
71		 krb5_data *response_data)
72{
73    krb5_error_code ret = 0;
74    krb5_data request_data;
75
76    HEIMDAL_MUTEX_lock(&kcm_mutex);
77    if (kcm_ipc == NULL)
78	ret = heim_ipc_init_context(kcm_ipc_name, &kcm_ipc);
79    HEIMDAL_MUTEX_unlock(&kcm_mutex);
80    if (ret)
81	return KRB5_CC_NOSUPP;
82
83    ret = krb5_storage_to_data(request, &request_data);
84    if (ret) {
85	krb5_clear_error_message(context);
86	return KRB5_CC_NOMEM;
87    }
88
89    ret = heim_ipc_call(kcm_ipc, &request_data, response_data, NULL);
90    krb5_data_free(&request_data);
91
92    if (ret) {
93	krb5_clear_error_message(context);
94	ret = KRB5_CC_NOSUPP;
95    }
96
97    return ret;
98}
99
100KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
101krb5_kcm_storage_request(krb5_context context,
102			 uint16_t opcode,
103			 krb5_storage **storage_p)
104{
105    krb5_storage *sp;
106    krb5_error_code ret;
107
108    *storage_p = NULL;
109
110    sp = krb5_storage_emem();
111    if (sp == NULL) {
112	krb5_set_error_message(context, KRB5_CC_NOMEM, N_("malloc: out of memory", ""));
113	return KRB5_CC_NOMEM;
114    }
115
116    /* Send MAJOR | VERSION | OPCODE */
117    ret  = krb5_store_int8(sp, KCM_PROTOCOL_VERSION_MAJOR);
118    if (ret)
119	goto fail;
120    ret = krb5_store_int8(sp, KCM_PROTOCOL_VERSION_MINOR);
121    if (ret)
122	goto fail;
123    ret = krb5_store_int16(sp, opcode);
124    if (ret)
125	goto fail;
126
127    *storage_p = sp;
128 fail:
129    if (ret) {
130	krb5_set_error_message(context, ret,
131			       N_("Failed to encode KCM request", ""));
132	krb5_storage_free(sp);
133    }
134
135    return ret;
136}
137
138static krb5_error_code
139kcm_alloc(krb5_context context, const char *name, krb5_ccache *id)
140{
141    krb5_kcmcache *k;
142
143    k = malloc(sizeof(*k));
144    if (k == NULL) {
145	krb5_set_error_message(context, KRB5_CC_NOMEM,
146			       N_("malloc: out of memory", ""));
147	return KRB5_CC_NOMEM;
148    }
149
150    if (name != NULL) {
151	k->name = strdup(name);
152	if (k->name == NULL) {
153	    free(k);
154	    krb5_set_error_message(context, KRB5_CC_NOMEM,
155				   N_("malloc: out of memory", ""));
156	    return KRB5_CC_NOMEM;
157	}
158    } else
159	k->name = NULL;
160
161    (*id)->data.data = k;
162    (*id)->data.length = sizeof(*k);
163
164    return 0;
165}
166
167KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
168krb5_kcm_call(krb5_context context,
169	      krb5_storage *request,
170	      krb5_storage **response_p,
171	      krb5_data *response_data_p)
172{
173    krb5_data response_data;
174    krb5_error_code ret;
175    int32_t status;
176    krb5_storage *response;
177
178    if (response_p != NULL)
179	*response_p = NULL;
180
181    krb5_data_zero(&response_data);
182
183    ret = kcm_send_request(context, request, &response_data);
184    if (ret)
185	return ret;
186
187    response = krb5_storage_from_data(&response_data);
188    if (response == NULL) {
189	krb5_data_free(&response_data);
190	return KRB5_CC_IO;
191    }
192
193    ret = krb5_ret_int32(response, &status);
194    if (ret) {
195	krb5_storage_free(response);
196	krb5_data_free(&response_data);
197	return KRB5_CC_FORMAT;
198    }
199
200    if (status) {
201	krb5_storage_free(response);
202	krb5_data_free(&response_data);
203	return status;
204    }
205
206    if (response_p != NULL) {
207	*response_data_p = response_data;
208	*response_p = response;
209
210	return 0;
211    }
212
213    krb5_storage_free(response);
214    krb5_data_free(&response_data);
215
216    return 0;
217}
218
219static void
220kcm_free(krb5_context context, krb5_ccache *id)
221{
222    krb5_kcmcache *k = KCMCACHE(*id);
223
224    if (k != NULL) {
225	if (k->name != NULL)
226	    free(k->name);
227	memset(k, 0, sizeof(*k));
228	krb5_data_free(&(*id)->data);
229    }
230}
231
232static const char *
233kcm_get_name(krb5_context context,
234	     krb5_ccache id)
235{
236    return CACHENAME(id);
237}
238
239static krb5_error_code
240kcm_resolve(krb5_context context, krb5_ccache *id, const char *res)
241{
242    return kcm_alloc(context, res, id);
243}
244
245/*
246 * Request:
247 *
248 * Response:
249 *      NameZ
250 */
251static krb5_error_code
252kcm_gen_new(krb5_context context, krb5_ccache *id)
253{
254    krb5_kcmcache *k;
255    krb5_error_code ret;
256    krb5_storage *request, *response;
257    krb5_data response_data;
258
259    ret = kcm_alloc(context, NULL, id);
260    if (ret)
261	return ret;
262
263    k = KCMCACHE(*id);
264
265    ret = krb5_kcm_storage_request(context, KCM_OP_GEN_NEW, &request);
266    if (ret) {
267	kcm_free(context, id);
268	return ret;
269    }
270
271    ret = krb5_kcm_call(context, request, &response, &response_data);
272    if (ret) {
273	krb5_storage_free(request);
274	kcm_free(context, id);
275	return ret;
276    }
277
278    ret = krb5_ret_stringz(response, &k->name);
279    if (ret)
280	ret = KRB5_CC_IO;
281
282    krb5_storage_free(request);
283    krb5_storage_free(response);
284    krb5_data_free(&response_data);
285
286    if (ret)
287	kcm_free(context, id);
288
289    return ret;
290}
291
292/*
293 * Request:
294 *      NameZ
295 *      Principal
296 *
297 * Response:
298 *
299 */
300static krb5_error_code
301kcm_initialize(krb5_context context,
302	       krb5_ccache id,
303	       krb5_principal primary_principal)
304{
305    krb5_error_code ret;
306    krb5_kcmcache *k = KCMCACHE(id);
307    krb5_storage *request;
308
309    ret = krb5_kcm_storage_request(context, KCM_OP_INITIALIZE, &request);
310    if (ret)
311	return ret;
312
313    ret = krb5_store_stringz(request, k->name);
314    if (ret) {
315	krb5_storage_free(request);
316	return ret;
317    }
318
319    ret = krb5_store_principal(request, primary_principal);
320    if (ret) {
321	krb5_storage_free(request);
322	return ret;
323    }
324
325    ret = krb5_kcm_call(context, request, NULL, NULL);
326
327    krb5_storage_free(request);
328
329    if (context->kdc_sec_offset)
330	kcm_set_kdc_offset(context, id, context->kdc_sec_offset);
331
332    return ret;
333}
334
335static krb5_error_code
336kcm_close(krb5_context context,
337	  krb5_ccache id)
338{
339    kcm_free(context, &id);
340    return 0;
341}
342
343/*
344 * Request:
345 *      NameZ
346 *
347 * Response:
348 *
349 */
350static krb5_error_code
351kcm_destroy(krb5_context context,
352	    krb5_ccache id)
353{
354    krb5_error_code ret;
355    krb5_kcmcache *k = KCMCACHE(id);
356    krb5_storage *request;
357
358    ret = krb5_kcm_storage_request(context, KCM_OP_DESTROY, &request);
359    if (ret)
360	return ret;
361
362    ret = krb5_store_stringz(request, k->name);
363    if (ret) {
364	krb5_storage_free(request);
365	return ret;
366    }
367
368    ret = krb5_kcm_call(context, request, NULL, NULL);
369
370    krb5_storage_free(request);
371    return ret;
372}
373
374/*
375 * Request:
376 *      NameZ
377 *      Creds
378 *
379 * Response:
380 *
381 */
382static krb5_error_code
383kcm_store_cred(krb5_context context,
384	       krb5_ccache id,
385	       krb5_creds *creds)
386{
387    krb5_error_code ret;
388    krb5_kcmcache *k = KCMCACHE(id);
389    krb5_storage *request;
390
391    ret = krb5_kcm_storage_request(context, KCM_OP_STORE, &request);
392    if (ret)
393	return ret;
394
395    ret = krb5_store_stringz(request, k->name);
396    if (ret) {
397	krb5_storage_free(request);
398	return ret;
399    }
400
401    ret = krb5_store_creds(request, creds);
402    if (ret) {
403	krb5_storage_free(request);
404	return ret;
405    }
406
407    ret = krb5_kcm_call(context, request, NULL, NULL);
408
409    krb5_storage_free(request);
410    return ret;
411}
412
413#if 0
414/*
415 * Request:
416 *      NameZ
417 *      WhichFields
418 *      MatchCreds
419 *
420 * Response:
421 *      Creds
422 *
423 */
424static krb5_error_code
425kcm_retrieve(krb5_context context,
426	     krb5_ccache id,
427	     krb5_flags which,
428	     const krb5_creds *mcred,
429	     krb5_creds *creds)
430{
431    krb5_error_code ret;
432    krb5_kcmcache *k = KCMCACHE(id);
433    krb5_storage *request, *response;
434    krb5_data response_data;
435
436    ret = krb5_kcm_storage_request(context, KCM_OP_RETRIEVE, &request);
437    if (ret)
438	return ret;
439
440    ret = krb5_store_stringz(request, k->name);
441    if (ret) {
442	krb5_storage_free(request);
443	return ret;
444    }
445
446    ret = krb5_store_int32(request, which);
447    if (ret) {
448	krb5_storage_free(request);
449	return ret;
450    }
451
452    ret = krb5_store_creds_tag(request, rk_UNCONST(mcred));
453    if (ret) {
454	krb5_storage_free(request);
455	return ret;
456    }
457
458    ret = krb5_kcm_call(context, request, &response, &response_data);
459    if (ret) {
460	krb5_storage_free(request);
461	return ret;
462    }
463
464    ret = krb5_ret_creds(response, creds);
465    if (ret)
466	ret = KRB5_CC_IO;
467
468    krb5_storage_free(request);
469    krb5_storage_free(response);
470    krb5_data_free(&response_data);
471
472    return ret;
473}
474#endif
475
476/*
477 * Request:
478 *      NameZ
479 *
480 * Response:
481 *      Principal
482 */
483static krb5_error_code
484kcm_get_principal(krb5_context context,
485		  krb5_ccache id,
486		  krb5_principal *principal)
487{
488    krb5_error_code ret;
489    krb5_kcmcache *k = KCMCACHE(id);
490    krb5_storage *request, *response;
491    krb5_data response_data;
492
493    ret = krb5_kcm_storage_request(context, KCM_OP_GET_PRINCIPAL, &request);
494    if (ret)
495	return ret;
496
497    ret = krb5_store_stringz(request, k->name);
498    if (ret) {
499	krb5_storage_free(request);
500	return ret;
501    }
502
503    ret = krb5_kcm_call(context, request, &response, &response_data);
504    if (ret) {
505	krb5_storage_free(request);
506	return ret;
507    }
508
509    ret = krb5_ret_principal(response, principal);
510    if (ret)
511	ret = KRB5_CC_IO;
512
513    krb5_storage_free(request);
514    krb5_storage_free(response);
515    krb5_data_free(&response_data);
516
517    return ret;
518}
519
520/*
521 * Request:
522 *      NameZ
523 *
524 * Response:
525 *      Cursor
526 *
527 */
528static krb5_error_code
529kcm_get_first (krb5_context context,
530	       krb5_ccache id,
531	       krb5_cc_cursor *cursor)
532{
533    krb5_error_code ret;
534    krb5_kcm_cursor c;
535    krb5_kcmcache *k = KCMCACHE(id);
536    krb5_storage *request, *response;
537    krb5_data response_data;
538
539    ret = krb5_kcm_storage_request(context, KCM_OP_GET_CRED_UUID_LIST, &request);
540    if (ret)
541	return ret;
542
543    ret = krb5_store_stringz(request, k->name);
544    if (ret) {
545	krb5_storage_free(request);
546	return ret;
547    }
548
549    ret = krb5_kcm_call(context, request, &response, &response_data);
550    krb5_storage_free(request);
551    if (ret)
552	return ret;
553
554    c = calloc(1, sizeof(*c));
555    if (c == NULL) {
556	ret = ENOMEM;
557	krb5_set_error_message(context, ret,
558			       N_("malloc: out of memory", ""));
559	return ret;
560    }
561
562    while (1) {
563	ssize_t sret;
564	kcmuuid_t uuid;
565	void *ptr;
566
567	sret = krb5_storage_read(response, &uuid, sizeof(uuid));
568	if (sret == 0) {
569	    ret = 0;
570	    break;
571	} else if (sret != sizeof(uuid)) {
572	    ret = EINVAL;
573	    break;
574	}
575
576	ptr = realloc(c->uuids, sizeof(c->uuids[0]) * (c->length + 1));
577	if (ptr == NULL) {
578	    free(c->uuids);
579	    free(c);
580	    krb5_set_error_message(context, ENOMEM,
581				   N_("malloc: out of memory", ""));
582	    return ENOMEM;
583	}
584	c->uuids = ptr;
585
586	memcpy(&c->uuids[c->length], &uuid, sizeof(uuid));
587	c->length += 1;
588    }
589
590    krb5_storage_free(response);
591    krb5_data_free(&response_data);
592
593    if (ret) {
594        free(c->uuids);
595        free(c);
596	return ret;
597    }
598
599    *cursor = c;
600
601    return 0;
602}
603
604/*
605 * Request:
606 *      NameZ
607 *      Cursor
608 *
609 * Response:
610 *      Creds
611 */
612static krb5_error_code
613kcm_get_next (krb5_context context,
614		krb5_ccache id,
615		krb5_cc_cursor *cursor,
616		krb5_creds *creds)
617{
618    krb5_error_code ret;
619    krb5_kcmcache *k = KCMCACHE(id);
620    krb5_kcm_cursor c = KCMCURSOR(*cursor);
621    krb5_storage *request, *response;
622    krb5_data response_data;
623    ssize_t sret;
624
625 again:
626
627    if (c->offset >= c->length)
628	return KRB5_CC_END;
629
630    ret = krb5_kcm_storage_request(context, KCM_OP_GET_CRED_BY_UUID, &request);
631    if (ret)
632	return ret;
633
634    ret = krb5_store_stringz(request, k->name);
635    if (ret) {
636	krb5_storage_free(request);
637	return ret;
638    }
639
640    sret = krb5_storage_write(request,
641			      &c->uuids[c->offset],
642			      sizeof(c->uuids[c->offset]));
643    c->offset++;
644    if (sret != sizeof(c->uuids[c->offset])) {
645	krb5_storage_free(request);
646	krb5_clear_error_message(context);
647	return ENOMEM;
648    }
649
650    ret = krb5_kcm_call(context, request, &response, &response_data);
651    krb5_storage_free(request);
652    if (ret == KRB5_CC_END) {
653	goto again;
654    }
655
656    ret = krb5_ret_creds(response, creds);
657    if (ret)
658	ret = KRB5_CC_IO;
659
660    krb5_storage_free(response);
661    krb5_data_free(&response_data);
662
663    return ret;
664}
665
666/*
667 * Request:
668 *      NameZ
669 *      Cursor
670 *
671 * Response:
672 *
673 */
674static krb5_error_code
675kcm_end_get (krb5_context context,
676	     krb5_ccache id,
677	     krb5_cc_cursor *cursor)
678{
679    krb5_kcm_cursor c = KCMCURSOR(*cursor);
680
681    free(c->uuids);
682    free(c);
683
684    *cursor = NULL;
685
686    return 0;
687}
688
689/*
690 * Request:
691 *      NameZ
692 *      WhichFields
693 *      MatchCreds
694 *
695 * Response:
696 *
697 */
698static krb5_error_code
699kcm_remove_cred(krb5_context context,
700		krb5_ccache id,
701		krb5_flags which,
702		krb5_creds *cred)
703{
704    krb5_error_code ret;
705    krb5_kcmcache *k = KCMCACHE(id);
706    krb5_storage *request;
707
708    ret = krb5_kcm_storage_request(context, KCM_OP_REMOVE_CRED, &request);
709    if (ret)
710	return ret;
711
712    ret = krb5_store_stringz(request, k->name);
713    if (ret) {
714	krb5_storage_free(request);
715	return ret;
716    }
717
718    ret = krb5_store_int32(request, which);
719    if (ret) {
720	krb5_storage_free(request);
721	return ret;
722    }
723
724    ret = krb5_store_creds_tag(request, cred);
725    if (ret) {
726	krb5_storage_free(request);
727	return ret;
728    }
729
730    ret = krb5_kcm_call(context, request, NULL, NULL);
731
732    krb5_storage_free(request);
733    return ret;
734}
735
736static krb5_error_code
737kcm_set_flags(krb5_context context,
738	      krb5_ccache id,
739	      krb5_flags flags)
740{
741    krb5_error_code ret;
742    krb5_kcmcache *k = KCMCACHE(id);
743    krb5_storage *request;
744
745    ret = krb5_kcm_storage_request(context, KCM_OP_SET_FLAGS, &request);
746    if (ret)
747	return ret;
748
749    ret = krb5_store_stringz(request, k->name);
750    if (ret) {
751	krb5_storage_free(request);
752	return ret;
753    }
754
755    ret = krb5_store_int32(request, flags);
756    if (ret) {
757	krb5_storage_free(request);
758	return ret;
759    }
760
761    ret = krb5_kcm_call(context, request, NULL, NULL);
762
763    krb5_storage_free(request);
764    return ret;
765}
766
767static int
768kcm_get_version(krb5_context context,
769		krb5_ccache id)
770{
771    return 0;
772}
773
774/*
775 * Send nothing
776 * get back list of uuids
777 */
778
779static krb5_error_code
780kcm_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
781{
782    krb5_error_code ret;
783    krb5_kcm_cursor c;
784    krb5_storage *request, *response;
785    krb5_data response_data;
786
787    *cursor = NULL;
788
789    c = calloc(1, sizeof(*c));
790    if (c == NULL) {
791	ret = ENOMEM;
792	krb5_set_error_message(context, ret,
793			       N_("malloc: out of memory", ""));
794	goto out;
795    }
796
797    ret = krb5_kcm_storage_request(context, KCM_OP_GET_CACHE_UUID_LIST, &request);
798    if (ret)
799	goto out;
800
801    ret = krb5_kcm_call(context, request, &response, &response_data);
802    krb5_storage_free(request);
803    if (ret)
804	goto out;
805
806    while (1) {
807	ssize_t sret;
808	kcmuuid_t uuid;
809	void *ptr;
810
811	sret = krb5_storage_read(response, &uuid, sizeof(uuid));
812	if (sret == 0) {
813	    ret = 0;
814	    break;
815	} else if (sret != sizeof(uuid)) {
816	    ret = EINVAL;
817	    goto out;
818	}
819
820	ptr = realloc(c->uuids, sizeof(c->uuids[0]) * (c->length + 1));
821	if (ptr == NULL) {
822	    ret = ENOMEM;
823	    krb5_set_error_message(context, ret,
824				   N_("malloc: out of memory", ""));
825	    goto out;
826	}
827	c->uuids = ptr;
828
829	memcpy(&c->uuids[c->length], &uuid, sizeof(uuid));
830	c->length += 1;
831    }
832
833    krb5_storage_free(response);
834    krb5_data_free(&response_data);
835
836 out:
837    if (ret && c) {
838        free(c->uuids);
839        free(c);
840    } else
841	*cursor = c;
842
843    return ret;
844}
845
846/*
847 * Send uuid
848 * Recv cache name
849 */
850
851static krb5_error_code
852kcm_get_cache_next(krb5_context context, krb5_cc_cursor cursor, const krb5_cc_ops *ops, krb5_ccache *id)
853{
854    krb5_error_code ret;
855    krb5_kcm_cursor c = KCMCURSOR(cursor);
856    krb5_storage *request, *response;
857    krb5_data response_data;
858    ssize_t sret;
859    char *name;
860
861    *id = NULL;
862
863 again:
864
865    if (c->offset >= c->length)
866	return KRB5_CC_END;
867
868    ret = krb5_kcm_storage_request(context, KCM_OP_GET_CACHE_BY_UUID, &request);
869    if (ret)
870	return ret;
871
872    sret = krb5_storage_write(request,
873			      &c->uuids[c->offset],
874			      sizeof(c->uuids[c->offset]));
875    c->offset++;
876    if (sret != sizeof(c->uuids[c->offset])) {
877	krb5_storage_free(request);
878	krb5_clear_error_message(context);
879	return ENOMEM;
880    }
881
882    ret = krb5_kcm_call(context, request, &response, &response_data);
883    krb5_storage_free(request);
884    if (ret == KRB5_CC_END)
885	goto again;
886
887    ret = krb5_ret_stringz(response, &name);
888    krb5_storage_free(response);
889    krb5_data_free(&response_data);
890
891    if (ret == 0) {
892	ret = _krb5_cc_allocate(context, ops, id);
893	if (ret == 0)
894	    ret = kcm_alloc(context, name, id);
895	krb5_xfree(name);
896    }
897
898    return ret;
899}
900
901static krb5_error_code
902kcm_get_cache_next_kcm(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
903{
904#ifndef KCM_IS_API_CACHE
905    return kcm_get_cache_next(context, cursor, &krb5_kcm_ops, id);
906#else
907    return KRB5_CC_END;
908#endif
909}
910
911static krb5_error_code
912kcm_get_cache_next_api(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
913{
914    return kcm_get_cache_next(context, cursor, &krb5_akcm_ops, id);
915}
916
917
918static krb5_error_code
919kcm_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
920{
921    krb5_kcm_cursor c = KCMCURSOR(cursor);
922
923    free(c->uuids);
924    free(c);
925    return 0;
926}
927
928
929static krb5_error_code
930kcm_move(krb5_context context, krb5_ccache from, krb5_ccache to)
931{
932    krb5_error_code ret;
933    krb5_kcmcache *oldk = KCMCACHE(from);
934    krb5_kcmcache *newk = KCMCACHE(to);
935    krb5_storage *request;
936
937    ret = krb5_kcm_storage_request(context, KCM_OP_MOVE_CACHE, &request);
938    if (ret)
939	return ret;
940
941    ret = krb5_store_stringz(request, oldk->name);
942    if (ret) {
943	krb5_storage_free(request);
944	return ret;
945    }
946
947    ret = krb5_store_stringz(request, newk->name);
948    if (ret) {
949	krb5_storage_free(request);
950	return ret;
951    }
952    ret = krb5_kcm_call(context, request, NULL, NULL);
953
954    krb5_storage_free(request);
955    return ret;
956}
957
958static krb5_error_code
959kcm_get_default_name(krb5_context context, const krb5_cc_ops *ops,
960		     const char *defstr, char **str)
961{
962    krb5_error_code ret;
963    krb5_storage *request, *response;
964    krb5_data response_data;
965    char *name;
966
967    *str = NULL;
968
969    ret = krb5_kcm_storage_request(context, KCM_OP_GET_DEFAULT_CACHE, &request);
970    if (ret)
971	return ret;
972
973    ret = krb5_kcm_call(context, request, &response, &response_data);
974    krb5_storage_free(request);
975    if (ret)
976	return _krb5_expand_default_cc_name(context, defstr, str);
977
978    ret = krb5_ret_stringz(response, &name);
979    krb5_storage_free(response);
980    krb5_data_free(&response_data);
981    if (ret)
982	return ret;
983
984    asprintf(str, "%s:%s", ops->prefix, name);
985    free(name);
986    if (str == NULL)
987	return ENOMEM;
988
989    return 0;
990}
991
992static krb5_error_code
993kcm_get_default_name_api(krb5_context context, char **str)
994{
995    return kcm_get_default_name(context, &krb5_akcm_ops,
996				KRB5_DEFAULT_CCNAME_KCM_API, str);
997}
998
999static krb5_error_code
1000kcm_get_default_name_kcm(krb5_context context, char **str)
1001{
1002    return kcm_get_default_name(context, &krb5_kcm_ops,
1003				KRB5_DEFAULT_CCNAME_KCM_KCM, str);
1004}
1005
1006static krb5_error_code
1007kcm_set_default(krb5_context context, krb5_ccache id)
1008{
1009    krb5_error_code ret;
1010    krb5_storage *request;
1011    krb5_kcmcache *k = KCMCACHE(id);
1012
1013    ret = krb5_kcm_storage_request(context, KCM_OP_SET_DEFAULT_CACHE, &request);
1014    if (ret)
1015	return ret;
1016
1017    ret = krb5_store_stringz(request, k->name);
1018    if (ret) {
1019	krb5_storage_free(request);
1020	return ret;
1021    }
1022
1023    ret = krb5_kcm_call(context, request, NULL, NULL);
1024    krb5_storage_free(request);
1025
1026    return ret;
1027}
1028
1029static krb5_error_code
1030kcm_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime)
1031{
1032    *mtime = time(NULL);
1033    return 0;
1034}
1035
1036static krb5_error_code
1037kcm_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat kdc_offset)
1038{
1039    krb5_kcmcache *k = KCMCACHE(id);
1040    krb5_error_code ret;
1041    krb5_storage *request;
1042
1043    ret = krb5_kcm_storage_request(context, KCM_OP_SET_KDC_OFFSET, &request);
1044    if (ret)
1045	return ret;
1046
1047    ret = krb5_store_stringz(request, k->name);
1048    if (ret) {
1049	krb5_storage_free(request);
1050	return ret;
1051    }
1052    ret = krb5_store_int32(request, kdc_offset);
1053    if (ret) {
1054	krb5_storage_free(request);
1055	return ret;
1056    }
1057
1058    ret = krb5_kcm_call(context, request, NULL, NULL);
1059    krb5_storage_free(request);
1060
1061    return ret;
1062}
1063
1064static krb5_error_code
1065kcm_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *kdc_offset)
1066{
1067    krb5_kcmcache *k = KCMCACHE(id);
1068    krb5_error_code ret;
1069    krb5_storage *request, *response;
1070    krb5_data response_data;
1071    int32_t offset;
1072
1073    ret = krb5_kcm_storage_request(context, KCM_OP_GET_KDC_OFFSET, &request);
1074    if (ret)
1075	return ret;
1076
1077    ret = krb5_store_stringz(request, k->name);
1078    if (ret) {
1079	krb5_storage_free(request);
1080	return ret;
1081    }
1082
1083    ret = krb5_kcm_call(context, request, &response, &response_data);
1084    krb5_storage_free(request);
1085    if (ret)
1086	return ret;
1087
1088    ret = krb5_ret_int32(response, &offset);
1089    krb5_storage_free(response);
1090    krb5_data_free(&response_data);
1091    if (ret)
1092	return ret;
1093
1094    *kdc_offset = offset;
1095
1096    return 0;
1097}
1098
1099/**
1100 * Variable containing the KCM based credential cache implemention.
1101 *
1102 * @ingroup krb5_ccache
1103 */
1104
1105KRB5_LIB_VARIABLE const krb5_cc_ops krb5_kcm_ops = {
1106    KRB5_CC_OPS_VERSION,
1107    "KCM",
1108    kcm_get_name,
1109    kcm_resolve,
1110    kcm_gen_new,
1111    kcm_initialize,
1112    kcm_destroy,
1113    kcm_close,
1114    kcm_store_cred,
1115    NULL /* kcm_retrieve */,
1116    kcm_get_principal,
1117    kcm_get_first,
1118    kcm_get_next,
1119    kcm_end_get,
1120    kcm_remove_cred,
1121    kcm_set_flags,
1122    kcm_get_version,
1123    kcm_get_cache_first,
1124    kcm_get_cache_next_kcm,
1125    kcm_end_cache_get,
1126    kcm_move,
1127    kcm_get_default_name_kcm,
1128    kcm_set_default,
1129    kcm_lastchange,
1130    kcm_set_kdc_offset,
1131    kcm_get_kdc_offset
1132};
1133
1134KRB5_LIB_VARIABLE const krb5_cc_ops krb5_akcm_ops = {
1135    KRB5_CC_OPS_VERSION,
1136    "API",
1137    kcm_get_name,
1138    kcm_resolve,
1139    kcm_gen_new,
1140    kcm_initialize,
1141    kcm_destroy,
1142    kcm_close,
1143    kcm_store_cred,
1144    NULL /* kcm_retrieve */,
1145    kcm_get_principal,
1146    kcm_get_first,
1147    kcm_get_next,
1148    kcm_end_get,
1149    kcm_remove_cred,
1150    kcm_set_flags,
1151    kcm_get_version,
1152    kcm_get_cache_first,
1153    kcm_get_cache_next_api,
1154    kcm_end_cache_get,
1155    kcm_move,
1156    kcm_get_default_name_api,
1157    kcm_set_default,
1158    kcm_lastchange,
1159    NULL,
1160    NULL
1161};
1162
1163
1164KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1165_krb5_kcm_is_running(krb5_context context)
1166{
1167    krb5_error_code ret;
1168    krb5_ccache_data ccdata;
1169    krb5_ccache id = &ccdata;
1170    krb5_boolean running;
1171
1172    ret = kcm_alloc(context, NULL, &id);
1173    if (ret)
1174	return 0;
1175
1176    running = (_krb5_kcm_noop(context, id) == 0);
1177
1178    kcm_free(context, &id);
1179
1180    return running;
1181}
1182
1183/*
1184 * Request:
1185 *
1186 * Response:
1187 *
1188 */
1189KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1190_krb5_kcm_noop(krb5_context context,
1191	       krb5_ccache id)
1192{
1193    krb5_error_code ret;
1194    krb5_storage *request;
1195
1196    ret = krb5_kcm_storage_request(context, KCM_OP_NOOP, &request);
1197    if (ret)
1198	return ret;
1199
1200    ret = krb5_kcm_call(context, request, NULL, NULL);
1201
1202    krb5_storage_free(request);
1203    return ret;
1204}
1205
1206
1207/*
1208 * Request:
1209 *      NameZ
1210 *      ServerPrincipalPresent
1211 *      ServerPrincipal OPTIONAL
1212 *      Key
1213 *
1214 * Repsonse:
1215 *
1216 */
1217KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1218_krb5_kcm_get_initial_ticket(krb5_context context,
1219			     krb5_ccache id,
1220			     krb5_principal server,
1221			     krb5_keyblock *key)
1222{
1223    krb5_kcmcache *k = KCMCACHE(id);
1224    krb5_error_code ret;
1225    krb5_storage *request;
1226
1227    ret = krb5_kcm_storage_request(context, KCM_OP_GET_INITIAL_TICKET, &request);
1228    if (ret)
1229	return ret;
1230
1231    ret = krb5_store_stringz(request, k->name);
1232    if (ret) {
1233	krb5_storage_free(request);
1234	return ret;
1235    }
1236
1237    ret = krb5_store_int8(request, (server == NULL) ? 0 : 1);
1238    if (ret) {
1239	krb5_storage_free(request);
1240	return ret;
1241    }
1242
1243    if (server != NULL) {
1244	ret = krb5_store_principal(request, server);
1245	if (ret) {
1246	    krb5_storage_free(request);
1247	    return ret;
1248	}
1249    }
1250
1251    ret = krb5_store_keyblock(request, *key);
1252    if (ret) {
1253	krb5_storage_free(request);
1254	return ret;
1255    }
1256
1257    ret = krb5_kcm_call(context, request, NULL, NULL);
1258
1259    krb5_storage_free(request);
1260    return ret;
1261}
1262
1263
1264/*
1265 * Request:
1266 *      NameZ
1267 *      KDCFlags
1268 *      EncryptionType
1269 *      ServerPrincipal
1270 *
1271 * Repsonse:
1272 *
1273 */
1274KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1275_krb5_kcm_get_ticket(krb5_context context,
1276		     krb5_ccache id,
1277		     krb5_kdc_flags flags,
1278		     krb5_enctype enctype,
1279		     krb5_principal server)
1280{
1281    krb5_error_code ret;
1282    krb5_kcmcache *k = KCMCACHE(id);
1283    krb5_storage *request;
1284
1285    ret = krb5_kcm_storage_request(context, KCM_OP_GET_TICKET, &request);
1286    if (ret)
1287	return ret;
1288
1289    ret = krb5_store_stringz(request, k->name);
1290    if (ret) {
1291	krb5_storage_free(request);
1292	return ret;
1293    }
1294
1295    ret = krb5_store_int32(request, flags.i);
1296    if (ret) {
1297	krb5_storage_free(request);
1298	return ret;
1299    }
1300
1301    ret = krb5_store_int32(request, enctype);
1302    if (ret) {
1303	krb5_storage_free(request);
1304	return ret;
1305    }
1306
1307    ret = krb5_store_principal(request, server);
1308    if (ret) {
1309	krb5_storage_free(request);
1310	return ret;
1311    }
1312
1313    ret = krb5_kcm_call(context, request, NULL, NULL);
1314
1315    krb5_storage_free(request);
1316    return ret;
1317}
1318
1319#endif /* HAVE_KCM */
1320