xref: /freebsd/crypto/heimdal/lib/krb5/kcm.c (revision f7c4bd95ba735bd6a5454b4953945a99cefbb80c)
1 /*
2  * Copyright (c) 2005, PADL Software Pty Ltd.
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  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * 3. Neither the name of PADL Software nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include "krb5_locl.h"
34 
35 #ifdef HAVE_KCM
36 /*
37  * Client library for Kerberos Credentials Manager (KCM) daemon
38  */
39 
40 #ifdef HAVE_SYS_UN_H
41 #include <sys/un.h>
42 #endif
43 
44 #include "kcm.h"
45 
46 RCSID("$Id: kcm.c 22108 2007-12-03 17:23:53Z lha $");
47 
48 typedef struct krb5_kcmcache {
49     char *name;
50     struct sockaddr_un path;
51     char *door_path;
52 } krb5_kcmcache;
53 
54 #define KCMCACHE(X)	((krb5_kcmcache *)(X)->data.data)
55 #define CACHENAME(X)	(KCMCACHE(X)->name)
56 #define KCMCURSOR(C)	(*(uint32_t *)(C))
57 
58 static krb5_error_code
59 try_door(krb5_context context, const krb5_kcmcache *k,
60 	 krb5_data *request_data,
61 	 krb5_data *response_data)
62 {
63 #ifdef HAVE_DOOR_CREATE
64     door_arg_t arg;
65     int fd;
66     int ret;
67 
68     memset(&arg, 0, sizeof(arg));
69 
70     fd = open(k->door_path, O_RDWR);
71     if (fd < 0)
72 	return KRB5_CC_IO;
73 
74     arg.data_ptr = request_data->data;
75     arg.data_size = request_data->length;
76     arg.desc_ptr = NULL;
77     arg.desc_num = 0;
78     arg.rbuf = NULL;
79     arg.rsize = 0;
80 
81     ret = door_call(fd, &arg);
82     close(fd);
83     if (ret != 0)
84 	return KRB5_CC_IO;
85 
86     ret = krb5_data_copy(response_data, arg.rbuf, arg.rsize);
87     munmap(arg.rbuf, arg.rsize);
88     if (ret)
89 	return ret;
90 
91     return 0;
92 #else
93     return KRB5_CC_IO;
94 #endif
95 }
96 
97 static krb5_error_code
98 try_unix_socket(krb5_context context, const krb5_kcmcache *k,
99 		krb5_data *request_data,
100 		krb5_data *response_data)
101 {
102     krb5_error_code ret;
103     int fd;
104 
105     fd = socket(AF_UNIX, SOCK_STREAM, 0);
106     if (fd < 0)
107 	return KRB5_CC_IO;
108 
109     if (connect(fd, rk_UNCONST(&k->path), sizeof(k->path)) != 0) {
110 	close(fd);
111 	return KRB5_CC_IO;
112     }
113 
114     ret = _krb5_send_and_recv_tcp(fd, context->kdc_timeout,
115 				  request_data, response_data);
116     close(fd);
117     return ret;
118 }
119 
120 static krb5_error_code
121 kcm_send_request(krb5_context context,
122 		 krb5_kcmcache *k,
123 		 krb5_storage *request,
124 		 krb5_data *response_data)
125 {
126     krb5_error_code ret;
127     krb5_data request_data;
128     int i;
129 
130     response_data->data = NULL;
131     response_data->length = 0;
132 
133     ret = krb5_storage_to_data(request, &request_data);
134     if (ret) {
135 	krb5_clear_error_string(context);
136 	return KRB5_CC_NOMEM;
137     }
138 
139     ret = KRB5_CC_IO;
140 
141     for (i = 0; i < context->max_retries; i++) {
142 	ret = try_door(context, k, &request_data, response_data);
143 	if (ret == 0 && response_data->length != 0)
144 	    break;
145 	ret = try_unix_socket(context, k, &request_data, response_data);
146 	if (ret == 0 && response_data->length != 0)
147 	    break;
148     }
149 
150     krb5_data_free(&request_data);
151 
152     if (ret) {
153 	krb5_clear_error_string(context);
154 	ret = KRB5_CC_IO;
155     }
156 
157     return ret;
158 }
159 
160 static krb5_error_code
161 kcm_storage_request(krb5_context context,
162 		    kcm_operation opcode,
163 		    krb5_storage **storage_p)
164 {
165     krb5_storage *sp;
166     krb5_error_code ret;
167 
168     *storage_p = NULL;
169 
170     sp = krb5_storage_emem();
171     if (sp == NULL) {
172 	krb5_set_error_string(context, "malloc: out of memory");
173 	return KRB5_CC_NOMEM;
174     }
175 
176     /* Send MAJOR | VERSION | OPCODE */
177     ret  = krb5_store_int8(sp, KCM_PROTOCOL_VERSION_MAJOR);
178     if (ret)
179 	goto fail;
180     ret = krb5_store_int8(sp, KCM_PROTOCOL_VERSION_MINOR);
181     if (ret)
182 	goto fail;
183     ret = krb5_store_int16(sp, opcode);
184     if (ret)
185 	goto fail;
186 
187     *storage_p = sp;
188  fail:
189     if (ret) {
190 	krb5_set_error_string(context, "Failed to encode request");
191 	krb5_storage_free(sp);
192     }
193 
194     return ret;
195 }
196 
197 static krb5_error_code
198 kcm_alloc(krb5_context context, const char *name, krb5_ccache *id)
199 {
200     krb5_kcmcache *k;
201     const char *path;
202 
203     k = malloc(sizeof(*k));
204     if (k == NULL) {
205 	krb5_set_error_string(context, "malloc: out of memory");
206 	return KRB5_CC_NOMEM;
207     }
208 
209     if (name != NULL) {
210 	k->name = strdup(name);
211 	if (k->name == NULL) {
212 	    free(k);
213 	    krb5_set_error_string(context, "malloc: out of memory");
214 	    return KRB5_CC_NOMEM;
215 	}
216     } else
217 	k->name = NULL;
218 
219     path = krb5_config_get_string_default(context, NULL,
220 					  _PATH_KCM_SOCKET,
221 					  "libdefaults",
222 					  "kcm_socket",
223 					  NULL);
224 
225     k->path.sun_family = AF_UNIX;
226     strlcpy(k->path.sun_path, path, sizeof(k->path.sun_path));
227 
228     path = krb5_config_get_string_default(context, NULL,
229 					  _PATH_KCM_DOOR,
230 					  "libdefaults",
231 					  "kcm_door",
232 					  NULL);
233     k->door_path = strdup(path);
234 
235     (*id)->data.data = k;
236     (*id)->data.length = sizeof(*k);
237 
238     return 0;
239 }
240 
241 static krb5_error_code
242 kcm_call(krb5_context context,
243 	 krb5_kcmcache *k,
244 	 krb5_storage *request,
245 	 krb5_storage **response_p,
246 	 krb5_data *response_data_p)
247 {
248     krb5_data response_data;
249     krb5_error_code ret;
250     int32_t status;
251     krb5_storage *response;
252 
253     if (response_p != NULL)
254 	*response_p = NULL;
255 
256     ret = kcm_send_request(context, k, request, &response_data);
257     if (ret) {
258 	return ret;
259     }
260 
261     response = krb5_storage_from_data(&response_data);
262     if (response == NULL) {
263 	krb5_data_free(&response_data);
264 	return KRB5_CC_IO;
265     }
266 
267     ret = krb5_ret_int32(response, &status);
268     if (ret) {
269 	krb5_storage_free(response);
270 	krb5_data_free(&response_data);
271 	return KRB5_CC_FORMAT;
272     }
273 
274     if (status) {
275 	krb5_storage_free(response);
276 	krb5_data_free(&response_data);
277 	return status;
278     }
279 
280     if (response_p != NULL) {
281 	*response_data_p = response_data;
282 	*response_p = response;
283 
284 	return 0;
285     }
286 
287     krb5_storage_free(response);
288     krb5_data_free(&response_data);
289 
290     return 0;
291 }
292 
293 static void
294 kcm_free(krb5_context context, krb5_ccache *id)
295 {
296     krb5_kcmcache *k = KCMCACHE(*id);
297 
298     if (k != NULL) {
299 	if (k->name != NULL)
300 	    free(k->name);
301 	if (k->door_path)
302 	    free(k->door_path);
303 	memset(k, 0, sizeof(*k));
304 	krb5_data_free(&(*id)->data);
305     }
306 
307     *id = NULL;
308 }
309 
310 static const char *
311 kcm_get_name(krb5_context context,
312 	     krb5_ccache id)
313 {
314     return CACHENAME(id);
315 }
316 
317 static krb5_error_code
318 kcm_resolve(krb5_context context, krb5_ccache *id, const char *res)
319 {
320     return kcm_alloc(context, res, id);
321 }
322 
323 /*
324  * Request:
325  *
326  * Response:
327  *      NameZ
328  */
329 static krb5_error_code
330 kcm_gen_new(krb5_context context, krb5_ccache *id)
331 {
332     krb5_kcmcache *k;
333     krb5_error_code ret;
334     krb5_storage *request, *response;
335     krb5_data response_data;
336 
337     ret = kcm_alloc(context, NULL, id);
338     if (ret)
339 	return ret;
340 
341     k = KCMCACHE(*id);
342 
343     ret = kcm_storage_request(context, KCM_OP_GEN_NEW, &request);
344     if (ret) {
345 	kcm_free(context, id);
346 	return ret;
347     }
348 
349     ret = kcm_call(context, k, request, &response, &response_data);
350     if (ret) {
351 	krb5_storage_free(request);
352 	kcm_free(context, id);
353 	return ret;
354     }
355 
356     ret = krb5_ret_stringz(response, &k->name);
357     if (ret)
358 	ret = KRB5_CC_IO;
359 
360     krb5_storage_free(request);
361     krb5_storage_free(response);
362     krb5_data_free(&response_data);
363 
364     if (ret)
365 	kcm_free(context, id);
366 
367     return ret;
368 }
369 
370 /*
371  * Request:
372  *      NameZ
373  *      Principal
374  *
375  * Response:
376  *
377  */
378 static krb5_error_code
379 kcm_initialize(krb5_context context,
380 	       krb5_ccache id,
381 	       krb5_principal primary_principal)
382 {
383     krb5_error_code ret;
384     krb5_kcmcache *k = KCMCACHE(id);
385     krb5_storage *request;
386 
387     ret = kcm_storage_request(context, KCM_OP_INITIALIZE, &request);
388     if (ret)
389 	return ret;
390 
391     ret = krb5_store_stringz(request, k->name);
392     if (ret) {
393 	krb5_storage_free(request);
394 	return ret;
395     }
396 
397     ret = krb5_store_principal(request, primary_principal);
398     if (ret) {
399 	krb5_storage_free(request);
400 	return ret;
401     }
402 
403     ret = kcm_call(context, k, request, NULL, NULL);
404 
405     krb5_storage_free(request);
406     return ret;
407 }
408 
409 static krb5_error_code
410 kcm_close(krb5_context context,
411 	  krb5_ccache id)
412 {
413     kcm_free(context, &id);
414     return 0;
415 }
416 
417 /*
418  * Request:
419  *      NameZ
420  *
421  * Response:
422  *
423  */
424 static krb5_error_code
425 kcm_destroy(krb5_context context,
426 	    krb5_ccache id)
427 {
428     krb5_error_code ret;
429     krb5_kcmcache *k = KCMCACHE(id);
430     krb5_storage *request;
431 
432     ret = kcm_storage_request(context, KCM_OP_DESTROY, &request);
433     if (ret)
434 	return ret;
435 
436     ret = krb5_store_stringz(request, k->name);
437     if (ret) {
438 	krb5_storage_free(request);
439 	return ret;
440     }
441 
442     ret = kcm_call(context, k, request, NULL, NULL);
443 
444     krb5_storage_free(request);
445     return ret;
446 }
447 
448 /*
449  * Request:
450  *      NameZ
451  *      Creds
452  *
453  * Response:
454  *
455  */
456 static krb5_error_code
457 kcm_store_cred(krb5_context context,
458 	       krb5_ccache id,
459 	       krb5_creds *creds)
460 {
461     krb5_error_code ret;
462     krb5_kcmcache *k = KCMCACHE(id);
463     krb5_storage *request;
464 
465     ret = kcm_storage_request(context, KCM_OP_STORE, &request);
466     if (ret)
467 	return ret;
468 
469     ret = krb5_store_stringz(request, k->name);
470     if (ret) {
471 	krb5_storage_free(request);
472 	return ret;
473     }
474 
475     ret = krb5_store_creds(request, creds);
476     if (ret) {
477 	krb5_storage_free(request);
478 	return ret;
479     }
480 
481     ret = kcm_call(context, k, request, NULL, NULL);
482 
483     krb5_storage_free(request);
484     return ret;
485 }
486 
487 /*
488  * Request:
489  *      NameZ
490  *      WhichFields
491  *      MatchCreds
492  *
493  * Response:
494  *      Creds
495  *
496  */
497 static krb5_error_code
498 kcm_retrieve(krb5_context context,
499 	     krb5_ccache id,
500 	     krb5_flags which,
501 	     const krb5_creds *mcred,
502 	     krb5_creds *creds)
503 {
504     krb5_error_code ret;
505     krb5_kcmcache *k = KCMCACHE(id);
506     krb5_storage *request, *response;
507     krb5_data response_data;
508 
509     ret = kcm_storage_request(context, KCM_OP_RETRIEVE, &request);
510     if (ret)
511 	return ret;
512 
513     ret = krb5_store_stringz(request, k->name);
514     if (ret) {
515 	krb5_storage_free(request);
516 	return ret;
517     }
518 
519     ret = krb5_store_int32(request, which);
520     if (ret) {
521 	krb5_storage_free(request);
522 	return ret;
523     }
524 
525     ret = krb5_store_creds_tag(request, rk_UNCONST(mcred));
526     if (ret) {
527 	krb5_storage_free(request);
528 	return ret;
529     }
530 
531     ret = kcm_call(context, k, request, &response, &response_data);
532     if (ret) {
533 	krb5_storage_free(request);
534 	return ret;
535     }
536 
537     ret = krb5_ret_creds(response, creds);
538     if (ret)
539 	ret = KRB5_CC_IO;
540 
541     krb5_storage_free(request);
542     krb5_storage_free(response);
543     krb5_data_free(&response_data);
544 
545     return ret;
546 }
547 
548 /*
549  * Request:
550  *      NameZ
551  *
552  * Response:
553  *      Principal
554  */
555 static krb5_error_code
556 kcm_get_principal(krb5_context context,
557 		  krb5_ccache id,
558 		  krb5_principal *principal)
559 {
560     krb5_error_code ret;
561     krb5_kcmcache *k = KCMCACHE(id);
562     krb5_storage *request, *response;
563     krb5_data response_data;
564 
565     ret = kcm_storage_request(context, KCM_OP_GET_PRINCIPAL, &request);
566     if (ret)
567 	return ret;
568 
569     ret = krb5_store_stringz(request, k->name);
570     if (ret) {
571 	krb5_storage_free(request);
572 	return ret;
573     }
574 
575     ret = kcm_call(context, k, request, &response, &response_data);
576     if (ret) {
577 	krb5_storage_free(request);
578 	return ret;
579     }
580 
581     ret = krb5_ret_principal(response, principal);
582     if (ret)
583 	ret = KRB5_CC_IO;
584 
585     krb5_storage_free(request);
586     krb5_storage_free(response);
587     krb5_data_free(&response_data);
588 
589     return ret;
590 }
591 
592 /*
593  * Request:
594  *      NameZ
595  *
596  * Response:
597  *      Cursor
598  *
599  */
600 static krb5_error_code
601 kcm_get_first (krb5_context context,
602 	       krb5_ccache id,
603 	       krb5_cc_cursor *cursor)
604 {
605     krb5_error_code ret;
606     krb5_kcmcache *k = KCMCACHE(id);
607     krb5_storage *request, *response;
608     krb5_data response_data;
609     int32_t tmp;
610 
611     ret = kcm_storage_request(context, KCM_OP_GET_FIRST, &request);
612     if (ret)
613 	return ret;
614 
615     ret = krb5_store_stringz(request, k->name);
616     if (ret) {
617 	krb5_storage_free(request);
618 	return ret;
619     }
620 
621     ret = kcm_call(context, k, request, &response, &response_data);
622     if (ret) {
623 	krb5_storage_free(request);
624 	return ret;
625     }
626 
627     ret = krb5_ret_int32(response, &tmp);
628     if (ret || tmp < 0)
629 	ret = KRB5_CC_IO;
630 
631     krb5_storage_free(request);
632     krb5_storage_free(response);
633     krb5_data_free(&response_data);
634 
635     if (ret)
636 	return ret;
637 
638     *cursor = malloc(sizeof(tmp));
639     if (*cursor == NULL)
640 	return KRB5_CC_NOMEM;
641 
642     KCMCURSOR(*cursor) = tmp;
643 
644     return 0;
645 }
646 
647 /*
648  * Request:
649  *      NameZ
650  *      Cursor
651  *
652  * Response:
653  *      Creds
654  */
655 static krb5_error_code
656 kcm_get_next (krb5_context context,
657 		krb5_ccache id,
658 		krb5_cc_cursor *cursor,
659 		krb5_creds *creds)
660 {
661     krb5_error_code ret;
662     krb5_kcmcache *k = KCMCACHE(id);
663     krb5_storage *request, *response;
664     krb5_data response_data;
665 
666     ret = kcm_storage_request(context, KCM_OP_GET_NEXT, &request);
667     if (ret)
668 	return ret;
669 
670     ret = krb5_store_stringz(request, k->name);
671     if (ret) {
672 	krb5_storage_free(request);
673 	return ret;
674     }
675 
676     ret = krb5_store_int32(request, KCMCURSOR(*cursor));
677     if (ret) {
678 	krb5_storage_free(request);
679 	return ret;
680     }
681 
682     ret = kcm_call(context, k, request, &response, &response_data);
683     if (ret) {
684 	krb5_storage_free(request);
685 	return ret;
686     }
687 
688     ret = krb5_ret_creds(response, creds);
689     if (ret)
690 	ret = KRB5_CC_IO;
691 
692     krb5_storage_free(request);
693     krb5_storage_free(response);
694     krb5_data_free(&response_data);
695 
696     return ret;
697 }
698 
699 /*
700  * Request:
701  *      NameZ
702  *      Cursor
703  *
704  * Response:
705  *
706  */
707 static krb5_error_code
708 kcm_end_get (krb5_context context,
709 	     krb5_ccache id,
710 	     krb5_cc_cursor *cursor)
711 {
712     krb5_error_code ret;
713     krb5_kcmcache *k = KCMCACHE(id);
714     krb5_storage *request;
715 
716     ret = kcm_storage_request(context, KCM_OP_END_GET, &request);
717     if (ret)
718 	return ret;
719 
720     ret = krb5_store_stringz(request, k->name);
721     if (ret) {
722 	krb5_storage_free(request);
723 	return ret;
724     }
725 
726     ret = krb5_store_int32(request, KCMCURSOR(*cursor));
727     if (ret) {
728 	krb5_storage_free(request);
729 	return ret;
730     }
731 
732     ret = kcm_call(context, k, request, NULL, NULL);
733     if (ret) {
734 	krb5_storage_free(request);
735 	return ret;
736     }
737 
738     krb5_storage_free(request);
739 
740     KCMCURSOR(*cursor) = 0;
741     free(*cursor);
742     *cursor = NULL;
743 
744     return ret;
745 }
746 
747 /*
748  * Request:
749  *      NameZ
750  *      WhichFields
751  *      MatchCreds
752  *
753  * Response:
754  *
755  */
756 static krb5_error_code
757 kcm_remove_cred(krb5_context context,
758 		krb5_ccache id,
759 		krb5_flags which,
760 		krb5_creds *cred)
761 {
762     krb5_error_code ret;
763     krb5_kcmcache *k = KCMCACHE(id);
764     krb5_storage *request;
765 
766     ret = kcm_storage_request(context, KCM_OP_REMOVE_CRED, &request);
767     if (ret)
768 	return ret;
769 
770     ret = krb5_store_stringz(request, k->name);
771     if (ret) {
772 	krb5_storage_free(request);
773 	return ret;
774     }
775 
776     ret = krb5_store_int32(request, which);
777     if (ret) {
778 	krb5_storage_free(request);
779 	return ret;
780     }
781 
782     ret = krb5_store_creds_tag(request, cred);
783     if (ret) {
784 	krb5_storage_free(request);
785 	return ret;
786     }
787 
788     ret = kcm_call(context, k, request, NULL, NULL);
789 
790     krb5_storage_free(request);
791     return ret;
792 }
793 
794 static krb5_error_code
795 kcm_set_flags(krb5_context context,
796 	      krb5_ccache id,
797 	      krb5_flags flags)
798 {
799     krb5_error_code ret;
800     krb5_kcmcache *k = KCMCACHE(id);
801     krb5_storage *request;
802 
803     ret = kcm_storage_request(context, KCM_OP_SET_FLAGS, &request);
804     if (ret)
805 	return ret;
806 
807     ret = krb5_store_stringz(request, k->name);
808     if (ret) {
809 	krb5_storage_free(request);
810 	return ret;
811     }
812 
813     ret = krb5_store_int32(request, flags);
814     if (ret) {
815 	krb5_storage_free(request);
816 	return ret;
817     }
818 
819     ret = kcm_call(context, k, request, NULL, NULL);
820 
821     krb5_storage_free(request);
822     return ret;
823 }
824 
825 static krb5_error_code
826 kcm_get_version(krb5_context context,
827 		krb5_ccache id)
828 {
829     return 0;
830 }
831 
832 static krb5_error_code
833 kcm_move(krb5_context context, krb5_ccache from, krb5_ccache to)
834 {
835     krb5_set_error_string(context, "kcm_move not implemented");
836     return EINVAL;
837 }
838 
839 static krb5_error_code
840 kcm_default_name(krb5_context context, char **str)
841 {
842     return _krb5_expand_default_cc_name(context,
843 					KRB5_DEFAULT_CCNAME_KCM,
844 					str);
845 }
846 
847 /**
848  * Variable containing the KCM based credential cache implemention.
849  *
850  * @ingroup krb5_ccache
851  */
852 
853 const krb5_cc_ops krb5_kcm_ops = {
854     "KCM",
855     kcm_get_name,
856     kcm_resolve,
857     kcm_gen_new,
858     kcm_initialize,
859     kcm_destroy,
860     kcm_close,
861     kcm_store_cred,
862     kcm_retrieve,
863     kcm_get_principal,
864     kcm_get_first,
865     kcm_get_next,
866     kcm_end_get,
867     kcm_remove_cred,
868     kcm_set_flags,
869     kcm_get_version,
870     NULL,
871     NULL,
872     NULL,
873     kcm_move,
874     kcm_default_name
875 };
876 
877 krb5_boolean
878 _krb5_kcm_is_running(krb5_context context)
879 {
880     krb5_error_code ret;
881     krb5_ccache_data ccdata;
882     krb5_ccache id = &ccdata;
883     krb5_boolean running;
884 
885     ret = kcm_alloc(context, NULL, &id);
886     if (ret)
887 	return 0;
888 
889     running = (_krb5_kcm_noop(context, id) == 0);
890 
891     kcm_free(context, &id);
892 
893     return running;
894 }
895 
896 /*
897  * Request:
898  *
899  * Response:
900  *
901  */
902 krb5_error_code
903 _krb5_kcm_noop(krb5_context context,
904 	       krb5_ccache id)
905 {
906     krb5_error_code ret;
907     krb5_kcmcache *k = KCMCACHE(id);
908     krb5_storage *request;
909 
910     ret = kcm_storage_request(context, KCM_OP_NOOP, &request);
911     if (ret)
912 	return ret;
913 
914     ret = kcm_call(context, k, request, NULL, NULL);
915 
916     krb5_storage_free(request);
917     return ret;
918 }
919 
920 
921 /*
922  * Request:
923  *      NameZ
924  *      Mode
925  *
926  * Response:
927  *
928  */
929 krb5_error_code
930 _krb5_kcm_chmod(krb5_context context,
931 		krb5_ccache id,
932 		uint16_t mode)
933 {
934     krb5_error_code ret;
935     krb5_kcmcache *k = KCMCACHE(id);
936     krb5_storage *request;
937 
938     ret = kcm_storage_request(context, KCM_OP_CHMOD, &request);
939     if (ret)
940 	return ret;
941 
942     ret = krb5_store_stringz(request, k->name);
943     if (ret) {
944 	krb5_storage_free(request);
945 	return ret;
946     }
947 
948     ret = krb5_store_int16(request, mode);
949     if (ret) {
950 	krb5_storage_free(request);
951 	return ret;
952     }
953 
954     ret = kcm_call(context, k, request, NULL, NULL);
955 
956     krb5_storage_free(request);
957     return ret;
958 }
959 
960 
961 /*
962  * Request:
963  *      NameZ
964  *      UID
965  *      GID
966  *
967  * Response:
968  *
969  */
970 krb5_error_code
971 _krb5_kcm_chown(krb5_context context,
972 		krb5_ccache id,
973 		uint32_t uid,
974 		uint32_t gid)
975 {
976     krb5_error_code ret;
977     krb5_kcmcache *k = KCMCACHE(id);
978     krb5_storage *request;
979 
980     ret = kcm_storage_request(context, KCM_OP_CHOWN, &request);
981     if (ret)
982 	return ret;
983 
984     ret = krb5_store_stringz(request, k->name);
985     if (ret) {
986 	krb5_storage_free(request);
987 	return ret;
988     }
989 
990     ret = krb5_store_int32(request, uid);
991     if (ret) {
992 	krb5_storage_free(request);
993 	return ret;
994     }
995 
996     ret = krb5_store_int32(request, gid);
997     if (ret) {
998 	krb5_storage_free(request);
999 	return ret;
1000     }
1001 
1002     ret = kcm_call(context, k, request, NULL, NULL);
1003 
1004     krb5_storage_free(request);
1005     return ret;
1006 }
1007 
1008 
1009 /*
1010  * Request:
1011  *      NameZ
1012  *      ServerPrincipalPresent
1013  *      ServerPrincipal OPTIONAL
1014  *      Key
1015  *
1016  * Repsonse:
1017  *
1018  */
1019 krb5_error_code
1020 _krb5_kcm_get_initial_ticket(krb5_context context,
1021 			     krb5_ccache id,
1022 			     krb5_principal server,
1023 			     krb5_keyblock *key)
1024 {
1025     krb5_error_code ret;
1026     krb5_kcmcache *k = KCMCACHE(id);
1027     krb5_storage *request;
1028 
1029     ret = kcm_storage_request(context, KCM_OP_GET_INITIAL_TICKET, &request);
1030     if (ret)
1031 	return ret;
1032 
1033     ret = krb5_store_stringz(request, k->name);
1034     if (ret) {
1035 	krb5_storage_free(request);
1036 	return ret;
1037     }
1038 
1039     ret = krb5_store_int8(request, (server == NULL) ? 0 : 1);
1040     if (ret) {
1041 	krb5_storage_free(request);
1042 	return ret;
1043     }
1044 
1045     if (server != NULL) {
1046 	ret = krb5_store_principal(request, server);
1047 	if (ret) {
1048 	    krb5_storage_free(request);
1049 	    return ret;
1050 	}
1051     }
1052 
1053     ret = krb5_store_keyblock(request, *key);
1054     if (ret) {
1055 	krb5_storage_free(request);
1056 	return ret;
1057     }
1058 
1059     ret = kcm_call(context, k, request, NULL, NULL);
1060 
1061     krb5_storage_free(request);
1062     return ret;
1063 }
1064 
1065 
1066 /*
1067  * Request:
1068  *      NameZ
1069  *      KDCFlags
1070  *      EncryptionType
1071  *      ServerPrincipal
1072  *
1073  * Repsonse:
1074  *
1075  */
1076 krb5_error_code
1077 _krb5_kcm_get_ticket(krb5_context context,
1078 		     krb5_ccache id,
1079 		     krb5_kdc_flags flags,
1080 		     krb5_enctype enctype,
1081 		     krb5_principal server)
1082 {
1083     krb5_error_code ret;
1084     krb5_kcmcache *k = KCMCACHE(id);
1085     krb5_storage *request;
1086 
1087     ret = kcm_storage_request(context, KCM_OP_GET_TICKET, &request);
1088     if (ret)
1089 	return ret;
1090 
1091     ret = krb5_store_stringz(request, k->name);
1092     if (ret) {
1093 	krb5_storage_free(request);
1094 	return ret;
1095     }
1096 
1097     ret = krb5_store_int32(request, flags.i);
1098     if (ret) {
1099 	krb5_storage_free(request);
1100 	return ret;
1101     }
1102 
1103     ret = krb5_store_int32(request, enctype);
1104     if (ret) {
1105 	krb5_storage_free(request);
1106 	return ret;
1107     }
1108 
1109     ret = krb5_store_principal(request, server);
1110     if (ret) {
1111 	krb5_storage_free(request);
1112 	return ret;
1113     }
1114 
1115     ret = kcm_call(context, k, request, NULL, NULL);
1116 
1117     krb5_storage_free(request);
1118     return ret;
1119 }
1120 
1121 
1122 #endif /* HAVE_KCM */
1123