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
46RCSID("$Id: kcm.c 22108 2007-12-03 17:23:53Z lha $");
47
48typedef 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
58static krb5_error_code
59try_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
97static krb5_error_code
98try_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
120static krb5_error_code
121kcm_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
160static krb5_error_code
161kcm_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
197static krb5_error_code
198kcm_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
241static krb5_error_code
242kcm_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
293static void
294kcm_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
310static const char *
311kcm_get_name(krb5_context context,
312	     krb5_ccache id)
313{
314    return CACHENAME(id);
315}
316
317static krb5_error_code
318kcm_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 */
329static krb5_error_code
330kcm_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 */
378static krb5_error_code
379kcm_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
409static krb5_error_code
410kcm_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 */
424static krb5_error_code
425kcm_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 */
456static krb5_error_code
457kcm_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 */
497static krb5_error_code
498kcm_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 */
555static krb5_error_code
556kcm_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 */
600static krb5_error_code
601kcm_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 */
655static krb5_error_code
656kcm_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 */
707static krb5_error_code
708kcm_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 */
756static krb5_error_code
757kcm_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
794static krb5_error_code
795kcm_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
825static krb5_error_code
826kcm_get_version(krb5_context context,
827		krb5_ccache id)
828{
829    return 0;
830}
831
832static krb5_error_code
833kcm_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
839static krb5_error_code
840kcm_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
853const 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
877krb5_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 */
902krb5_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 */
929krb5_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 */
970krb5_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 */
1019krb5_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 */
1076krb5_error_code
1077_krb5_kcm_get_ticket(krb5_context context,
1078		     krb5_ccache id,
1079		     krb5_kdc_flags flags,
1080		     krb5_enctype enctype,
1081		     krb5_principal server)
1082{
1083    krb5_error_code ret;
1084    krb5_kcmcache *k = KCMCACHE(id);
1085    krb5_storage *request;
1086
1087    ret = kcm_storage_request(context, KCM_OP_GET_TICKET, &request);
1088    if (ret)
1089	return ret;
1090
1091    ret = krb5_store_stringz(request, k->name);
1092    if (ret) {
1093	krb5_storage_free(request);
1094	return ret;
1095    }
1096
1097    ret = krb5_store_int32(request, flags.i);
1098    if (ret) {
1099	krb5_storage_free(request);
1100	return ret;
1101    }
1102
1103    ret = krb5_store_int32(request, enctype);
1104    if (ret) {
1105	krb5_storage_free(request);
1106	return ret;
1107    }
1108
1109    ret = krb5_store_principal(request, server);
1110    if (ret) {
1111	krb5_storage_free(request);
1112	return ret;
1113    }
1114
1115    ret = kcm_call(context, k, request, NULL, NULL);
1116
1117    krb5_storage_free(request);
1118    return ret;
1119}
1120
1121
1122#endif /* HAVE_KCM */
1123