kcm.c revision 178825
1178825Sdfr/*
2178825Sdfr * Copyright (c) 2005, PADL Software Pty Ltd.
3178825Sdfr * All rights reserved.
4178825Sdfr *
5178825Sdfr * Redistribution and use in source and binary forms, with or without
6178825Sdfr * modification, are permitted provided that the following conditions
7178825Sdfr * are met:
8178825Sdfr *
9178825Sdfr * 1. Redistributions of source code must retain the above copyright
10178825Sdfr *    notice, this list of conditions and the following disclaimer.
11178825Sdfr *
12178825Sdfr * 2. Redistributions in binary form must reproduce the above copyright
13178825Sdfr *    notice, this list of conditions and the following disclaimer in the
14178825Sdfr *    documentation and/or other materials provided with the distribution.
15178825Sdfr *
16178825Sdfr * 3. Neither the name of PADL Software nor the names of its contributors
17178825Sdfr *    may be used to endorse or promote products derived from this software
18178825Sdfr *    without specific prior written permission.
19178825Sdfr *
20178825Sdfr * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
21178825Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22178825Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23178825Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
24178825Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25178825Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26178825Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27178825Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28178825Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29178825Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30178825Sdfr * SUCH DAMAGE.
31178825Sdfr */
32178825Sdfr
33178825Sdfr#include "krb5_locl.h"
34178825Sdfr
35178825Sdfr#ifdef HAVE_KCM
36178825Sdfr/*
37178825Sdfr * Client library for Kerberos Credentials Manager (KCM) daemon
38178825Sdfr */
39178825Sdfr
40178825Sdfr#ifdef HAVE_SYS_UN_H
41178825Sdfr#include <sys/un.h>
42178825Sdfr#endif
43178825Sdfr
44178825Sdfr#include "kcm.h"
45178825Sdfr
46178825SdfrRCSID("$Id: kcm.c 22108 2007-12-03 17:23:53Z lha $");
47178825Sdfr
48178825Sdfrtypedef struct krb5_kcmcache {
49178825Sdfr    char *name;
50178825Sdfr    struct sockaddr_un path;
51178825Sdfr    char *door_path;
52178825Sdfr} krb5_kcmcache;
53178825Sdfr
54178825Sdfr#define KCMCACHE(X)	((krb5_kcmcache *)(X)->data.data)
55178825Sdfr#define CACHENAME(X)	(KCMCACHE(X)->name)
56178825Sdfr#define KCMCURSOR(C)	(*(uint32_t *)(C))
57178825Sdfr
58178825Sdfrstatic krb5_error_code
59178825Sdfrtry_door(krb5_context context, const krb5_kcmcache *k,
60178825Sdfr	 krb5_data *request_data,
61178825Sdfr	 krb5_data *response_data)
62178825Sdfr{
63178825Sdfr#ifdef HAVE_DOOR_CREATE
64178825Sdfr    door_arg_t arg;
65178825Sdfr    int fd;
66178825Sdfr    int ret;
67178825Sdfr
68178825Sdfr    memset(&arg, 0, sizeof(arg));
69178825Sdfr
70178825Sdfr    fd = open(k->door_path, O_RDWR);
71178825Sdfr    if (fd < 0)
72178825Sdfr	return KRB5_CC_IO;
73178825Sdfr
74178825Sdfr    arg.data_ptr = request_data->data;
75178825Sdfr    arg.data_size = request_data->length;
76178825Sdfr    arg.desc_ptr = NULL;
77178825Sdfr    arg.desc_num = 0;
78178825Sdfr    arg.rbuf = NULL;
79178825Sdfr    arg.rsize = 0;
80178825Sdfr
81178825Sdfr    ret = door_call(fd, &arg);
82178825Sdfr    close(fd);
83178825Sdfr    if (ret != 0)
84178825Sdfr	return KRB5_CC_IO;
85178825Sdfr
86178825Sdfr    ret = krb5_data_copy(response_data, arg.rbuf, arg.rsize);
87178825Sdfr    munmap(arg.rbuf, arg.rsize);
88178825Sdfr    if (ret)
89178825Sdfr	return ret;
90178825Sdfr
91178825Sdfr    return 0;
92178825Sdfr#else
93178825Sdfr    return KRB5_CC_IO;
94178825Sdfr#endif
95178825Sdfr}
96178825Sdfr
97178825Sdfrstatic krb5_error_code
98178825Sdfrtry_unix_socket(krb5_context context, const krb5_kcmcache *k,
99178825Sdfr		krb5_data *request_data,
100178825Sdfr		krb5_data *response_data)
101178825Sdfr{
102178825Sdfr    krb5_error_code ret;
103178825Sdfr    int fd;
104178825Sdfr
105178825Sdfr    fd = socket(AF_UNIX, SOCK_STREAM, 0);
106178825Sdfr    if (fd < 0)
107178825Sdfr	return KRB5_CC_IO;
108178825Sdfr
109178825Sdfr    if (connect(fd, rk_UNCONST(&k->path), sizeof(k->path)) != 0) {
110178825Sdfr	close(fd);
111178825Sdfr	return KRB5_CC_IO;
112178825Sdfr    }
113178825Sdfr
114178825Sdfr    ret = _krb5_send_and_recv_tcp(fd, context->kdc_timeout,
115178825Sdfr				  request_data, response_data);
116178825Sdfr    close(fd);
117178825Sdfr    return ret;
118178825Sdfr}
119178825Sdfr
120178825Sdfrstatic krb5_error_code
121178825Sdfrkcm_send_request(krb5_context context,
122178825Sdfr		 krb5_kcmcache *k,
123178825Sdfr		 krb5_storage *request,
124178825Sdfr		 krb5_data *response_data)
125178825Sdfr{
126178825Sdfr    krb5_error_code ret;
127178825Sdfr    krb5_data request_data;
128178825Sdfr    int i;
129178825Sdfr
130178825Sdfr    response_data->data = NULL;
131178825Sdfr    response_data->length = 0;
132178825Sdfr
133178825Sdfr    ret = krb5_storage_to_data(request, &request_data);
134178825Sdfr    if (ret) {
135178825Sdfr	krb5_clear_error_string(context);
136178825Sdfr	return KRB5_CC_NOMEM;
137178825Sdfr    }
138178825Sdfr
139178825Sdfr    ret = KRB5_CC_IO;
140178825Sdfr
141178825Sdfr    for (i = 0; i < context->max_retries; i++) {
142178825Sdfr	ret = try_door(context, k, &request_data, response_data);
143178825Sdfr	if (ret == 0 && response_data->length != 0)
144178825Sdfr	    break;
145178825Sdfr	ret = try_unix_socket(context, k, &request_data, response_data);
146178825Sdfr	if (ret == 0 && response_data->length != 0)
147178825Sdfr	    break;
148178825Sdfr    }
149178825Sdfr
150178825Sdfr    krb5_data_free(&request_data);
151178825Sdfr
152178825Sdfr    if (ret) {
153178825Sdfr	krb5_clear_error_string(context);
154178825Sdfr	ret = KRB5_CC_IO;
155178825Sdfr    }
156178825Sdfr
157178825Sdfr    return ret;
158178825Sdfr}
159178825Sdfr
160178825Sdfrstatic krb5_error_code
161178825Sdfrkcm_storage_request(krb5_context context,
162178825Sdfr		    kcm_operation opcode,
163178825Sdfr		    krb5_storage **storage_p)
164178825Sdfr{
165178825Sdfr    krb5_storage *sp;
166178825Sdfr    krb5_error_code ret;
167178825Sdfr
168178825Sdfr    *storage_p = NULL;
169178825Sdfr
170178825Sdfr    sp = krb5_storage_emem();
171178825Sdfr    if (sp == NULL) {
172178825Sdfr	krb5_set_error_string(context, "malloc: out of memory");
173178825Sdfr	return KRB5_CC_NOMEM;
174178825Sdfr    }
175178825Sdfr
176178825Sdfr    /* Send MAJOR | VERSION | OPCODE */
177178825Sdfr    ret  = krb5_store_int8(sp, KCM_PROTOCOL_VERSION_MAJOR);
178178825Sdfr    if (ret)
179178825Sdfr	goto fail;
180178825Sdfr    ret = krb5_store_int8(sp, KCM_PROTOCOL_VERSION_MINOR);
181178825Sdfr    if (ret)
182178825Sdfr	goto fail;
183178825Sdfr    ret = krb5_store_int16(sp, opcode);
184178825Sdfr    if (ret)
185178825Sdfr	goto fail;
186178825Sdfr
187178825Sdfr    *storage_p = sp;
188178825Sdfr fail:
189178825Sdfr    if (ret) {
190178825Sdfr	krb5_set_error_string(context, "Failed to encode request");
191178825Sdfr	krb5_storage_free(sp);
192178825Sdfr    }
193178825Sdfr
194178825Sdfr    return ret;
195178825Sdfr}
196178825Sdfr
197178825Sdfrstatic krb5_error_code
198178825Sdfrkcm_alloc(krb5_context context, const char *name, krb5_ccache *id)
199178825Sdfr{
200178825Sdfr    krb5_kcmcache *k;
201178825Sdfr    const char *path;
202178825Sdfr
203178825Sdfr    k = malloc(sizeof(*k));
204178825Sdfr    if (k == NULL) {
205178825Sdfr	krb5_set_error_string(context, "malloc: out of memory");
206178825Sdfr	return KRB5_CC_NOMEM;
207178825Sdfr    }
208178825Sdfr
209178825Sdfr    if (name != NULL) {
210178825Sdfr	k->name = strdup(name);
211178825Sdfr	if (k->name == NULL) {
212178825Sdfr	    free(k);
213178825Sdfr	    krb5_set_error_string(context, "malloc: out of memory");
214178825Sdfr	    return KRB5_CC_NOMEM;
215178825Sdfr	}
216178825Sdfr    } else
217178825Sdfr	k->name = NULL;
218178825Sdfr
219178825Sdfr    path = krb5_config_get_string_default(context, NULL,
220178825Sdfr					  _PATH_KCM_SOCKET,
221178825Sdfr					  "libdefaults",
222178825Sdfr					  "kcm_socket",
223178825Sdfr					  NULL);
224178825Sdfr
225178825Sdfr    k->path.sun_family = AF_UNIX;
226178825Sdfr    strlcpy(k->path.sun_path, path, sizeof(k->path.sun_path));
227178825Sdfr
228178825Sdfr    path = krb5_config_get_string_default(context, NULL,
229178825Sdfr					  _PATH_KCM_DOOR,
230178825Sdfr					  "libdefaults",
231178825Sdfr					  "kcm_door",
232178825Sdfr					  NULL);
233178825Sdfr    k->door_path = strdup(path);
234178825Sdfr
235178825Sdfr    (*id)->data.data = k;
236178825Sdfr    (*id)->data.length = sizeof(*k);
237178825Sdfr
238178825Sdfr    return 0;
239178825Sdfr}
240178825Sdfr
241178825Sdfrstatic krb5_error_code
242178825Sdfrkcm_call(krb5_context context,
243178825Sdfr	 krb5_kcmcache *k,
244178825Sdfr	 krb5_storage *request,
245178825Sdfr	 krb5_storage **response_p,
246178825Sdfr	 krb5_data *response_data_p)
247178825Sdfr{
248178825Sdfr    krb5_data response_data;
249178825Sdfr    krb5_error_code ret;
250178825Sdfr    int32_t status;
251178825Sdfr    krb5_storage *response;
252178825Sdfr
253178825Sdfr    if (response_p != NULL)
254178825Sdfr	*response_p = NULL;
255178825Sdfr
256178825Sdfr    ret = kcm_send_request(context, k, request, &response_data);
257178825Sdfr    if (ret) {
258178825Sdfr	return ret;
259178825Sdfr    }
260178825Sdfr
261178825Sdfr    response = krb5_storage_from_data(&response_data);
262178825Sdfr    if (response == NULL) {
263178825Sdfr	krb5_data_free(&response_data);
264178825Sdfr	return KRB5_CC_IO;
265178825Sdfr    }
266178825Sdfr
267178825Sdfr    ret = krb5_ret_int32(response, &status);
268178825Sdfr    if (ret) {
269178825Sdfr	krb5_storage_free(response);
270178825Sdfr	krb5_data_free(&response_data);
271178825Sdfr	return KRB5_CC_FORMAT;
272178825Sdfr    }
273178825Sdfr
274178825Sdfr    if (status) {
275178825Sdfr	krb5_storage_free(response);
276178825Sdfr	krb5_data_free(&response_data);
277178825Sdfr	return status;
278178825Sdfr    }
279178825Sdfr
280178825Sdfr    if (response_p != NULL) {
281178825Sdfr	*response_data_p = response_data;
282178825Sdfr	*response_p = response;
283178825Sdfr
284178825Sdfr	return 0;
285178825Sdfr    }
286178825Sdfr
287178825Sdfr    krb5_storage_free(response);
288178825Sdfr    krb5_data_free(&response_data);
289178825Sdfr
290178825Sdfr    return 0;
291178825Sdfr}
292178825Sdfr
293178825Sdfrstatic void
294178825Sdfrkcm_free(krb5_context context, krb5_ccache *id)
295178825Sdfr{
296178825Sdfr    krb5_kcmcache *k = KCMCACHE(*id);
297178825Sdfr
298178825Sdfr    if (k != NULL) {
299178825Sdfr	if (k->name != NULL)
300178825Sdfr	    free(k->name);
301178825Sdfr	if (k->door_path)
302178825Sdfr	    free(k->door_path);
303178825Sdfr	memset(k, 0, sizeof(*k));
304178825Sdfr	krb5_data_free(&(*id)->data);
305178825Sdfr    }
306178825Sdfr
307178825Sdfr    *id = NULL;
308178825Sdfr}
309178825Sdfr
310178825Sdfrstatic const char *
311178825Sdfrkcm_get_name(krb5_context context,
312178825Sdfr	     krb5_ccache id)
313178825Sdfr{
314178825Sdfr    return CACHENAME(id);
315178825Sdfr}
316178825Sdfr
317178825Sdfrstatic krb5_error_code
318178825Sdfrkcm_resolve(krb5_context context, krb5_ccache *id, const char *res)
319178825Sdfr{
320178825Sdfr    return kcm_alloc(context, res, id);
321178825Sdfr}
322178825Sdfr
323178825Sdfr/*
324178825Sdfr * Request:
325178825Sdfr *
326178825Sdfr * Response:
327178825Sdfr *      NameZ
328178825Sdfr */
329178825Sdfrstatic krb5_error_code
330178825Sdfrkcm_gen_new(krb5_context context, krb5_ccache *id)
331178825Sdfr{
332178825Sdfr    krb5_kcmcache *k;
333178825Sdfr    krb5_error_code ret;
334178825Sdfr    krb5_storage *request, *response;
335178825Sdfr    krb5_data response_data;
336178825Sdfr
337178825Sdfr    ret = kcm_alloc(context, NULL, id);
338178825Sdfr    if (ret)
339178825Sdfr	return ret;
340178825Sdfr
341178825Sdfr    k = KCMCACHE(*id);
342178825Sdfr
343178825Sdfr    ret = kcm_storage_request(context, KCM_OP_GEN_NEW, &request);
344178825Sdfr    if (ret) {
345178825Sdfr	kcm_free(context, id);
346178825Sdfr	return ret;
347178825Sdfr    }
348178825Sdfr
349178825Sdfr    ret = kcm_call(context, k, request, &response, &response_data);
350178825Sdfr    if (ret) {
351178825Sdfr	krb5_storage_free(request);
352178825Sdfr	kcm_free(context, id);
353178825Sdfr	return ret;
354178825Sdfr    }
355178825Sdfr
356178825Sdfr    ret = krb5_ret_stringz(response, &k->name);
357178825Sdfr    if (ret)
358178825Sdfr	ret = KRB5_CC_IO;
359178825Sdfr
360178825Sdfr    krb5_storage_free(request);
361178825Sdfr    krb5_storage_free(response);
362178825Sdfr    krb5_data_free(&response_data);
363178825Sdfr
364178825Sdfr    if (ret)
365178825Sdfr	kcm_free(context, id);
366178825Sdfr
367178825Sdfr    return ret;
368178825Sdfr}
369178825Sdfr
370178825Sdfr/*
371178825Sdfr * Request:
372178825Sdfr *      NameZ
373178825Sdfr *      Principal
374178825Sdfr *
375178825Sdfr * Response:
376178825Sdfr *
377178825Sdfr */
378178825Sdfrstatic krb5_error_code
379178825Sdfrkcm_initialize(krb5_context context,
380178825Sdfr	       krb5_ccache id,
381178825Sdfr	       krb5_principal primary_principal)
382178825Sdfr{
383178825Sdfr    krb5_error_code ret;
384178825Sdfr    krb5_kcmcache *k = KCMCACHE(id);
385178825Sdfr    krb5_storage *request;
386178825Sdfr
387178825Sdfr    ret = kcm_storage_request(context, KCM_OP_INITIALIZE, &request);
388178825Sdfr    if (ret)
389178825Sdfr	return ret;
390178825Sdfr
391178825Sdfr    ret = krb5_store_stringz(request, k->name);
392178825Sdfr    if (ret) {
393178825Sdfr	krb5_storage_free(request);
394178825Sdfr	return ret;
395178825Sdfr    }
396178825Sdfr
397178825Sdfr    ret = krb5_store_principal(request, primary_principal);
398178825Sdfr    if (ret) {
399178825Sdfr	krb5_storage_free(request);
400178825Sdfr	return ret;
401178825Sdfr    }
402178825Sdfr
403178825Sdfr    ret = kcm_call(context, k, request, NULL, NULL);
404178825Sdfr
405178825Sdfr    krb5_storage_free(request);
406178825Sdfr    return ret;
407178825Sdfr}
408178825Sdfr
409178825Sdfrstatic krb5_error_code
410178825Sdfrkcm_close(krb5_context context,
411178825Sdfr	  krb5_ccache id)
412178825Sdfr{
413178825Sdfr    kcm_free(context, &id);
414178825Sdfr    return 0;
415178825Sdfr}
416178825Sdfr
417178825Sdfr/*
418178825Sdfr * Request:
419178825Sdfr *      NameZ
420178825Sdfr *
421178825Sdfr * Response:
422178825Sdfr *
423178825Sdfr */
424178825Sdfrstatic krb5_error_code
425178825Sdfrkcm_destroy(krb5_context context,
426178825Sdfr	    krb5_ccache id)
427178825Sdfr{
428178825Sdfr    krb5_error_code ret;
429178825Sdfr    krb5_kcmcache *k = KCMCACHE(id);
430178825Sdfr    krb5_storage *request;
431178825Sdfr
432178825Sdfr    ret = kcm_storage_request(context, KCM_OP_DESTROY, &request);
433178825Sdfr    if (ret)
434178825Sdfr	return ret;
435178825Sdfr
436178825Sdfr    ret = krb5_store_stringz(request, k->name);
437178825Sdfr    if (ret) {
438178825Sdfr	krb5_storage_free(request);
439178825Sdfr	return ret;
440178825Sdfr    }
441178825Sdfr
442178825Sdfr    ret = kcm_call(context, k, request, NULL, NULL);
443178825Sdfr
444178825Sdfr    krb5_storage_free(request);
445178825Sdfr    return ret;
446178825Sdfr}
447178825Sdfr
448178825Sdfr/*
449178825Sdfr * Request:
450178825Sdfr *      NameZ
451178825Sdfr *      Creds
452178825Sdfr *
453178825Sdfr * Response:
454178825Sdfr *
455178825Sdfr */
456178825Sdfrstatic krb5_error_code
457178825Sdfrkcm_store_cred(krb5_context context,
458178825Sdfr	       krb5_ccache id,
459178825Sdfr	       krb5_creds *creds)
460178825Sdfr{
461178825Sdfr    krb5_error_code ret;
462178825Sdfr    krb5_kcmcache *k = KCMCACHE(id);
463178825Sdfr    krb5_storage *request;
464178825Sdfr
465178825Sdfr    ret = kcm_storage_request(context, KCM_OP_STORE, &request);
466178825Sdfr    if (ret)
467178825Sdfr	return ret;
468178825Sdfr
469178825Sdfr    ret = krb5_store_stringz(request, k->name);
470178825Sdfr    if (ret) {
471178825Sdfr	krb5_storage_free(request);
472178825Sdfr	return ret;
473178825Sdfr    }
474178825Sdfr
475178825Sdfr    ret = krb5_store_creds(request, creds);
476178825Sdfr    if (ret) {
477178825Sdfr	krb5_storage_free(request);
478178825Sdfr	return ret;
479178825Sdfr    }
480178825Sdfr
481178825Sdfr    ret = kcm_call(context, k, request, NULL, NULL);
482178825Sdfr
483178825Sdfr    krb5_storage_free(request);
484178825Sdfr    return ret;
485178825Sdfr}
486178825Sdfr
487178825Sdfr/*
488178825Sdfr * Request:
489178825Sdfr *      NameZ
490178825Sdfr *      WhichFields
491178825Sdfr *      MatchCreds
492178825Sdfr *
493178825Sdfr * Response:
494178825Sdfr *      Creds
495178825Sdfr *
496178825Sdfr */
497178825Sdfrstatic krb5_error_code
498178825Sdfrkcm_retrieve(krb5_context context,
499178825Sdfr	     krb5_ccache id,
500178825Sdfr	     krb5_flags which,
501178825Sdfr	     const krb5_creds *mcred,
502178825Sdfr	     krb5_creds *creds)
503178825Sdfr{
504178825Sdfr    krb5_error_code ret;
505178825Sdfr    krb5_kcmcache *k = KCMCACHE(id);
506178825Sdfr    krb5_storage *request, *response;
507178825Sdfr    krb5_data response_data;
508178825Sdfr
509178825Sdfr    ret = kcm_storage_request(context, KCM_OP_RETRIEVE, &request);
510178825Sdfr    if (ret)
511178825Sdfr	return ret;
512178825Sdfr
513178825Sdfr    ret = krb5_store_stringz(request, k->name);
514178825Sdfr    if (ret) {
515178825Sdfr	krb5_storage_free(request);
516178825Sdfr	return ret;
517178825Sdfr    }
518178825Sdfr
519178825Sdfr    ret = krb5_store_int32(request, which);
520178825Sdfr    if (ret) {
521178825Sdfr	krb5_storage_free(request);
522178825Sdfr	return ret;
523178825Sdfr    }
524178825Sdfr
525178825Sdfr    ret = krb5_store_creds_tag(request, rk_UNCONST(mcred));
526178825Sdfr    if (ret) {
527178825Sdfr	krb5_storage_free(request);
528178825Sdfr	return ret;
529178825Sdfr    }
530178825Sdfr
531178825Sdfr    ret = kcm_call(context, k, request, &response, &response_data);
532178825Sdfr    if (ret) {
533178825Sdfr	krb5_storage_free(request);
534178825Sdfr	return ret;
535178825Sdfr    }
536178825Sdfr
537178825Sdfr    ret = krb5_ret_creds(response, creds);
538178825Sdfr    if (ret)
539178825Sdfr	ret = KRB5_CC_IO;
540178825Sdfr
541178825Sdfr    krb5_storage_free(request);
542178825Sdfr    krb5_storage_free(response);
543178825Sdfr    krb5_data_free(&response_data);
544178825Sdfr
545178825Sdfr    return ret;
546178825Sdfr}
547178825Sdfr
548178825Sdfr/*
549178825Sdfr * Request:
550178825Sdfr *      NameZ
551178825Sdfr *
552178825Sdfr * Response:
553178825Sdfr *      Principal
554178825Sdfr */
555178825Sdfrstatic krb5_error_code
556178825Sdfrkcm_get_principal(krb5_context context,
557178825Sdfr		  krb5_ccache id,
558178825Sdfr		  krb5_principal *principal)
559178825Sdfr{
560178825Sdfr    krb5_error_code ret;
561178825Sdfr    krb5_kcmcache *k = KCMCACHE(id);
562178825Sdfr    krb5_storage *request, *response;
563178825Sdfr    krb5_data response_data;
564178825Sdfr
565178825Sdfr    ret = kcm_storage_request(context, KCM_OP_GET_PRINCIPAL, &request);
566178825Sdfr    if (ret)
567178825Sdfr	return ret;
568178825Sdfr
569178825Sdfr    ret = krb5_store_stringz(request, k->name);
570178825Sdfr    if (ret) {
571178825Sdfr	krb5_storage_free(request);
572178825Sdfr	return ret;
573178825Sdfr    }
574178825Sdfr
575178825Sdfr    ret = kcm_call(context, k, request, &response, &response_data);
576178825Sdfr    if (ret) {
577178825Sdfr	krb5_storage_free(request);
578178825Sdfr	return ret;
579178825Sdfr    }
580178825Sdfr
581178825Sdfr    ret = krb5_ret_principal(response, principal);
582178825Sdfr    if (ret)
583178825Sdfr	ret = KRB5_CC_IO;
584178825Sdfr
585178825Sdfr    krb5_storage_free(request);
586178825Sdfr    krb5_storage_free(response);
587178825Sdfr    krb5_data_free(&response_data);
588178825Sdfr
589178825Sdfr    return ret;
590178825Sdfr}
591178825Sdfr
592178825Sdfr/*
593178825Sdfr * Request:
594178825Sdfr *      NameZ
595178825Sdfr *
596178825Sdfr * Response:
597178825Sdfr *      Cursor
598178825Sdfr *
599178825Sdfr */
600178825Sdfrstatic krb5_error_code
601178825Sdfrkcm_get_first (krb5_context context,
602178825Sdfr	       krb5_ccache id,
603178825Sdfr	       krb5_cc_cursor *cursor)
604178825Sdfr{
605178825Sdfr    krb5_error_code ret;
606178825Sdfr    krb5_kcmcache *k = KCMCACHE(id);
607178825Sdfr    krb5_storage *request, *response;
608178825Sdfr    krb5_data response_data;
609178825Sdfr    int32_t tmp;
610178825Sdfr
611178825Sdfr    ret = kcm_storage_request(context, KCM_OP_GET_FIRST, &request);
612178825Sdfr    if (ret)
613178825Sdfr	return ret;
614178825Sdfr
615178825Sdfr    ret = krb5_store_stringz(request, k->name);
616178825Sdfr    if (ret) {
617178825Sdfr	krb5_storage_free(request);
618178825Sdfr	return ret;
619178825Sdfr    }
620178825Sdfr
621178825Sdfr    ret = kcm_call(context, k, request, &response, &response_data);
622178825Sdfr    if (ret) {
623178825Sdfr	krb5_storage_free(request);
624178825Sdfr	return ret;
625178825Sdfr    }
626178825Sdfr
627178825Sdfr    ret = krb5_ret_int32(response, &tmp);
628178825Sdfr    if (ret || tmp < 0)
629178825Sdfr	ret = KRB5_CC_IO;
630178825Sdfr
631178825Sdfr    krb5_storage_free(request);
632178825Sdfr    krb5_storage_free(response);
633178825Sdfr    krb5_data_free(&response_data);
634178825Sdfr
635178825Sdfr    if (ret)
636178825Sdfr	return ret;
637178825Sdfr
638178825Sdfr    *cursor = malloc(sizeof(tmp));
639178825Sdfr    if (*cursor == NULL)
640178825Sdfr	return KRB5_CC_NOMEM;
641178825Sdfr
642178825Sdfr    KCMCURSOR(*cursor) = tmp;
643178825Sdfr
644178825Sdfr    return 0;
645178825Sdfr}
646178825Sdfr
647178825Sdfr/*
648178825Sdfr * Request:
649178825Sdfr *      NameZ
650178825Sdfr *      Cursor
651178825Sdfr *
652178825Sdfr * Response:
653178825Sdfr *      Creds
654178825Sdfr */
655178825Sdfrstatic krb5_error_code
656178825Sdfrkcm_get_next (krb5_context context,
657178825Sdfr		krb5_ccache id,
658178825Sdfr		krb5_cc_cursor *cursor,
659178825Sdfr		krb5_creds *creds)
660178825Sdfr{
661178825Sdfr    krb5_error_code ret;
662178825Sdfr    krb5_kcmcache *k = KCMCACHE(id);
663178825Sdfr    krb5_storage *request, *response;
664178825Sdfr    krb5_data response_data;
665178825Sdfr
666178825Sdfr    ret = kcm_storage_request(context, KCM_OP_GET_NEXT, &request);
667178825Sdfr    if (ret)
668178825Sdfr	return ret;
669178825Sdfr
670178825Sdfr    ret = krb5_store_stringz(request, k->name);
671178825Sdfr    if (ret) {
672178825Sdfr	krb5_storage_free(request);
673178825Sdfr	return ret;
674178825Sdfr    }
675178825Sdfr
676178825Sdfr    ret = krb5_store_int32(request, KCMCURSOR(*cursor));
677178825Sdfr    if (ret) {
678178825Sdfr	krb5_storage_free(request);
679178825Sdfr	return ret;
680178825Sdfr    }
681178825Sdfr
682178825Sdfr    ret = kcm_call(context, k, request, &response, &response_data);
683178825Sdfr    if (ret) {
684178825Sdfr	krb5_storage_free(request);
685178825Sdfr	return ret;
686178825Sdfr    }
687178825Sdfr
688178825Sdfr    ret = krb5_ret_creds(response, creds);
689178825Sdfr    if (ret)
690178825Sdfr	ret = KRB5_CC_IO;
691178825Sdfr
692178825Sdfr    krb5_storage_free(request);
693178825Sdfr    krb5_storage_free(response);
694178825Sdfr    krb5_data_free(&response_data);
695178825Sdfr
696178825Sdfr    return ret;
697178825Sdfr}
698178825Sdfr
699178825Sdfr/*
700178825Sdfr * Request:
701178825Sdfr *      NameZ
702178825Sdfr *      Cursor
703178825Sdfr *
704178825Sdfr * Response:
705178825Sdfr *
706178825Sdfr */
707178825Sdfrstatic krb5_error_code
708178825Sdfrkcm_end_get (krb5_context context,
709178825Sdfr	     krb5_ccache id,
710178825Sdfr	     krb5_cc_cursor *cursor)
711178825Sdfr{
712178825Sdfr    krb5_error_code ret;
713178825Sdfr    krb5_kcmcache *k = KCMCACHE(id);
714178825Sdfr    krb5_storage *request;
715178825Sdfr
716178825Sdfr    ret = kcm_storage_request(context, KCM_OP_END_GET, &request);
717178825Sdfr    if (ret)
718178825Sdfr	return ret;
719178825Sdfr
720178825Sdfr    ret = krb5_store_stringz(request, k->name);
721178825Sdfr    if (ret) {
722178825Sdfr	krb5_storage_free(request);
723178825Sdfr	return ret;
724178825Sdfr    }
725178825Sdfr
726178825Sdfr    ret = krb5_store_int32(request, KCMCURSOR(*cursor));
727178825Sdfr    if (ret) {
728178825Sdfr	krb5_storage_free(request);
729178825Sdfr	return ret;
730178825Sdfr    }
731178825Sdfr
732178825Sdfr    ret = kcm_call(context, k, request, NULL, NULL);
733178825Sdfr    if (ret) {
734178825Sdfr	krb5_storage_free(request);
735178825Sdfr	return ret;
736178825Sdfr    }
737178825Sdfr
738178825Sdfr    krb5_storage_free(request);
739178825Sdfr
740178825Sdfr    KCMCURSOR(*cursor) = 0;
741178825Sdfr    free(*cursor);
742178825Sdfr    *cursor = NULL;
743178825Sdfr
744178825Sdfr    return ret;
745178825Sdfr}
746178825Sdfr
747178825Sdfr/*
748178825Sdfr * Request:
749178825Sdfr *      NameZ
750178825Sdfr *      WhichFields
751178825Sdfr *      MatchCreds
752178825Sdfr *
753178825Sdfr * Response:
754178825Sdfr *
755178825Sdfr */
756178825Sdfrstatic krb5_error_code
757178825Sdfrkcm_remove_cred(krb5_context context,
758178825Sdfr		krb5_ccache id,
759178825Sdfr		krb5_flags which,
760178825Sdfr		krb5_creds *cred)
761178825Sdfr{
762178825Sdfr    krb5_error_code ret;
763178825Sdfr    krb5_kcmcache *k = KCMCACHE(id);
764178825Sdfr    krb5_storage *request;
765178825Sdfr
766178825Sdfr    ret = kcm_storage_request(context, KCM_OP_REMOVE_CRED, &request);
767178825Sdfr    if (ret)
768178825Sdfr	return ret;
769178825Sdfr
770178825Sdfr    ret = krb5_store_stringz(request, k->name);
771178825Sdfr    if (ret) {
772178825Sdfr	krb5_storage_free(request);
773178825Sdfr	return ret;
774178825Sdfr    }
775178825Sdfr
776178825Sdfr    ret = krb5_store_int32(request, which);
777178825Sdfr    if (ret) {
778178825Sdfr	krb5_storage_free(request);
779178825Sdfr	return ret;
780178825Sdfr    }
781178825Sdfr
782178825Sdfr    ret = krb5_store_creds_tag(request, cred);
783178825Sdfr    if (ret) {
784178825Sdfr	krb5_storage_free(request);
785178825Sdfr	return ret;
786178825Sdfr    }
787178825Sdfr
788178825Sdfr    ret = kcm_call(context, k, request, NULL, NULL);
789178825Sdfr
790178825Sdfr    krb5_storage_free(request);
791178825Sdfr    return ret;
792178825Sdfr}
793178825Sdfr
794178825Sdfrstatic krb5_error_code
795178825Sdfrkcm_set_flags(krb5_context context,
796178825Sdfr	      krb5_ccache id,
797178825Sdfr	      krb5_flags flags)
798178825Sdfr{
799178825Sdfr    krb5_error_code ret;
800178825Sdfr    krb5_kcmcache *k = KCMCACHE(id);
801178825Sdfr    krb5_storage *request;
802178825Sdfr
803178825Sdfr    ret = kcm_storage_request(context, KCM_OP_SET_FLAGS, &request);
804178825Sdfr    if (ret)
805178825Sdfr	return ret;
806178825Sdfr
807178825Sdfr    ret = krb5_store_stringz(request, k->name);
808178825Sdfr    if (ret) {
809178825Sdfr	krb5_storage_free(request);
810178825Sdfr	return ret;
811178825Sdfr    }
812178825Sdfr
813178825Sdfr    ret = krb5_store_int32(request, flags);
814178825Sdfr    if (ret) {
815178825Sdfr	krb5_storage_free(request);
816178825Sdfr	return ret;
817178825Sdfr    }
818178825Sdfr
819178825Sdfr    ret = kcm_call(context, k, request, NULL, NULL);
820178825Sdfr
821178825Sdfr    krb5_storage_free(request);
822178825Sdfr    return ret;
823178825Sdfr}
824178825Sdfr
825178825Sdfrstatic krb5_error_code
826178825Sdfrkcm_get_version(krb5_context context,
827178825Sdfr		krb5_ccache id)
828178825Sdfr{
829178825Sdfr    return 0;
830178825Sdfr}
831178825Sdfr
832178825Sdfrstatic krb5_error_code
833178825Sdfrkcm_move(krb5_context context, krb5_ccache from, krb5_ccache to)
834178825Sdfr{
835178825Sdfr    krb5_set_error_string(context, "kcm_move not implemented");
836178825Sdfr    return EINVAL;
837178825Sdfr}
838178825Sdfr
839178825Sdfrstatic krb5_error_code
840178825Sdfrkcm_default_name(krb5_context context, char **str)
841178825Sdfr{
842178825Sdfr    return _krb5_expand_default_cc_name(context,
843178825Sdfr					KRB5_DEFAULT_CCNAME_KCM,
844178825Sdfr					str);
845178825Sdfr}
846178825Sdfr
847178825Sdfr/**
848178825Sdfr * Variable containing the KCM based credential cache implemention.
849178825Sdfr *
850178825Sdfr * @ingroup krb5_ccache
851178825Sdfr */
852178825Sdfr
853178825Sdfrconst krb5_cc_ops krb5_kcm_ops = {
854178825Sdfr    "KCM",
855178825Sdfr    kcm_get_name,
856178825Sdfr    kcm_resolve,
857178825Sdfr    kcm_gen_new,
858178825Sdfr    kcm_initialize,
859178825Sdfr    kcm_destroy,
860178825Sdfr    kcm_close,
861178825Sdfr    kcm_store_cred,
862178825Sdfr    kcm_retrieve,
863178825Sdfr    kcm_get_principal,
864178825Sdfr    kcm_get_first,
865178825Sdfr    kcm_get_next,
866178825Sdfr    kcm_end_get,
867178825Sdfr    kcm_remove_cred,
868178825Sdfr    kcm_set_flags,
869178825Sdfr    kcm_get_version,
870178825Sdfr    NULL,
871178825Sdfr    NULL,
872178825Sdfr    NULL,
873178825Sdfr    kcm_move,
874178825Sdfr    kcm_default_name
875178825Sdfr};
876178825Sdfr
877178825Sdfrkrb5_boolean
878178825Sdfr_krb5_kcm_is_running(krb5_context context)
879178825Sdfr{
880178825Sdfr    krb5_error_code ret;
881178825Sdfr    krb5_ccache_data ccdata;
882178825Sdfr    krb5_ccache id = &ccdata;
883178825Sdfr    krb5_boolean running;
884178825Sdfr
885178825Sdfr    ret = kcm_alloc(context, NULL, &id);
886178825Sdfr    if (ret)
887178825Sdfr	return 0;
888178825Sdfr
889178825Sdfr    running = (_krb5_kcm_noop(context, id) == 0);
890178825Sdfr
891178825Sdfr    kcm_free(context, &id);
892178825Sdfr
893178825Sdfr    return running;
894178825Sdfr}
895178825Sdfr
896178825Sdfr/*
897178825Sdfr * Request:
898178825Sdfr *
899178825Sdfr * Response:
900178825Sdfr *
901178825Sdfr */
902178825Sdfrkrb5_error_code
903178825Sdfr_krb5_kcm_noop(krb5_context context,
904178825Sdfr	       krb5_ccache id)
905178825Sdfr{
906178825Sdfr    krb5_error_code ret;
907178825Sdfr    krb5_kcmcache *k = KCMCACHE(id);
908178825Sdfr    krb5_storage *request;
909178825Sdfr
910178825Sdfr    ret = kcm_storage_request(context, KCM_OP_NOOP, &request);
911178825Sdfr    if (ret)
912178825Sdfr	return ret;
913178825Sdfr
914178825Sdfr    ret = kcm_call(context, k, request, NULL, NULL);
915178825Sdfr
916178825Sdfr    krb5_storage_free(request);
917178825Sdfr    return ret;
918178825Sdfr}
919178825Sdfr
920178825Sdfr
921178825Sdfr/*
922178825Sdfr * Request:
923178825Sdfr *      NameZ
924178825Sdfr *      Mode
925178825Sdfr *
926178825Sdfr * Response:
927178825Sdfr *
928178825Sdfr */
929178825Sdfrkrb5_error_code
930178825Sdfr_krb5_kcm_chmod(krb5_context context,
931178825Sdfr		krb5_ccache id,
932178825Sdfr		uint16_t mode)
933178825Sdfr{
934178825Sdfr    krb5_error_code ret;
935178825Sdfr    krb5_kcmcache *k = KCMCACHE(id);
936178825Sdfr    krb5_storage *request;
937178825Sdfr
938178825Sdfr    ret = kcm_storage_request(context, KCM_OP_CHMOD, &request);
939178825Sdfr    if (ret)
940178825Sdfr	return ret;
941178825Sdfr
942178825Sdfr    ret = krb5_store_stringz(request, k->name);
943178825Sdfr    if (ret) {
944178825Sdfr	krb5_storage_free(request);
945178825Sdfr	return ret;
946178825Sdfr    }
947178825Sdfr
948178825Sdfr    ret = krb5_store_int16(request, mode);
949178825Sdfr    if (ret) {
950178825Sdfr	krb5_storage_free(request);
951178825Sdfr	return ret;
952178825Sdfr    }
953178825Sdfr
954178825Sdfr    ret = kcm_call(context, k, request, NULL, NULL);
955178825Sdfr
956178825Sdfr    krb5_storage_free(request);
957178825Sdfr    return ret;
958178825Sdfr}
959178825Sdfr
960178825Sdfr
961178825Sdfr/*
962178825Sdfr * Request:
963178825Sdfr *      NameZ
964178825Sdfr *      UID
965178825Sdfr *      GID
966178825Sdfr *
967178825Sdfr * Response:
968178825Sdfr *
969178825Sdfr */
970178825Sdfrkrb5_error_code
971178825Sdfr_krb5_kcm_chown(krb5_context context,
972178825Sdfr		krb5_ccache id,
973178825Sdfr		uint32_t uid,
974178825Sdfr		uint32_t gid)
975178825Sdfr{
976178825Sdfr    krb5_error_code ret;
977178825Sdfr    krb5_kcmcache *k = KCMCACHE(id);
978178825Sdfr    krb5_storage *request;
979178825Sdfr
980178825Sdfr    ret = kcm_storage_request(context, KCM_OP_CHOWN, &request);
981178825Sdfr    if (ret)
982178825Sdfr	return ret;
983178825Sdfr
984178825Sdfr    ret = krb5_store_stringz(request, k->name);
985178825Sdfr    if (ret) {
986178825Sdfr	krb5_storage_free(request);
987178825Sdfr	return ret;
988178825Sdfr    }
989178825Sdfr
990178825Sdfr    ret = krb5_store_int32(request, uid);
991178825Sdfr    if (ret) {
992178825Sdfr	krb5_storage_free(request);
993178825Sdfr	return ret;
994178825Sdfr    }
995178825Sdfr
996178825Sdfr    ret = krb5_store_int32(request, gid);
997178825Sdfr    if (ret) {
998178825Sdfr	krb5_storage_free(request);
999178825Sdfr	return ret;
1000178825Sdfr    }
1001178825Sdfr
1002178825Sdfr    ret = kcm_call(context, k, request, NULL, NULL);
1003178825Sdfr
1004178825Sdfr    krb5_storage_free(request);
1005178825Sdfr    return ret;
1006178825Sdfr}
1007178825Sdfr
1008178825Sdfr
1009178825Sdfr/*
1010178825Sdfr * Request:
1011178825Sdfr *      NameZ
1012178825Sdfr *      ServerPrincipalPresent
1013178825Sdfr *      ServerPrincipal OPTIONAL
1014178825Sdfr *      Key
1015178825Sdfr *
1016178825Sdfr * Repsonse:
1017178825Sdfr *
1018178825Sdfr */
1019178825Sdfrkrb5_error_code
1020178825Sdfr_krb5_kcm_get_initial_ticket(krb5_context context,
1021178825Sdfr			     krb5_ccache id,
1022178825Sdfr			     krb5_principal server,
1023178825Sdfr			     krb5_keyblock *key)
1024178825Sdfr{
1025178825Sdfr    krb5_error_code ret;
1026178825Sdfr    krb5_kcmcache *k = KCMCACHE(id);
1027178825Sdfr    krb5_storage *request;
1028178825Sdfr
1029178825Sdfr    ret = kcm_storage_request(context, KCM_OP_GET_INITIAL_TICKET, &request);
1030178825Sdfr    if (ret)
1031178825Sdfr	return ret;
1032178825Sdfr
1033178825Sdfr    ret = krb5_store_stringz(request, k->name);
1034178825Sdfr    if (ret) {
1035178825Sdfr	krb5_storage_free(request);
1036178825Sdfr	return ret;
1037178825Sdfr    }
1038178825Sdfr
1039178825Sdfr    ret = krb5_store_int8(request, (server == NULL) ? 0 : 1);
1040178825Sdfr    if (ret) {
1041178825Sdfr	krb5_storage_free(request);
1042178825Sdfr	return ret;
1043178825Sdfr    }
1044178825Sdfr
1045178825Sdfr    if (server != NULL) {
1046178825Sdfr	ret = krb5_store_principal(request, server);
1047178825Sdfr	if (ret) {
1048178825Sdfr	    krb5_storage_free(request);
1049178825Sdfr	    return ret;
1050178825Sdfr	}
1051178825Sdfr    }
1052178825Sdfr
1053178825Sdfr    ret = krb5_store_keyblock(request, *key);
1054178825Sdfr    if (ret) {
1055178825Sdfr	krb5_storage_free(request);
1056178825Sdfr	return ret;
1057178825Sdfr    }
1058178825Sdfr
1059178825Sdfr    ret = kcm_call(context, k, request, NULL, NULL);
1060178825Sdfr
1061178825Sdfr    krb5_storage_free(request);
1062178825Sdfr    return ret;
1063178825Sdfr}
1064178825Sdfr
1065178825Sdfr
1066178825Sdfr/*
1067178825Sdfr * Request:
1068178825Sdfr *      NameZ
1069178825Sdfr *      KDCFlags
1070178825Sdfr *      EncryptionType
1071178825Sdfr *      ServerPrincipal
1072178825Sdfr *
1073178825Sdfr * Repsonse:
1074178825Sdfr *
1075178825Sdfr */
1076178825Sdfrkrb5_error_code
1077178825Sdfr_krb5_kcm_get_ticket(krb5_context context,
1078178825Sdfr		     krb5_ccache id,
1079178825Sdfr		     krb5_kdc_flags flags,
1080178825Sdfr		     krb5_enctype enctype,
1081178825Sdfr		     krb5_principal server)
1082178825Sdfr{
1083178825Sdfr    krb5_error_code ret;
1084178825Sdfr    krb5_kcmcache *k = KCMCACHE(id);
1085178825Sdfr    krb5_storage *request;
1086178825Sdfr
1087178825Sdfr    ret = kcm_storage_request(context, KCM_OP_GET_TICKET, &request);
1088178825Sdfr    if (ret)
1089178825Sdfr	return ret;
1090178825Sdfr
1091178825Sdfr    ret = krb5_store_stringz(request, k->name);
1092178825Sdfr    if (ret) {
1093178825Sdfr	krb5_storage_free(request);
1094178825Sdfr	return ret;
1095178825Sdfr    }
1096178825Sdfr
1097178825Sdfr    ret = krb5_store_int32(request, flags.i);
1098178825Sdfr    if (ret) {
1099178825Sdfr	krb5_storage_free(request);
1100178825Sdfr	return ret;
1101178825Sdfr    }
1102178825Sdfr
1103178825Sdfr    ret = krb5_store_int32(request, enctype);
1104178825Sdfr    if (ret) {
1105178825Sdfr	krb5_storage_free(request);
1106178825Sdfr	return ret;
1107178825Sdfr    }
1108178825Sdfr
1109178825Sdfr    ret = krb5_store_principal(request, server);
1110178825Sdfr    if (ret) {
1111178825Sdfr	krb5_storage_free(request);
1112178825Sdfr	return ret;
1113178825Sdfr    }
1114178825Sdfr
1115178825Sdfr    ret = kcm_call(context, k, request, NULL, NULL);
1116178825Sdfr
1117178825Sdfr    krb5_storage_free(request);
1118178825Sdfr    return ret;
1119178825Sdfr}
1120178825Sdfr
1121178825Sdfr
1122178825Sdfr#endif /* HAVE_KCM */
1123