1178825Sdfr/*
2178825Sdfr * Copyright (c) 2005, PADL Software Pty Ltd.
3178825Sdfr * All rights reserved.
4178825Sdfr *
5233294Sstas * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
6233294Sstas *
7178825Sdfr * Redistribution and use in source and binary forms, with or without
8178825Sdfr * modification, are permitted provided that the following conditions
9178825Sdfr * are met:
10178825Sdfr *
11178825Sdfr * 1. Redistributions of source code must retain the above copyright
12178825Sdfr *    notice, this list of conditions and the following disclaimer.
13178825Sdfr *
14178825Sdfr * 2. Redistributions in binary form must reproduce the above copyright
15178825Sdfr *    notice, this list of conditions and the following disclaimer in the
16178825Sdfr *    documentation and/or other materials provided with the distribution.
17178825Sdfr *
18178825Sdfr * 3. Neither the name of PADL Software nor the names of its contributors
19178825Sdfr *    may be used to endorse or promote products derived from this software
20178825Sdfr *    without specific prior written permission.
21178825Sdfr *
22178825Sdfr * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
23178825Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24178825Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25178825Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
26178825Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27178825Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28178825Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29178825Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30178825Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31178825Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32178825Sdfr * SUCH DAMAGE.
33178825Sdfr */
34178825Sdfr
35178825Sdfr#include "krb5_locl.h"
36178825Sdfr
37178825Sdfr#ifdef HAVE_KCM
38178825Sdfr/*
39178825Sdfr * Client library for Kerberos Credentials Manager (KCM) daemon
40178825Sdfr */
41178825Sdfr
42178825Sdfr#include "kcm.h"
43233294Sstas#include <heim-ipc.h>
44178825Sdfr
45233294Sstasstatic krb5_error_code
46233294Sstaskcm_set_kdc_offset(krb5_context, krb5_ccache, krb5_deltat);
47178825Sdfr
48233294Sstasstatic const char *kcm_ipc_name = "ANY:org.h5l.kcm";
49233294Sstas
50178825Sdfrtypedef struct krb5_kcmcache {
51178825Sdfr    char *name;
52178825Sdfr} krb5_kcmcache;
53178825Sdfr
54233294Sstastypedef struct krb5_kcm_cursor {
55233294Sstas    unsigned long offset;
56233294Sstas    unsigned long length;
57233294Sstas    kcmuuid_t *uuids;
58233294Sstas} *krb5_kcm_cursor;
59233294Sstas
60233294Sstas
61178825Sdfr#define KCMCACHE(X)	((krb5_kcmcache *)(X)->data.data)
62178825Sdfr#define CACHENAME(X)	(KCMCACHE(X)->name)
63233294Sstas#define KCMCURSOR(C)	((krb5_kcm_cursor)(C))
64178825Sdfr
65233294Sstasstatic HEIMDAL_MUTEX kcm_mutex = HEIMDAL_MUTEX_INITIALIZER;
66233294Sstasstatic heim_ipc kcm_ipc = NULL;
67178825Sdfr
68178825Sdfrstatic krb5_error_code
69178825Sdfrkcm_send_request(krb5_context context,
70178825Sdfr		 krb5_storage *request,
71178825Sdfr		 krb5_data *response_data)
72178825Sdfr{
73233294Sstas    krb5_error_code ret = 0;
74178825Sdfr    krb5_data request_data;
75178825Sdfr
76233294Sstas    HEIMDAL_MUTEX_lock(&kcm_mutex);
77233294Sstas    if (kcm_ipc == NULL)
78233294Sstas	ret = heim_ipc_init_context(kcm_ipc_name, &kcm_ipc);
79233294Sstas    HEIMDAL_MUTEX_unlock(&kcm_mutex);
80233294Sstas    if (ret)
81233294Sstas	return KRB5_CC_NOSUPP;
82178825Sdfr
83178825Sdfr    ret = krb5_storage_to_data(request, &request_data);
84178825Sdfr    if (ret) {
85233294Sstas	krb5_clear_error_message(context);
86178825Sdfr	return KRB5_CC_NOMEM;
87178825Sdfr    }
88178825Sdfr
89233294Sstas    ret = heim_ipc_call(kcm_ipc, &request_data, response_data, NULL);
90178825Sdfr    krb5_data_free(&request_data);
91178825Sdfr
92178825Sdfr    if (ret) {
93233294Sstas	krb5_clear_error_message(context);
94233294Sstas	ret = KRB5_CC_NOSUPP;
95178825Sdfr    }
96178825Sdfr
97178825Sdfr    return ret;
98178825Sdfr}
99178825Sdfr
100233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
101233294Sstaskrb5_kcm_storage_request(krb5_context context,
102233294Sstas			 uint16_t opcode,
103233294Sstas			 krb5_storage **storage_p)
104178825Sdfr{
105178825Sdfr    krb5_storage *sp;
106178825Sdfr    krb5_error_code ret;
107178825Sdfr
108178825Sdfr    *storage_p = NULL;
109178825Sdfr
110178825Sdfr    sp = krb5_storage_emem();
111178825Sdfr    if (sp == NULL) {
112233294Sstas	krb5_set_error_message(context, KRB5_CC_NOMEM, N_("malloc: out of memory", ""));
113178825Sdfr	return KRB5_CC_NOMEM;
114178825Sdfr    }
115178825Sdfr
116178825Sdfr    /* Send MAJOR | VERSION | OPCODE */
117178825Sdfr    ret  = krb5_store_int8(sp, KCM_PROTOCOL_VERSION_MAJOR);
118178825Sdfr    if (ret)
119178825Sdfr	goto fail;
120178825Sdfr    ret = krb5_store_int8(sp, KCM_PROTOCOL_VERSION_MINOR);
121178825Sdfr    if (ret)
122178825Sdfr	goto fail;
123178825Sdfr    ret = krb5_store_int16(sp, opcode);
124178825Sdfr    if (ret)
125178825Sdfr	goto fail;
126178825Sdfr
127178825Sdfr    *storage_p = sp;
128178825Sdfr fail:
129178825Sdfr    if (ret) {
130233294Sstas	krb5_set_error_message(context, ret,
131233294Sstas			       N_("Failed to encode KCM request", ""));
132178825Sdfr	krb5_storage_free(sp);
133178825Sdfr    }
134233294Sstas
135233294Sstas    return ret;
136178825Sdfr}
137178825Sdfr
138178825Sdfrstatic krb5_error_code
139178825Sdfrkcm_alloc(krb5_context context, const char *name, krb5_ccache *id)
140178825Sdfr{
141178825Sdfr    krb5_kcmcache *k;
142178825Sdfr
143178825Sdfr    k = malloc(sizeof(*k));
144178825Sdfr    if (k == NULL) {
145233294Sstas	krb5_set_error_message(context, KRB5_CC_NOMEM,
146233294Sstas			       N_("malloc: out of memory", ""));
147178825Sdfr	return KRB5_CC_NOMEM;
148178825Sdfr    }
149178825Sdfr
150178825Sdfr    if (name != NULL) {
151178825Sdfr	k->name = strdup(name);
152178825Sdfr	if (k->name == NULL) {
153178825Sdfr	    free(k);
154233294Sstas	    krb5_set_error_message(context, KRB5_CC_NOMEM,
155233294Sstas				   N_("malloc: out of memory", ""));
156178825Sdfr	    return KRB5_CC_NOMEM;
157178825Sdfr	}
158178825Sdfr    } else
159178825Sdfr	k->name = NULL;
160178825Sdfr
161178825Sdfr    (*id)->data.data = k;
162178825Sdfr    (*id)->data.length = sizeof(*k);
163178825Sdfr
164178825Sdfr    return 0;
165178825Sdfr}
166178825Sdfr
167233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
168233294Sstaskrb5_kcm_call(krb5_context context,
169233294Sstas	      krb5_storage *request,
170233294Sstas	      krb5_storage **response_p,
171233294Sstas	      krb5_data *response_data_p)
172178825Sdfr{
173178825Sdfr    krb5_data response_data;
174178825Sdfr    krb5_error_code ret;
175178825Sdfr    int32_t status;
176178825Sdfr    krb5_storage *response;
177178825Sdfr
178178825Sdfr    if (response_p != NULL)
179178825Sdfr	*response_p = NULL;
180178825Sdfr
181233294Sstas    krb5_data_zero(&response_data);
182233294Sstas
183233294Sstas    ret = kcm_send_request(context, request, &response_data);
184233294Sstas    if (ret)
185178825Sdfr	return ret;
186178825Sdfr
187178825Sdfr    response = krb5_storage_from_data(&response_data);
188178825Sdfr    if (response == NULL) {
189178825Sdfr	krb5_data_free(&response_data);
190178825Sdfr	return KRB5_CC_IO;
191178825Sdfr    }
192178825Sdfr
193178825Sdfr    ret = krb5_ret_int32(response, &status);
194178825Sdfr    if (ret) {
195178825Sdfr	krb5_storage_free(response);
196178825Sdfr	krb5_data_free(&response_data);
197178825Sdfr	return KRB5_CC_FORMAT;
198178825Sdfr    }
199178825Sdfr
200178825Sdfr    if (status) {
201178825Sdfr	krb5_storage_free(response);
202178825Sdfr	krb5_data_free(&response_data);
203178825Sdfr	return status;
204178825Sdfr    }
205178825Sdfr
206178825Sdfr    if (response_p != NULL) {
207178825Sdfr	*response_data_p = response_data;
208178825Sdfr	*response_p = response;
209178825Sdfr
210178825Sdfr	return 0;
211178825Sdfr    }
212178825Sdfr
213178825Sdfr    krb5_storage_free(response);
214178825Sdfr    krb5_data_free(&response_data);
215178825Sdfr
216178825Sdfr    return 0;
217178825Sdfr}
218178825Sdfr
219178825Sdfrstatic void
220178825Sdfrkcm_free(krb5_context context, krb5_ccache *id)
221178825Sdfr{
222178825Sdfr    krb5_kcmcache *k = KCMCACHE(*id);
223178825Sdfr
224178825Sdfr    if (k != NULL) {
225178825Sdfr	if (k->name != NULL)
226178825Sdfr	    free(k->name);
227178825Sdfr	memset(k, 0, sizeof(*k));
228178825Sdfr	krb5_data_free(&(*id)->data);
229178825Sdfr    }
230178825Sdfr}
231178825Sdfr
232178825Sdfrstatic const char *
233178825Sdfrkcm_get_name(krb5_context context,
234178825Sdfr	     krb5_ccache id)
235178825Sdfr{
236178825Sdfr    return CACHENAME(id);
237178825Sdfr}
238178825Sdfr
239178825Sdfrstatic krb5_error_code
240178825Sdfrkcm_resolve(krb5_context context, krb5_ccache *id, const char *res)
241178825Sdfr{
242178825Sdfr    return kcm_alloc(context, res, id);
243178825Sdfr}
244178825Sdfr
245178825Sdfr/*
246178825Sdfr * Request:
247178825Sdfr *
248178825Sdfr * Response:
249178825Sdfr *      NameZ
250178825Sdfr */
251178825Sdfrstatic krb5_error_code
252178825Sdfrkcm_gen_new(krb5_context context, krb5_ccache *id)
253178825Sdfr{
254178825Sdfr    krb5_kcmcache *k;
255178825Sdfr    krb5_error_code ret;
256178825Sdfr    krb5_storage *request, *response;
257178825Sdfr    krb5_data response_data;
258178825Sdfr
259178825Sdfr    ret = kcm_alloc(context, NULL, id);
260178825Sdfr    if (ret)
261178825Sdfr	return ret;
262178825Sdfr
263178825Sdfr    k = KCMCACHE(*id);
264178825Sdfr
265233294Sstas    ret = krb5_kcm_storage_request(context, KCM_OP_GEN_NEW, &request);
266178825Sdfr    if (ret) {
267178825Sdfr	kcm_free(context, id);
268178825Sdfr	return ret;
269178825Sdfr    }
270178825Sdfr
271233294Sstas    ret = krb5_kcm_call(context, request, &response, &response_data);
272178825Sdfr    if (ret) {
273178825Sdfr	krb5_storage_free(request);
274178825Sdfr	kcm_free(context, id);
275178825Sdfr	return ret;
276178825Sdfr    }
277178825Sdfr
278178825Sdfr    ret = krb5_ret_stringz(response, &k->name);
279178825Sdfr    if (ret)
280178825Sdfr	ret = KRB5_CC_IO;
281178825Sdfr
282178825Sdfr    krb5_storage_free(request);
283178825Sdfr    krb5_storage_free(response);
284178825Sdfr    krb5_data_free(&response_data);
285178825Sdfr
286178825Sdfr    if (ret)
287178825Sdfr	kcm_free(context, id);
288178825Sdfr
289178825Sdfr    return ret;
290178825Sdfr}
291178825Sdfr
292178825Sdfr/*
293178825Sdfr * Request:
294178825Sdfr *      NameZ
295178825Sdfr *      Principal
296178825Sdfr *
297178825Sdfr * Response:
298178825Sdfr *
299178825Sdfr */
300178825Sdfrstatic krb5_error_code
301178825Sdfrkcm_initialize(krb5_context context,
302178825Sdfr	       krb5_ccache id,
303178825Sdfr	       krb5_principal primary_principal)
304178825Sdfr{
305178825Sdfr    krb5_error_code ret;
306178825Sdfr    krb5_kcmcache *k = KCMCACHE(id);
307178825Sdfr    krb5_storage *request;
308178825Sdfr
309233294Sstas    ret = krb5_kcm_storage_request(context, KCM_OP_INITIALIZE, &request);
310178825Sdfr    if (ret)
311178825Sdfr	return ret;
312178825Sdfr
313178825Sdfr    ret = krb5_store_stringz(request, k->name);
314178825Sdfr    if (ret) {
315178825Sdfr	krb5_storage_free(request);
316178825Sdfr	return ret;
317178825Sdfr    }
318178825Sdfr
319178825Sdfr    ret = krb5_store_principal(request, primary_principal);
320178825Sdfr    if (ret) {
321178825Sdfr	krb5_storage_free(request);
322178825Sdfr	return ret;
323178825Sdfr    }
324178825Sdfr
325233294Sstas    ret = krb5_kcm_call(context, request, NULL, NULL);
326178825Sdfr
327178825Sdfr    krb5_storage_free(request);
328233294Sstas
329233294Sstas    if (context->kdc_sec_offset)
330233294Sstas	kcm_set_kdc_offset(context, id, context->kdc_sec_offset);
331233294Sstas
332178825Sdfr    return ret;
333178825Sdfr}
334178825Sdfr
335178825Sdfrstatic krb5_error_code
336178825Sdfrkcm_close(krb5_context context,
337178825Sdfr	  krb5_ccache id)
338178825Sdfr{
339178825Sdfr    kcm_free(context, &id);
340178825Sdfr    return 0;
341178825Sdfr}
342178825Sdfr
343178825Sdfr/*
344178825Sdfr * Request:
345178825Sdfr *      NameZ
346178825Sdfr *
347178825Sdfr * Response:
348178825Sdfr *
349178825Sdfr */
350178825Sdfrstatic krb5_error_code
351178825Sdfrkcm_destroy(krb5_context context,
352178825Sdfr	    krb5_ccache id)
353178825Sdfr{
354178825Sdfr    krb5_error_code ret;
355178825Sdfr    krb5_kcmcache *k = KCMCACHE(id);
356178825Sdfr    krb5_storage *request;
357178825Sdfr
358233294Sstas    ret = krb5_kcm_storage_request(context, KCM_OP_DESTROY, &request);
359178825Sdfr    if (ret)
360178825Sdfr	return ret;
361178825Sdfr
362178825Sdfr    ret = krb5_store_stringz(request, k->name);
363178825Sdfr    if (ret) {
364178825Sdfr	krb5_storage_free(request);
365178825Sdfr	return ret;
366178825Sdfr    }
367178825Sdfr
368233294Sstas    ret = krb5_kcm_call(context, request, NULL, NULL);
369178825Sdfr
370178825Sdfr    krb5_storage_free(request);
371178825Sdfr    return ret;
372178825Sdfr}
373178825Sdfr
374178825Sdfr/*
375178825Sdfr * Request:
376178825Sdfr *      NameZ
377178825Sdfr *      Creds
378178825Sdfr *
379178825Sdfr * Response:
380178825Sdfr *
381178825Sdfr */
382178825Sdfrstatic krb5_error_code
383178825Sdfrkcm_store_cred(krb5_context context,
384178825Sdfr	       krb5_ccache id,
385178825Sdfr	       krb5_creds *creds)
386178825Sdfr{
387178825Sdfr    krb5_error_code ret;
388178825Sdfr    krb5_kcmcache *k = KCMCACHE(id);
389178825Sdfr    krb5_storage *request;
390178825Sdfr
391233294Sstas    ret = krb5_kcm_storage_request(context, KCM_OP_STORE, &request);
392178825Sdfr    if (ret)
393178825Sdfr	return ret;
394178825Sdfr
395178825Sdfr    ret = krb5_store_stringz(request, k->name);
396178825Sdfr    if (ret) {
397178825Sdfr	krb5_storage_free(request);
398178825Sdfr	return ret;
399178825Sdfr    }
400178825Sdfr
401178825Sdfr    ret = krb5_store_creds(request, creds);
402178825Sdfr    if (ret) {
403178825Sdfr	krb5_storage_free(request);
404178825Sdfr	return ret;
405178825Sdfr    }
406178825Sdfr
407233294Sstas    ret = krb5_kcm_call(context, request, NULL, NULL);
408178825Sdfr
409178825Sdfr    krb5_storage_free(request);
410178825Sdfr    return ret;
411178825Sdfr}
412178825Sdfr
413233294Sstas#if 0
414178825Sdfr/*
415178825Sdfr * Request:
416178825Sdfr *      NameZ
417178825Sdfr *      WhichFields
418178825Sdfr *      MatchCreds
419178825Sdfr *
420178825Sdfr * Response:
421178825Sdfr *      Creds
422178825Sdfr *
423178825Sdfr */
424178825Sdfrstatic krb5_error_code
425178825Sdfrkcm_retrieve(krb5_context context,
426178825Sdfr	     krb5_ccache id,
427178825Sdfr	     krb5_flags which,
428178825Sdfr	     const krb5_creds *mcred,
429178825Sdfr	     krb5_creds *creds)
430178825Sdfr{
431178825Sdfr    krb5_error_code ret;
432178825Sdfr    krb5_kcmcache *k = KCMCACHE(id);
433178825Sdfr    krb5_storage *request, *response;
434178825Sdfr    krb5_data response_data;
435178825Sdfr
436233294Sstas    ret = krb5_kcm_storage_request(context, KCM_OP_RETRIEVE, &request);
437178825Sdfr    if (ret)
438178825Sdfr	return ret;
439178825Sdfr
440178825Sdfr    ret = krb5_store_stringz(request, k->name);
441178825Sdfr    if (ret) {
442178825Sdfr	krb5_storage_free(request);
443178825Sdfr	return ret;
444178825Sdfr    }
445178825Sdfr
446178825Sdfr    ret = krb5_store_int32(request, which);
447178825Sdfr    if (ret) {
448178825Sdfr	krb5_storage_free(request);
449178825Sdfr	return ret;
450178825Sdfr    }
451178825Sdfr
452178825Sdfr    ret = krb5_store_creds_tag(request, rk_UNCONST(mcred));
453178825Sdfr    if (ret) {
454178825Sdfr	krb5_storage_free(request);
455178825Sdfr	return ret;
456178825Sdfr    }
457178825Sdfr
458233294Sstas    ret = krb5_kcm_call(context, request, &response, &response_data);
459178825Sdfr    if (ret) {
460178825Sdfr	krb5_storage_free(request);
461178825Sdfr	return ret;
462178825Sdfr    }
463178825Sdfr
464178825Sdfr    ret = krb5_ret_creds(response, creds);
465178825Sdfr    if (ret)
466178825Sdfr	ret = KRB5_CC_IO;
467178825Sdfr
468178825Sdfr    krb5_storage_free(request);
469178825Sdfr    krb5_storage_free(response);
470178825Sdfr    krb5_data_free(&response_data);
471178825Sdfr
472178825Sdfr    return ret;
473178825Sdfr}
474233294Sstas#endif
475178825Sdfr
476178825Sdfr/*
477178825Sdfr * Request:
478178825Sdfr *      NameZ
479178825Sdfr *
480178825Sdfr * Response:
481178825Sdfr *      Principal
482178825Sdfr */
483178825Sdfrstatic krb5_error_code
484178825Sdfrkcm_get_principal(krb5_context context,
485178825Sdfr		  krb5_ccache id,
486178825Sdfr		  krb5_principal *principal)
487178825Sdfr{
488178825Sdfr    krb5_error_code ret;
489178825Sdfr    krb5_kcmcache *k = KCMCACHE(id);
490178825Sdfr    krb5_storage *request, *response;
491178825Sdfr    krb5_data response_data;
492178825Sdfr
493233294Sstas    ret = krb5_kcm_storage_request(context, KCM_OP_GET_PRINCIPAL, &request);
494178825Sdfr    if (ret)
495178825Sdfr	return ret;
496178825Sdfr
497178825Sdfr    ret = krb5_store_stringz(request, k->name);
498178825Sdfr    if (ret) {
499178825Sdfr	krb5_storage_free(request);
500178825Sdfr	return ret;
501178825Sdfr    }
502178825Sdfr
503233294Sstas    ret = krb5_kcm_call(context, request, &response, &response_data);
504178825Sdfr    if (ret) {
505178825Sdfr	krb5_storage_free(request);
506178825Sdfr	return ret;
507178825Sdfr    }
508178825Sdfr
509178825Sdfr    ret = krb5_ret_principal(response, principal);
510178825Sdfr    if (ret)
511178825Sdfr	ret = KRB5_CC_IO;
512178825Sdfr
513178825Sdfr    krb5_storage_free(request);
514178825Sdfr    krb5_storage_free(response);
515178825Sdfr    krb5_data_free(&response_data);
516178825Sdfr
517178825Sdfr    return ret;
518178825Sdfr}
519178825Sdfr
520178825Sdfr/*
521178825Sdfr * Request:
522178825Sdfr *      NameZ
523178825Sdfr *
524178825Sdfr * Response:
525178825Sdfr *      Cursor
526178825Sdfr *
527178825Sdfr */
528178825Sdfrstatic krb5_error_code
529178825Sdfrkcm_get_first (krb5_context context,
530178825Sdfr	       krb5_ccache id,
531178825Sdfr	       krb5_cc_cursor *cursor)
532178825Sdfr{
533178825Sdfr    krb5_error_code ret;
534233294Sstas    krb5_kcm_cursor c;
535178825Sdfr    krb5_kcmcache *k = KCMCACHE(id);
536178825Sdfr    krb5_storage *request, *response;
537178825Sdfr    krb5_data response_data;
538178825Sdfr
539233294Sstas    ret = krb5_kcm_storage_request(context, KCM_OP_GET_CRED_UUID_LIST, &request);
540178825Sdfr    if (ret)
541178825Sdfr	return ret;
542178825Sdfr
543178825Sdfr    ret = krb5_store_stringz(request, k->name);
544178825Sdfr    if (ret) {
545178825Sdfr	krb5_storage_free(request);
546178825Sdfr	return ret;
547178825Sdfr    }
548178825Sdfr
549233294Sstas    ret = krb5_kcm_call(context, request, &response, &response_data);
550233294Sstas    krb5_storage_free(request);
551233294Sstas    if (ret)
552178825Sdfr	return ret;
553233294Sstas
554233294Sstas    c = calloc(1, sizeof(*c));
555233294Sstas    if (c == NULL) {
556233294Sstas	ret = ENOMEM;
557233294Sstas	krb5_set_error_message(context, ret,
558233294Sstas			       N_("malloc: out of memory", ""));
559233294Sstas	return ret;
560178825Sdfr    }
561178825Sdfr
562233294Sstas    while (1) {
563233294Sstas	ssize_t sret;
564233294Sstas	kcmuuid_t uuid;
565233294Sstas	void *ptr;
566178825Sdfr
567233294Sstas	sret = krb5_storage_read(response, &uuid, sizeof(uuid));
568233294Sstas	if (sret == 0) {
569233294Sstas	    ret = 0;
570233294Sstas	    break;
571233294Sstas	} else if (sret != sizeof(uuid)) {
572233294Sstas	    ret = EINVAL;
573233294Sstas	    break;
574233294Sstas	}
575233294Sstas
576233294Sstas	ptr = realloc(c->uuids, sizeof(c->uuids[0]) * (c->length + 1));
577233294Sstas	if (ptr == NULL) {
578233294Sstas	    free(c->uuids);
579233294Sstas	    free(c);
580233294Sstas	    krb5_set_error_message(context, ENOMEM,
581233294Sstas				   N_("malloc: out of memory", ""));
582233294Sstas	    return ENOMEM;
583233294Sstas	}
584233294Sstas	c->uuids = ptr;
585233294Sstas
586233294Sstas	memcpy(&c->uuids[c->length], &uuid, sizeof(uuid));
587233294Sstas	c->length += 1;
588233294Sstas    }
589233294Sstas
590178825Sdfr    krb5_storage_free(response);
591178825Sdfr    krb5_data_free(&response_data);
592178825Sdfr
593233294Sstas    if (ret) {
594233294Sstas        free(c->uuids);
595233294Sstas        free(c);
596178825Sdfr	return ret;
597233294Sstas    }
598178825Sdfr
599233294Sstas    *cursor = c;
600178825Sdfr
601178825Sdfr    return 0;
602178825Sdfr}
603178825Sdfr
604178825Sdfr/*
605178825Sdfr * Request:
606178825Sdfr *      NameZ
607178825Sdfr *      Cursor
608178825Sdfr *
609178825Sdfr * Response:
610178825Sdfr *      Creds
611178825Sdfr */
612178825Sdfrstatic krb5_error_code
613178825Sdfrkcm_get_next (krb5_context context,
614178825Sdfr		krb5_ccache id,
615178825Sdfr		krb5_cc_cursor *cursor,
616178825Sdfr		krb5_creds *creds)
617178825Sdfr{
618178825Sdfr    krb5_error_code ret;
619178825Sdfr    krb5_kcmcache *k = KCMCACHE(id);
620233294Sstas    krb5_kcm_cursor c = KCMCURSOR(*cursor);
621178825Sdfr    krb5_storage *request, *response;
622178825Sdfr    krb5_data response_data;
623233294Sstas    ssize_t sret;
624178825Sdfr
625233294Sstas again:
626233294Sstas
627233294Sstas    if (c->offset >= c->length)
628233294Sstas	return KRB5_CC_END;
629233294Sstas
630233294Sstas    ret = krb5_kcm_storage_request(context, KCM_OP_GET_CRED_BY_UUID, &request);
631178825Sdfr    if (ret)
632178825Sdfr	return ret;
633178825Sdfr
634178825Sdfr    ret = krb5_store_stringz(request, k->name);
635178825Sdfr    if (ret) {
636178825Sdfr	krb5_storage_free(request);
637178825Sdfr	return ret;
638178825Sdfr    }
639178825Sdfr
640233294Sstas    sret = krb5_storage_write(request,
641233294Sstas			      &c->uuids[c->offset],
642233294Sstas			      sizeof(c->uuids[c->offset]));
643233294Sstas    c->offset++;
644233294Sstas    if (sret != sizeof(c->uuids[c->offset])) {
645178825Sdfr	krb5_storage_free(request);
646233294Sstas	krb5_clear_error_message(context);
647233294Sstas	return ENOMEM;
648178825Sdfr    }
649178825Sdfr
650233294Sstas    ret = krb5_kcm_call(context, request, &response, &response_data);
651233294Sstas    krb5_storage_free(request);
652233294Sstas    if (ret == KRB5_CC_END) {
653233294Sstas	goto again;
654178825Sdfr    }
655178825Sdfr
656178825Sdfr    ret = krb5_ret_creds(response, creds);
657178825Sdfr    if (ret)
658178825Sdfr	ret = KRB5_CC_IO;
659178825Sdfr
660178825Sdfr    krb5_storage_free(response);
661178825Sdfr    krb5_data_free(&response_data);
662178825Sdfr
663178825Sdfr    return ret;
664178825Sdfr}
665178825Sdfr
666178825Sdfr/*
667178825Sdfr * Request:
668178825Sdfr *      NameZ
669178825Sdfr *      Cursor
670178825Sdfr *
671178825Sdfr * Response:
672178825Sdfr *
673178825Sdfr */
674178825Sdfrstatic krb5_error_code
675178825Sdfrkcm_end_get (krb5_context context,
676178825Sdfr	     krb5_ccache id,
677178825Sdfr	     krb5_cc_cursor *cursor)
678178825Sdfr{
679233294Sstas    krb5_kcm_cursor c = KCMCURSOR(*cursor);
680178825Sdfr
681233294Sstas    free(c->uuids);
682233294Sstas    free(c);
683178825Sdfr
684178825Sdfr    *cursor = NULL;
685178825Sdfr
686233294Sstas    return 0;
687178825Sdfr}
688178825Sdfr
689178825Sdfr/*
690178825Sdfr * Request:
691178825Sdfr *      NameZ
692178825Sdfr *      WhichFields
693178825Sdfr *      MatchCreds
694178825Sdfr *
695178825Sdfr * Response:
696178825Sdfr *
697178825Sdfr */
698178825Sdfrstatic krb5_error_code
699178825Sdfrkcm_remove_cred(krb5_context context,
700178825Sdfr		krb5_ccache id,
701178825Sdfr		krb5_flags which,
702178825Sdfr		krb5_creds *cred)
703178825Sdfr{
704178825Sdfr    krb5_error_code ret;
705178825Sdfr    krb5_kcmcache *k = KCMCACHE(id);
706178825Sdfr    krb5_storage *request;
707178825Sdfr
708233294Sstas    ret = krb5_kcm_storage_request(context, KCM_OP_REMOVE_CRED, &request);
709178825Sdfr    if (ret)
710178825Sdfr	return ret;
711178825Sdfr
712178825Sdfr    ret = krb5_store_stringz(request, k->name);
713178825Sdfr    if (ret) {
714178825Sdfr	krb5_storage_free(request);
715178825Sdfr	return ret;
716178825Sdfr    }
717178825Sdfr
718178825Sdfr    ret = krb5_store_int32(request, which);
719178825Sdfr    if (ret) {
720178825Sdfr	krb5_storage_free(request);
721178825Sdfr	return ret;
722178825Sdfr    }
723178825Sdfr
724178825Sdfr    ret = krb5_store_creds_tag(request, cred);
725178825Sdfr    if (ret) {
726178825Sdfr	krb5_storage_free(request);
727178825Sdfr	return ret;
728178825Sdfr    }
729178825Sdfr
730233294Sstas    ret = krb5_kcm_call(context, request, NULL, NULL);
731178825Sdfr
732178825Sdfr    krb5_storage_free(request);
733178825Sdfr    return ret;
734178825Sdfr}
735178825Sdfr
736178825Sdfrstatic krb5_error_code
737178825Sdfrkcm_set_flags(krb5_context context,
738178825Sdfr	      krb5_ccache id,
739178825Sdfr	      krb5_flags flags)
740178825Sdfr{
741178825Sdfr    krb5_error_code ret;
742178825Sdfr    krb5_kcmcache *k = KCMCACHE(id);
743178825Sdfr    krb5_storage *request;
744178825Sdfr
745233294Sstas    ret = krb5_kcm_storage_request(context, KCM_OP_SET_FLAGS, &request);
746178825Sdfr    if (ret)
747178825Sdfr	return ret;
748178825Sdfr
749178825Sdfr    ret = krb5_store_stringz(request, k->name);
750178825Sdfr    if (ret) {
751178825Sdfr	krb5_storage_free(request);
752178825Sdfr	return ret;
753178825Sdfr    }
754178825Sdfr
755178825Sdfr    ret = krb5_store_int32(request, flags);
756178825Sdfr    if (ret) {
757178825Sdfr	krb5_storage_free(request);
758178825Sdfr	return ret;
759178825Sdfr    }
760178825Sdfr
761233294Sstas    ret = krb5_kcm_call(context, request, NULL, NULL);
762178825Sdfr
763178825Sdfr    krb5_storage_free(request);
764178825Sdfr    return ret;
765178825Sdfr}
766178825Sdfr
767233294Sstasstatic int
768178825Sdfrkcm_get_version(krb5_context context,
769178825Sdfr		krb5_ccache id)
770178825Sdfr{
771178825Sdfr    return 0;
772178825Sdfr}
773178825Sdfr
774233294Sstas/*
775233294Sstas * Send nothing
776233294Sstas * get back list of uuids
777233294Sstas */
778178825Sdfr
779178825Sdfrstatic krb5_error_code
780233294Sstaskcm_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
781178825Sdfr{
782233294Sstas    krb5_error_code ret;
783233294Sstas    krb5_kcm_cursor c;
784233294Sstas    krb5_storage *request, *response;
785233294Sstas    krb5_data response_data;
786178825Sdfr
787233294Sstas    *cursor = NULL;
788178825Sdfr
789233294Sstas    c = calloc(1, sizeof(*c));
790233294Sstas    if (c == NULL) {
791233294Sstas	ret = ENOMEM;
792233294Sstas	krb5_set_error_message(context, ret,
793233294Sstas			       N_("malloc: out of memory", ""));
794233294Sstas	goto out;
795233294Sstas    }
796178825Sdfr
797233294Sstas    ret = krb5_kcm_storage_request(context, KCM_OP_GET_CACHE_UUID_LIST, &request);
798233294Sstas    if (ret)
799233294Sstas	goto out;
800178825Sdfr
801233294Sstas    ret = krb5_kcm_call(context, request, &response, &response_data);
802233294Sstas    krb5_storage_free(request);
803178825Sdfr    if (ret)
804233294Sstas	goto out;
805178825Sdfr
806233294Sstas    while (1) {
807233294Sstas	ssize_t sret;
808233294Sstas	kcmuuid_t uuid;
809233294Sstas	void *ptr;
810178825Sdfr
811233294Sstas	sret = krb5_storage_read(response, &uuid, sizeof(uuid));
812233294Sstas	if (sret == 0) {
813233294Sstas	    ret = 0;
814233294Sstas	    break;
815233294Sstas	} else if (sret != sizeof(uuid)) {
816233294Sstas	    ret = EINVAL;
817233294Sstas	    goto out;
818233294Sstas	}
819178825Sdfr
820233294Sstas	ptr = realloc(c->uuids, sizeof(c->uuids[0]) * (c->length + 1));
821233294Sstas	if (ptr == NULL) {
822233294Sstas	    ret = ENOMEM;
823233294Sstas	    krb5_set_error_message(context, ret,
824233294Sstas				   N_("malloc: out of memory", ""));
825233294Sstas	    goto out;
826233294Sstas	}
827233294Sstas	c->uuids = ptr;
828233294Sstas
829233294Sstas	memcpy(&c->uuids[c->length], &uuid, sizeof(uuid));
830233294Sstas	c->length += 1;
831233294Sstas    }
832233294Sstas
833233294Sstas    krb5_storage_free(response);
834233294Sstas    krb5_data_free(&response_data);
835233294Sstas
836233294Sstas out:
837233294Sstas    if (ret && c) {
838233294Sstas        free(c->uuids);
839233294Sstas        free(c);
840233294Sstas    } else
841233294Sstas	*cursor = c;
842233294Sstas
843233294Sstas    return ret;
844178825Sdfr}
845178825Sdfr
846178825Sdfr/*
847233294Sstas * Send uuid
848233294Sstas * Recv cache name
849178825Sdfr */
850233294Sstas
851233294Sstasstatic krb5_error_code
852233294Sstaskcm_get_cache_next(krb5_context context, krb5_cc_cursor cursor, const krb5_cc_ops *ops, krb5_ccache *id)
853178825Sdfr{
854178825Sdfr    krb5_error_code ret;
855233294Sstas    krb5_kcm_cursor c = KCMCURSOR(cursor);
856233294Sstas    krb5_storage *request, *response;
857233294Sstas    krb5_data response_data;
858233294Sstas    ssize_t sret;
859233294Sstas    char *name;
860178825Sdfr
861233294Sstas    *id = NULL;
862233294Sstas
863233294Sstas again:
864233294Sstas
865233294Sstas    if (c->offset >= c->length)
866233294Sstas	return KRB5_CC_END;
867233294Sstas
868233294Sstas    ret = krb5_kcm_storage_request(context, KCM_OP_GET_CACHE_BY_UUID, &request);
869178825Sdfr    if (ret)
870178825Sdfr	return ret;
871178825Sdfr
872233294Sstas    sret = krb5_storage_write(request,
873233294Sstas			      &c->uuids[c->offset],
874233294Sstas			      sizeof(c->uuids[c->offset]));
875233294Sstas    c->offset++;
876233294Sstas    if (sret != sizeof(c->uuids[c->offset])) {
877233294Sstas	krb5_storage_free(request);
878233294Sstas	krb5_clear_error_message(context);
879233294Sstas	return ENOMEM;
880233294Sstas    }
881178825Sdfr
882233294Sstas    ret = krb5_kcm_call(context, request, &response, &response_data);
883178825Sdfr    krb5_storage_free(request);
884233294Sstas    if (ret == KRB5_CC_END)
885233294Sstas	goto again;
886233294Sstas
887233294Sstas    ret = krb5_ret_stringz(response, &name);
888233294Sstas    krb5_storage_free(response);
889233294Sstas    krb5_data_free(&response_data);
890233294Sstas
891233294Sstas    if (ret == 0) {
892233294Sstas	ret = _krb5_cc_allocate(context, ops, id);
893233294Sstas	if (ret == 0)
894233294Sstas	    ret = kcm_alloc(context, name, id);
895233294Sstas	krb5_xfree(name);
896233294Sstas    }
897233294Sstas
898178825Sdfr    return ret;
899178825Sdfr}
900178825Sdfr
901233294Sstasstatic krb5_error_code
902233294Sstaskcm_get_cache_next_kcm(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
903233294Sstas{
904233294Sstas#ifndef KCM_IS_API_CACHE
905233294Sstas    return kcm_get_cache_next(context, cursor, &krb5_kcm_ops, id);
906233294Sstas#else
907233294Sstas    return KRB5_CC_END;
908233294Sstas#endif
909233294Sstas}
910178825Sdfr
911233294Sstasstatic krb5_error_code
912233294Sstaskcm_get_cache_next_api(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
913178825Sdfr{
914233294Sstas    return kcm_get_cache_next(context, cursor, &krb5_akcm_ops, id);
915233294Sstas}
916233294Sstas
917233294Sstas
918233294Sstasstatic krb5_error_code
919233294Sstaskcm_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
920233294Sstas{
921233294Sstas    krb5_kcm_cursor c = KCMCURSOR(cursor);
922233294Sstas
923233294Sstas    free(c->uuids);
924233294Sstas    free(c);
925233294Sstas    return 0;
926233294Sstas}
927233294Sstas
928233294Sstas
929233294Sstasstatic krb5_error_code
930233294Sstaskcm_move(krb5_context context, krb5_ccache from, krb5_ccache to)
931233294Sstas{
932178825Sdfr    krb5_error_code ret;
933233294Sstas    krb5_kcmcache *oldk = KCMCACHE(from);
934233294Sstas    krb5_kcmcache *newk = KCMCACHE(to);
935178825Sdfr    krb5_storage *request;
936178825Sdfr
937233294Sstas    ret = krb5_kcm_storage_request(context, KCM_OP_MOVE_CACHE, &request);
938178825Sdfr    if (ret)
939178825Sdfr	return ret;
940178825Sdfr
941233294Sstas    ret = krb5_store_stringz(request, oldk->name);
942178825Sdfr    if (ret) {
943178825Sdfr	krb5_storage_free(request);
944178825Sdfr	return ret;
945178825Sdfr    }
946178825Sdfr
947233294Sstas    ret = krb5_store_stringz(request, newk->name);
948178825Sdfr    if (ret) {
949178825Sdfr	krb5_storage_free(request);
950178825Sdfr	return ret;
951178825Sdfr    }
952233294Sstas    ret = krb5_kcm_call(context, request, NULL, NULL);
953178825Sdfr
954178825Sdfr    krb5_storage_free(request);
955178825Sdfr    return ret;
956178825Sdfr}
957178825Sdfr
958233294Sstasstatic krb5_error_code
959233294Sstaskcm_get_default_name(krb5_context context, const krb5_cc_ops *ops,
960233294Sstas		     const char *defstr, char **str)
961233294Sstas{
962233294Sstas    krb5_error_code ret;
963233294Sstas    krb5_storage *request, *response;
964233294Sstas    krb5_data response_data;
965233294Sstas    char *name;
966178825Sdfr
967233294Sstas    *str = NULL;
968233294Sstas
969233294Sstas    ret = krb5_kcm_storage_request(context, KCM_OP_GET_DEFAULT_CACHE, &request);
970233294Sstas    if (ret)
971233294Sstas	return ret;
972233294Sstas
973233294Sstas    ret = krb5_kcm_call(context, request, &response, &response_data);
974233294Sstas    krb5_storage_free(request);
975233294Sstas    if (ret)
976233294Sstas	return _krb5_expand_default_cc_name(context, defstr, str);
977233294Sstas
978233294Sstas    ret = krb5_ret_stringz(response, &name);
979233294Sstas    krb5_storage_free(response);
980233294Sstas    krb5_data_free(&response_data);
981233294Sstas    if (ret)
982233294Sstas	return ret;
983233294Sstas
984233294Sstas    asprintf(str, "%s:%s", ops->prefix, name);
985233294Sstas    free(name);
986233294Sstas    if (str == NULL)
987233294Sstas	return ENOMEM;
988233294Sstas
989233294Sstas    return 0;
990233294Sstas}
991233294Sstas
992233294Sstasstatic krb5_error_code
993233294Sstaskcm_get_default_name_api(krb5_context context, char **str)
994178825Sdfr{
995233294Sstas    return kcm_get_default_name(context, &krb5_akcm_ops,
996233294Sstas				KRB5_DEFAULT_CCNAME_KCM_API, str);
997233294Sstas}
998233294Sstas
999233294Sstasstatic krb5_error_code
1000233294Sstaskcm_get_default_name_kcm(krb5_context context, char **str)
1001233294Sstas{
1002233294Sstas    return kcm_get_default_name(context, &krb5_kcm_ops,
1003233294Sstas				KRB5_DEFAULT_CCNAME_KCM_KCM, str);
1004233294Sstas}
1005233294Sstas
1006233294Sstasstatic krb5_error_code
1007233294Sstaskcm_set_default(krb5_context context, krb5_ccache id)
1008233294Sstas{
1009178825Sdfr    krb5_error_code ret;
1010233294Sstas    krb5_storage *request;
1011178825Sdfr    krb5_kcmcache *k = KCMCACHE(id);
1012178825Sdfr
1013233294Sstas    ret = krb5_kcm_storage_request(context, KCM_OP_SET_DEFAULT_CACHE, &request);
1014178825Sdfr    if (ret)
1015178825Sdfr	return ret;
1016178825Sdfr
1017178825Sdfr    ret = krb5_store_stringz(request, k->name);
1018178825Sdfr    if (ret) {
1019178825Sdfr	krb5_storage_free(request);
1020178825Sdfr	return ret;
1021178825Sdfr    }
1022178825Sdfr
1023233294Sstas    ret = krb5_kcm_call(context, request, NULL, NULL);
1024233294Sstas    krb5_storage_free(request);
1025233294Sstas
1026233294Sstas    return ret;
1027233294Sstas}
1028233294Sstas
1029233294Sstasstatic krb5_error_code
1030233294Sstaskcm_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime)
1031233294Sstas{
1032233294Sstas    *mtime = time(NULL);
1033233294Sstas    return 0;
1034233294Sstas}
1035233294Sstas
1036233294Sstasstatic krb5_error_code
1037233294Sstaskcm_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat kdc_offset)
1038233294Sstas{
1039233294Sstas    krb5_kcmcache *k = KCMCACHE(id);
1040233294Sstas    krb5_error_code ret;
1041233294Sstas    krb5_storage *request;
1042233294Sstas
1043233294Sstas    ret = krb5_kcm_storage_request(context, KCM_OP_SET_KDC_OFFSET, &request);
1044233294Sstas    if (ret)
1045233294Sstas	return ret;
1046233294Sstas
1047233294Sstas    ret = krb5_store_stringz(request, k->name);
1048178825Sdfr    if (ret) {
1049178825Sdfr	krb5_storage_free(request);
1050178825Sdfr	return ret;
1051178825Sdfr    }
1052233294Sstas    ret = krb5_store_int32(request, kdc_offset);
1053233294Sstas    if (ret) {
1054233294Sstas	krb5_storage_free(request);
1055233294Sstas	return ret;
1056233294Sstas    }
1057178825Sdfr
1058233294Sstas    ret = krb5_kcm_call(context, request, NULL, NULL);
1059233294Sstas    krb5_storage_free(request);
1060233294Sstas
1061233294Sstas    return ret;
1062233294Sstas}
1063233294Sstas
1064233294Sstasstatic krb5_error_code
1065233294Sstaskcm_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *kdc_offset)
1066233294Sstas{
1067233294Sstas    krb5_kcmcache *k = KCMCACHE(id);
1068233294Sstas    krb5_error_code ret;
1069233294Sstas    krb5_storage *request, *response;
1070233294Sstas    krb5_data response_data;
1071233294Sstas    int32_t offset;
1072233294Sstas
1073233294Sstas    ret = krb5_kcm_storage_request(context, KCM_OP_GET_KDC_OFFSET, &request);
1074233294Sstas    if (ret)
1075233294Sstas	return ret;
1076233294Sstas
1077233294Sstas    ret = krb5_store_stringz(request, k->name);
1078178825Sdfr    if (ret) {
1079178825Sdfr	krb5_storage_free(request);
1080178825Sdfr	return ret;
1081178825Sdfr    }
1082178825Sdfr
1083233294Sstas    ret = krb5_kcm_call(context, request, &response, &response_data);
1084233294Sstas    krb5_storage_free(request);
1085233294Sstas    if (ret)
1086233294Sstas	return ret;
1087178825Sdfr
1088233294Sstas    ret = krb5_ret_int32(response, &offset);
1089233294Sstas    krb5_storage_free(response);
1090233294Sstas    krb5_data_free(&response_data);
1091233294Sstas    if (ret)
1092233294Sstas	return ret;
1093233294Sstas
1094233294Sstas    *kdc_offset = offset;
1095233294Sstas
1096233294Sstas    return 0;
1097233294Sstas}
1098233294Sstas
1099233294Sstas/**
1100233294Sstas * Variable containing the KCM based credential cache implemention.
1101233294Sstas *
1102233294Sstas * @ingroup krb5_ccache
1103233294Sstas */
1104233294Sstas
1105233294SstasKRB5_LIB_VARIABLE const krb5_cc_ops krb5_kcm_ops = {
1106233294Sstas    KRB5_CC_OPS_VERSION,
1107233294Sstas    "KCM",
1108233294Sstas    kcm_get_name,
1109233294Sstas    kcm_resolve,
1110233294Sstas    kcm_gen_new,
1111233294Sstas    kcm_initialize,
1112233294Sstas    kcm_destroy,
1113233294Sstas    kcm_close,
1114233294Sstas    kcm_store_cred,
1115233294Sstas    NULL /* kcm_retrieve */,
1116233294Sstas    kcm_get_principal,
1117233294Sstas    kcm_get_first,
1118233294Sstas    kcm_get_next,
1119233294Sstas    kcm_end_get,
1120233294Sstas    kcm_remove_cred,
1121233294Sstas    kcm_set_flags,
1122233294Sstas    kcm_get_version,
1123233294Sstas    kcm_get_cache_first,
1124233294Sstas    kcm_get_cache_next_kcm,
1125233294Sstas    kcm_end_cache_get,
1126233294Sstas    kcm_move,
1127233294Sstas    kcm_get_default_name_kcm,
1128233294Sstas    kcm_set_default,
1129233294Sstas    kcm_lastchange,
1130233294Sstas    kcm_set_kdc_offset,
1131233294Sstas    kcm_get_kdc_offset
1132233294Sstas};
1133233294Sstas
1134233294SstasKRB5_LIB_VARIABLE const krb5_cc_ops krb5_akcm_ops = {
1135233294Sstas    KRB5_CC_OPS_VERSION,
1136233294Sstas    "API",
1137233294Sstas    kcm_get_name,
1138233294Sstas    kcm_resolve,
1139233294Sstas    kcm_gen_new,
1140233294Sstas    kcm_initialize,
1141233294Sstas    kcm_destroy,
1142233294Sstas    kcm_close,
1143233294Sstas    kcm_store_cred,
1144233294Sstas    NULL /* kcm_retrieve */,
1145233294Sstas    kcm_get_principal,
1146233294Sstas    kcm_get_first,
1147233294Sstas    kcm_get_next,
1148233294Sstas    kcm_end_get,
1149233294Sstas    kcm_remove_cred,
1150233294Sstas    kcm_set_flags,
1151233294Sstas    kcm_get_version,
1152233294Sstas    kcm_get_cache_first,
1153233294Sstas    kcm_get_cache_next_api,
1154233294Sstas    kcm_end_cache_get,
1155233294Sstas    kcm_move,
1156233294Sstas    kcm_get_default_name_api,
1157233294Sstas    kcm_set_default,
1158233294Sstas    kcm_lastchange,
1159233294Sstas    NULL,
1160233294Sstas    NULL
1161233294Sstas};
1162233294Sstas
1163233294Sstas
1164233294SstasKRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1165233294Sstas_krb5_kcm_is_running(krb5_context context)
1166233294Sstas{
1167233294Sstas    krb5_error_code ret;
1168233294Sstas    krb5_ccache_data ccdata;
1169233294Sstas    krb5_ccache id = &ccdata;
1170233294Sstas    krb5_boolean running;
1171233294Sstas
1172233294Sstas    ret = kcm_alloc(context, NULL, &id);
1173233294Sstas    if (ret)
1174233294Sstas	return 0;
1175233294Sstas
1176233294Sstas    running = (_krb5_kcm_noop(context, id) == 0);
1177233294Sstas
1178233294Sstas    kcm_free(context, &id);
1179233294Sstas
1180233294Sstas    return running;
1181233294Sstas}
1182233294Sstas
1183233294Sstas/*
1184233294Sstas * Request:
1185233294Sstas *
1186233294Sstas * Response:
1187233294Sstas *
1188233294Sstas */
1189233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1190233294Sstas_krb5_kcm_noop(krb5_context context,
1191233294Sstas	       krb5_ccache id)
1192233294Sstas{
1193233294Sstas    krb5_error_code ret;
1194233294Sstas    krb5_storage *request;
1195233294Sstas
1196233294Sstas    ret = krb5_kcm_storage_request(context, KCM_OP_NOOP, &request);
1197233294Sstas    if (ret)
1198233294Sstas	return ret;
1199233294Sstas
1200233294Sstas    ret = krb5_kcm_call(context, request, NULL, NULL);
1201233294Sstas
1202178825Sdfr    krb5_storage_free(request);
1203178825Sdfr    return ret;
1204178825Sdfr}
1205178825Sdfr
1206178825Sdfr
1207178825Sdfr/*
1208178825Sdfr * Request:
1209178825Sdfr *      NameZ
1210178825Sdfr *      ServerPrincipalPresent
1211178825Sdfr *      ServerPrincipal OPTIONAL
1212178825Sdfr *      Key
1213178825Sdfr *
1214178825Sdfr * Repsonse:
1215178825Sdfr *
1216178825Sdfr */
1217233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1218178825Sdfr_krb5_kcm_get_initial_ticket(krb5_context context,
1219178825Sdfr			     krb5_ccache id,
1220178825Sdfr			     krb5_principal server,
1221178825Sdfr			     krb5_keyblock *key)
1222178825Sdfr{
1223233294Sstas    krb5_kcmcache *k = KCMCACHE(id);
1224178825Sdfr    krb5_error_code ret;
1225178825Sdfr    krb5_storage *request;
1226178825Sdfr
1227233294Sstas    ret = krb5_kcm_storage_request(context, KCM_OP_GET_INITIAL_TICKET, &request);
1228178825Sdfr    if (ret)
1229178825Sdfr	return ret;
1230178825Sdfr
1231178825Sdfr    ret = krb5_store_stringz(request, k->name);
1232178825Sdfr    if (ret) {
1233178825Sdfr	krb5_storage_free(request);
1234178825Sdfr	return ret;
1235178825Sdfr    }
1236178825Sdfr
1237178825Sdfr    ret = krb5_store_int8(request, (server == NULL) ? 0 : 1);
1238178825Sdfr    if (ret) {
1239178825Sdfr	krb5_storage_free(request);
1240178825Sdfr	return ret;
1241178825Sdfr    }
1242178825Sdfr
1243178825Sdfr    if (server != NULL) {
1244178825Sdfr	ret = krb5_store_principal(request, server);
1245178825Sdfr	if (ret) {
1246178825Sdfr	    krb5_storage_free(request);
1247178825Sdfr	    return ret;
1248178825Sdfr	}
1249178825Sdfr    }
1250178825Sdfr
1251178825Sdfr    ret = krb5_store_keyblock(request, *key);
1252178825Sdfr    if (ret) {
1253178825Sdfr	krb5_storage_free(request);
1254178825Sdfr	return ret;
1255178825Sdfr    }
1256178825Sdfr
1257233294Sstas    ret = krb5_kcm_call(context, request, NULL, NULL);
1258178825Sdfr
1259178825Sdfr    krb5_storage_free(request);
1260178825Sdfr    return ret;
1261178825Sdfr}
1262178825Sdfr
1263178825Sdfr
1264178825Sdfr/*
1265178825Sdfr * Request:
1266178825Sdfr *      NameZ
1267178825Sdfr *      KDCFlags
1268178825Sdfr *      EncryptionType
1269178825Sdfr *      ServerPrincipal
1270178825Sdfr *
1271178825Sdfr * Repsonse:
1272178825Sdfr *
1273178825Sdfr */
1274233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1275178825Sdfr_krb5_kcm_get_ticket(krb5_context context,
1276178825Sdfr		     krb5_ccache id,
1277178825Sdfr		     krb5_kdc_flags flags,
1278178825Sdfr		     krb5_enctype enctype,
1279178825Sdfr		     krb5_principal server)
1280178825Sdfr{
1281178825Sdfr    krb5_error_code ret;
1282178825Sdfr    krb5_kcmcache *k = KCMCACHE(id);
1283178825Sdfr    krb5_storage *request;
1284178825Sdfr
1285233294Sstas    ret = krb5_kcm_storage_request(context, KCM_OP_GET_TICKET, &request);
1286178825Sdfr    if (ret)
1287178825Sdfr	return ret;
1288178825Sdfr
1289178825Sdfr    ret = krb5_store_stringz(request, k->name);
1290178825Sdfr    if (ret) {
1291178825Sdfr	krb5_storage_free(request);
1292178825Sdfr	return ret;
1293178825Sdfr    }
1294178825Sdfr
1295178825Sdfr    ret = krb5_store_int32(request, flags.i);
1296178825Sdfr    if (ret) {
1297178825Sdfr	krb5_storage_free(request);
1298178825Sdfr	return ret;
1299178825Sdfr    }
1300178825Sdfr
1301178825Sdfr    ret = krb5_store_int32(request, enctype);
1302178825Sdfr    if (ret) {
1303178825Sdfr	krb5_storage_free(request);
1304178825Sdfr	return ret;
1305178825Sdfr    }
1306178825Sdfr
1307178825Sdfr    ret = krb5_store_principal(request, server);
1308178825Sdfr    if (ret) {
1309178825Sdfr	krb5_storage_free(request);
1310178825Sdfr	return ret;
1311178825Sdfr    }
1312178825Sdfr
1313233294Sstas    ret = krb5_kcm_call(context, request, NULL, NULL);
1314178825Sdfr
1315178825Sdfr    krb5_storage_free(request);
1316178825Sdfr    return ret;
1317178825Sdfr}
1318178825Sdfr
1319178825Sdfr#endif /* HAVE_KCM */
1320