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