]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - crypto/heimdal/lib/krb5/kcm.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / crypto / 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: 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 */