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