r8302: import mini HEIMDAL into the tree
[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: kcm.c,v 1.7 2005/06/17 04:20:11 lha Exp $");
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)    (*(u_int32_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, status;
250     krb5_storage *response;
251
252     if (response_p != NULL)
253         *response_p = NULL;
254
255     ret = kcm_send_request(context, k, request, &response_data);
256     if (ret) {
257         return ret;
258     }
259
260     response = krb5_storage_from_data(&response_data);
261     if (response == NULL) {
262         krb5_data_free(&response_data);
263         return KRB5_CC_IO;
264     }
265
266     ret = krb5_ret_int32(response, &status);
267     if (ret) {
268         krb5_storage_free(response);
269         krb5_data_free(&response_data);
270         return KRB5_CC_FORMAT;
271     }
272
273     if (status) {
274         krb5_storage_free(response);
275         krb5_data_free(&response_data);
276         return status;
277     }
278
279     if (response_p != NULL) {
280         *response_data_p = response_data;
281         *response_p = response;
282
283         return 0;
284     }
285
286     krb5_storage_free(response);
287     krb5_data_free(&response_data);
288
289     return 0;
290 }
291
292 static void
293 kcm_free(krb5_context context, krb5_ccache *id)
294 {
295     krb5_kcmcache *k = KCMCACHE(*id);
296
297     if (k != NULL) {
298         if (k->name != NULL)
299             free(k->name);
300         if (k->door_path)
301             free(k->door_path);
302         memset(k, 0, sizeof(*k));
303         krb5_data_free(&(*id)->data);
304     }
305
306     *id = NULL;
307 }
308
309 static const char *
310 kcm_get_name(krb5_context context,
311              krb5_ccache id)
312 {
313     return CACHENAME(id);
314 }
315
316 static krb5_error_code
317 kcm_resolve(krb5_context context, krb5_ccache *id, const char *res)
318 {
319     return kcm_alloc(context, res, id);
320 }
321
322 /*
323  * Request:
324  *
325  * Response:
326  *      NameZ
327  */
328 static krb5_error_code
329 kcm_gen_new(krb5_context context, krb5_ccache *id)
330 {
331     krb5_kcmcache *k;
332     krb5_error_code ret;
333     krb5_storage *request, *response;
334     krb5_data response_data;
335
336     ret = kcm_alloc(context, NULL, id);
337     if (ret)
338         return ret;
339
340     k = KCMCACHE(*id);
341
342     ret = kcm_storage_request(context, KCM_OP_GEN_NEW, &request);
343     if (ret) {
344         kcm_free(context, id);
345         return ret;
346     }
347
348     ret = kcm_call(context, k, request, &response, &response_data);
349     if (ret) {
350         krb5_storage_free(request);
351         kcm_free(context, id);
352         return ret;
353     }
354
355     ret = krb5_ret_stringz(response, &k->name);
356     if (ret)
357         ret = KRB5_CC_IO;
358
359     krb5_storage_free(request);
360     krb5_storage_free(response);
361     krb5_data_free(&response_data);
362
363     if (ret)
364         kcm_free(context, id);
365
366     return ret;
367 }
368
369 /*
370  * Request:
371  *      NameZ
372  *      Principal
373  *
374  * Response:
375  *
376  */
377 static krb5_error_code
378 kcm_initialize(krb5_context context,
379                krb5_ccache id,
380                krb5_principal primary_principal)
381 {
382     krb5_error_code ret;
383     krb5_kcmcache *k = KCMCACHE(id);
384     krb5_storage *request;
385
386     ret = kcm_storage_request(context, KCM_OP_INITIALIZE, &request);
387     if (ret)
388         return ret;
389
390     ret = krb5_store_stringz(request, k->name);
391     if (ret) {
392         krb5_storage_free(request);
393         return ret;
394     }
395
396     ret = krb5_store_principal(request, primary_principal);
397     if (ret) {
398         krb5_storage_free(request);
399         return ret;
400     }
401
402     ret = kcm_call(context, k, request, NULL, NULL);
403
404     krb5_storage_free(request);
405     return ret;
406 }
407
408 static krb5_error_code
409 kcm_close(krb5_context context,
410           krb5_ccache id)
411 {
412     kcm_free(context, &id);
413     return 0;
414 }
415
416 /*
417  * Request:
418  *      NameZ
419  *
420  * Response:
421  *
422  */
423 static krb5_error_code
424 kcm_destroy(krb5_context context,
425             krb5_ccache id)
426 {
427     krb5_error_code ret;
428     krb5_kcmcache *k = KCMCACHE(id);
429     krb5_storage *request;
430
431     ret = kcm_storage_request(context, KCM_OP_DESTROY, &request);
432     if (ret)
433         return ret;
434
435     ret = krb5_store_stringz(request, k->name);
436     if (ret) {
437         krb5_storage_free(request);
438         return ret;
439     }
440
441     ret = kcm_call(context, k, request, NULL, NULL);
442
443     krb5_storage_free(request);
444     return ret;
445 }
446
447 /*
448  * Request:
449  *      NameZ
450  *      Creds
451  *
452  * Response:
453  *
454  */
455 static krb5_error_code
456 kcm_store_cred(krb5_context context,
457                krb5_ccache id,
458                krb5_creds *creds)
459 {
460     krb5_error_code ret;
461     krb5_kcmcache *k = KCMCACHE(id);
462     krb5_storage *request;
463
464     ret = kcm_storage_request(context, KCM_OP_STORE, &request);
465     if (ret)
466         return ret;
467
468     ret = krb5_store_stringz(request, k->name);
469     if (ret) {
470         krb5_storage_free(request);
471         return ret;
472     }
473
474     ret = krb5_store_creds(request, creds);
475     if (ret) {
476         krb5_storage_free(request);
477         return ret;
478     }
479
480     ret = kcm_call(context, k, request, NULL, NULL);
481
482     krb5_storage_free(request);
483     return ret;
484 }
485
486 /*
487  * Request:
488  *      NameZ
489  *      WhichFields
490  *      MatchCreds
491  *
492  * Response:
493  *      Creds
494  *
495  */
496 static krb5_error_code
497 kcm_retrieve(krb5_context context,
498              krb5_ccache id,
499              krb5_flags which,
500              const krb5_creds *mcred,
501              krb5_creds *creds)
502 {
503     krb5_error_code ret;
504     krb5_kcmcache *k = KCMCACHE(id);
505     krb5_storage *request, *response;
506     krb5_data response_data;
507
508     ret = kcm_storage_request(context, KCM_OP_RETRIEVE, &request);
509     if (ret)
510         return ret;
511
512     ret = krb5_store_stringz(request, k->name);
513     if (ret) {
514         krb5_storage_free(request);
515         return ret;
516     }
517
518     ret = krb5_store_int32(request, which);
519     if (ret) {
520         krb5_storage_free(request);
521         return ret;
522     }
523
524     ret = krb5_store_creds_tag(request, rk_UNCONST(mcred));
525     if (ret) {
526         krb5_storage_free(request);
527         return ret;
528     }
529
530     ret = kcm_call(context, k, request, &response, &response_data);
531     if (ret) {
532         krb5_storage_free(request);
533         return ret;
534     }
535
536     ret = krb5_ret_creds(response, creds);
537     if (ret)
538         ret = KRB5_CC_IO;
539
540     krb5_storage_free(request);
541     krb5_storage_free(response);
542     krb5_data_free(&response_data);
543
544     return ret;
545 }
546
547 /*
548  * Request:
549  *      NameZ
550  *
551  * Response:
552  *      Principal
553  */
554 static krb5_error_code
555 kcm_get_principal(krb5_context context,
556                   krb5_ccache id,
557                   krb5_principal *principal)
558 {
559     krb5_error_code ret;
560     krb5_kcmcache *k = KCMCACHE(id);
561     krb5_storage *request, *response;
562     krb5_data response_data;
563
564     ret = kcm_storage_request(context, KCM_OP_GET_PRINCIPAL, &request);
565     if (ret)
566         return ret;
567
568     ret = krb5_store_stringz(request, k->name);
569     if (ret) {
570         krb5_storage_free(request);
571         return ret;
572     }
573
574     ret = kcm_call(context, k, request, &response, &response_data);
575     if (ret) {
576         krb5_storage_free(request);
577         return ret;
578     }
579
580     ret = krb5_ret_principal(response, principal);
581     if (ret)
582         ret = KRB5_CC_IO;
583
584     krb5_storage_free(request);
585     krb5_storage_free(response);
586     krb5_data_free(&response_data);
587
588     return ret;
589 }
590
591 /*
592  * Request:
593  *      NameZ
594  *
595  * Response:
596  *      Cursor
597  *
598  */
599 static krb5_error_code
600 kcm_get_first (krb5_context context,
601                krb5_ccache id,
602                krb5_cc_cursor *cursor)
603 {
604     krb5_error_code ret;
605     krb5_kcmcache *k = KCMCACHE(id);
606     krb5_storage *request, *response;
607     krb5_data response_data;
608     u_int32_t tmp;
609
610     ret = kcm_storage_request(context, KCM_OP_GET_FIRST, &request);
611     if (ret)
612         return ret;
613
614     ret = krb5_store_stringz(request, k->name);
615     if (ret) {
616         krb5_storage_free(request);
617         return ret;
618     }
619
620     ret = kcm_call(context, k, request, &response, &response_data);
621     if (ret) {
622         krb5_storage_free(request);
623         return ret;
624     }
625
626     ret = krb5_ret_int32(response, &tmp);
627     if (ret)
628         ret = KRB5_CC_IO;
629
630     krb5_storage_free(request);
631     krb5_storage_free(response);
632     krb5_data_free(&response_data);
633
634     if (ret)
635         return ret;
636
637     *cursor = malloc(sizeof(tmp));
638     if (*cursor == NULL)
639         return KRB5_CC_NOMEM;
640
641     KCMCURSOR(*cursor) = tmp;
642
643     return 0;
644 }
645
646 /*
647  * Request:
648  *      NameZ
649  *      Cursor
650  *
651  * Response:
652  *      Creds
653  */
654 static krb5_error_code
655 kcm_get_next (krb5_context context,
656                 krb5_ccache id,
657                 krb5_cc_cursor *cursor,
658                 krb5_creds *creds)
659 {
660     krb5_error_code ret;
661     krb5_kcmcache *k = KCMCACHE(id);
662     krb5_storage *request, *response;
663     krb5_data response_data;
664
665     ret = kcm_storage_request(context, KCM_OP_GET_NEXT, &request);
666     if (ret)
667         return ret;
668
669     ret = krb5_store_stringz(request, k->name);
670     if (ret) {
671         krb5_storage_free(request);
672         return ret;
673     }
674
675     ret = krb5_store_int32(request, KCMCURSOR(*cursor));
676     if (ret) {
677         krb5_storage_free(request);
678         return ret;
679     }
680
681     ret = kcm_call(context, k, request, &response, &response_data);
682     if (ret) {
683         krb5_storage_free(request);
684         return ret;
685     }
686
687     ret = krb5_ret_creds(response, creds);
688     if (ret)
689         ret = KRB5_CC_IO;
690
691     krb5_storage_free(request);
692     krb5_storage_free(response);
693     krb5_data_free(&response_data);
694
695     return ret;
696 }
697
698 /*
699  * Request:
700  *      NameZ
701  *      Cursor
702  *
703  * Response:
704  *
705  */
706 static krb5_error_code
707 kcm_end_get (krb5_context context,
708              krb5_ccache id,
709              krb5_cc_cursor *cursor)
710 {
711     krb5_error_code ret;
712     krb5_kcmcache *k = KCMCACHE(id);
713     krb5_storage *request;
714
715     ret = kcm_storage_request(context, KCM_OP_END_GET, &request);
716     if (ret)
717         return ret;
718
719     ret = krb5_store_stringz(request, k->name);
720     if (ret) {
721         krb5_storage_free(request);
722         return ret;
723     }
724
725     ret = krb5_store_int32(request, KCMCURSOR(*cursor));
726     if (ret) {
727         krb5_storage_free(request);
728         return ret;
729     }
730
731     ret = kcm_call(context, k, request, NULL, NULL);
732     if (ret) {
733         krb5_storage_free(request);
734         return ret;
735     }
736   
737     krb5_storage_free(request);
738
739     KCMCURSOR(*cursor) = 0;
740     free(*cursor);
741     *cursor = NULL;
742
743     return ret;
744 }
745
746 /*
747  * Request:
748  *      NameZ
749  *      WhichFields
750  *      MatchCreds
751  *
752  * Response:
753  *
754  */
755 static krb5_error_code
756 kcm_remove_cred(krb5_context context,
757                 krb5_ccache id,
758                 krb5_flags which,
759                 krb5_creds *cred)
760 {
761     krb5_error_code ret;
762     krb5_kcmcache *k = KCMCACHE(id);
763     krb5_storage *request;
764
765     ret = kcm_storage_request(context, KCM_OP_REMOVE_CRED, &request);
766     if (ret)
767         return ret;
768
769     ret = krb5_store_stringz(request, k->name);
770     if (ret) {
771         krb5_storage_free(request);
772         return ret;
773     }
774
775     ret = krb5_store_int32(request, which);
776     if (ret) {
777         krb5_storage_free(request);
778         return ret;
779     }
780
781     ret = krb5_store_creds_tag(request, cred);
782     if (ret) {
783         krb5_storage_free(request);
784         return ret;
785     }
786
787     ret = kcm_call(context, k, request, NULL, NULL);
788
789     krb5_storage_free(request);
790     return ret;
791 }
792
793 static krb5_error_code
794 kcm_set_flags(krb5_context context,
795               krb5_ccache id,
796               krb5_flags flags)
797 {
798     krb5_error_code ret;
799     krb5_kcmcache *k = KCMCACHE(id);
800     krb5_storage *request;
801
802     ret = kcm_storage_request(context, KCM_OP_SET_FLAGS, &request);
803     if (ret)
804         return ret;
805
806     ret = krb5_store_stringz(request, k->name);
807     if (ret) {
808         krb5_storage_free(request);
809         return ret;
810     }
811
812     ret = krb5_store_int32(request, flags);
813     if (ret) {
814         krb5_storage_free(request);
815         return ret;
816     }
817
818     ret = kcm_call(context, k, request, NULL, NULL);
819
820     krb5_storage_free(request);
821     return ret;
822 }
823
824 static krb5_error_code
825 kcm_get_version(krb5_context context,
826                 krb5_ccache id)
827 {
828     return 0;
829 }
830
831 const krb5_cc_ops krb5_kcm_ops = {
832     "KCM",
833     kcm_get_name,
834     kcm_resolve,
835     kcm_gen_new,
836     kcm_initialize,
837     kcm_destroy,
838     kcm_close,
839     kcm_store_cred,
840     kcm_retrieve,
841     kcm_get_principal,
842     kcm_get_first,
843     kcm_get_next,
844     kcm_end_get,
845     kcm_remove_cred,
846     kcm_set_flags,
847     kcm_get_version
848 };
849
850 krb5_boolean
851 _krb5_kcm_is_running(krb5_context context)
852 {
853     krb5_error_code ret;
854     krb5_ccache_data ccdata;
855     krb5_ccache id = &ccdata;
856     krb5_boolean running;
857
858     ret = kcm_alloc(context, NULL, &id);
859     if (ret)
860         return 0;
861
862     running = (_krb5_kcm_noop(context, id) == 0);
863
864     kcm_free(context, &id);
865
866     return running;
867 }
868
869 /*
870  * Request:
871  *
872  * Response:
873  *
874  */
875 krb5_error_code
876 _krb5_kcm_noop(krb5_context context,
877                krb5_ccache id)
878 {
879     krb5_error_code ret;
880     krb5_kcmcache *k = KCMCACHE(id);
881     krb5_storage *request;
882
883     ret = kcm_storage_request(context, KCM_OP_NOOP, &request);
884     if (ret)
885         return ret;
886
887     ret = kcm_call(context, k, request, NULL, NULL);
888
889     krb5_storage_free(request);
890     return ret;
891 }
892
893
894 /*
895  * Request:
896  *      NameZ
897  *      Mode
898  *
899  * Response:
900  *
901  */
902 krb5_error_code
903 _krb5_kcm_chmod(krb5_context context,
904                 krb5_ccache id,
905                 u_int16_t mode)
906 {
907     krb5_error_code ret;
908     krb5_kcmcache *k = KCMCACHE(id);
909     krb5_storage *request;
910
911     ret = kcm_storage_request(context, KCM_OP_CHMOD, &request);
912     if (ret)
913         return ret;
914
915     ret = krb5_store_stringz(request, k->name);
916     if (ret) {
917         krb5_storage_free(request);
918         return ret;
919     }
920
921     ret = krb5_store_int16(request, mode);
922     if (ret) {
923         krb5_storage_free(request);
924         return ret;
925     }
926
927     ret = kcm_call(context, k, request, NULL, NULL);
928
929     krb5_storage_free(request);
930     return ret;
931 }
932
933
934 /*
935  * Request:
936  *      NameZ
937  *      UID
938  *      GID
939  *
940  * Response:
941  *
942  */
943 krb5_error_code
944 _krb5_kcm_chown(krb5_context context,
945                 krb5_ccache id,
946                 u_int32_t uid,
947                 u_int32_t gid)
948 {
949     krb5_error_code ret;
950     krb5_kcmcache *k = KCMCACHE(id);
951     krb5_storage *request;
952
953     ret = kcm_storage_request(context, KCM_OP_CHOWN, &request);
954     if (ret)
955         return ret;
956
957     ret = krb5_store_stringz(request, k->name);
958     if (ret) {
959         krb5_storage_free(request);
960         return ret;
961     }
962
963     ret = krb5_store_int32(request, uid);
964     if (ret) {
965         krb5_storage_free(request);
966         return ret;
967     }
968
969     ret = krb5_store_int32(request, gid);
970     if (ret) {
971         krb5_storage_free(request);
972         return ret;
973     }
974
975     ret = kcm_call(context, k, request, NULL, NULL);
976
977     krb5_storage_free(request);
978     return ret;
979 }
980
981
982 /*
983  * Request:
984  *      NameZ
985  *      ServerPrincipalPresent
986  *      ServerPrincipal OPTIONAL
987  *      Key
988  *
989  * Repsonse:
990  *
991  */
992 krb5_error_code
993 _krb5_kcm_get_initial_ticket(krb5_context context,
994                              krb5_ccache id,
995                              krb5_principal server,
996                              krb5_keyblock *key)
997 {
998     krb5_error_code ret;
999     krb5_kcmcache *k = KCMCACHE(id);
1000     krb5_storage *request;
1001
1002     ret = kcm_storage_request(context, KCM_OP_GET_INITIAL_TICKET, &request);
1003     if (ret)
1004         return ret;
1005
1006     ret = krb5_store_stringz(request, k->name);
1007     if (ret) {
1008         krb5_storage_free(request);
1009         return ret;
1010     }
1011
1012     ret = krb5_store_int8(request, (server == NULL) ? 0 : 1);
1013     if (ret) {
1014         krb5_storage_free(request);
1015         return ret;
1016     }
1017
1018     if (server != NULL) {
1019         ret = krb5_store_principal(request, server);
1020         if (ret) {
1021             krb5_storage_free(request);
1022             return ret;
1023         }
1024     }
1025
1026     ret = krb5_store_keyblock(request, *key);
1027     if (ret) {
1028         krb5_storage_free(request);
1029         return ret;
1030     }
1031
1032     ret = kcm_call(context, k, request, NULL, NULL);
1033
1034     krb5_storage_free(request);
1035     return ret;
1036 }
1037
1038
1039 /*
1040  * Request:
1041  *      NameZ
1042  *      KDCFlags
1043  *      EncryptionType
1044  *      ServerPrincipal
1045  *
1046  * Repsonse:
1047  *
1048  */
1049 krb5_error_code
1050 _krb5_kcm_get_ticket(krb5_context context,
1051                      krb5_ccache id,
1052                      krb5_kdc_flags flags,
1053                      krb5_enctype enctype,
1054                      krb5_principal server)
1055 {
1056     krb5_error_code ret;
1057     krb5_kcmcache *k = KCMCACHE(id);
1058     krb5_storage *request;
1059
1060     ret = kcm_storage_request(context, KCM_OP_GET_TICKET, &request);
1061     if (ret)
1062         return ret;
1063
1064     ret = krb5_store_stringz(request, k->name);
1065     if (ret) {
1066         krb5_storage_free(request);
1067         return ret;
1068     }
1069
1070     ret = krb5_store_int32(request, flags.i);
1071     if (ret) {
1072         krb5_storage_free(request);
1073         return ret;
1074     }
1075
1076     ret = krb5_store_int32(request, enctype);
1077     if (ret) {
1078         krb5_storage_free(request);
1079         return ret;
1080     }
1081
1082     ret = krb5_store_principal(request, server);
1083     if (ret) {
1084         krb5_storage_free(request);
1085         return ret;
1086     }
1087
1088     ret = kcm_call(context, k, request, NULL, NULL);
1089
1090     krb5_storage_free(request);
1091     return ret;
1092 }
1093
1094
1095 #endif /* HAVE_KCM */