xref: /minix/crypto/external/bsd/heimdal/dist/lib/krb5/kcm.c (revision 0a6a1f1d)
1 /*	$NetBSD: kcm.c,v 1.1.1.2 2014/04/24 12:45:50 pettai Exp $	*/
2 
3 /*
4  * Copyright (c) 2005, PADL Software Pty Ltd.
5  * All rights reserved.
6  *
7  * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * 3. Neither the name of PADL Software nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36 
37 #include "krb5_locl.h"
38 
39 #ifdef HAVE_KCM
40 /*
41  * Client library for Kerberos Credentials Manager (KCM) daemon
42  */
43 
44 #include <krb5/kcm.h>
45 #include <heim-ipc.h>
46 
47 static krb5_error_code
48 kcm_set_kdc_offset(krb5_context, krb5_ccache, krb5_deltat);
49 
50 static const char *kcm_ipc_name = "ANY:org.h5l.kcm";
51 
52 typedef struct krb5_kcmcache {
53     char *name;
54 } krb5_kcmcache;
55 
56 typedef struct krb5_kcm_cursor {
57     unsigned long offset;
58     unsigned long length;
59     kcmuuid_t *uuids;
60 } *krb5_kcm_cursor;
61 
62 
63 #define KCMCACHE(X)	((krb5_kcmcache *)(X)->data.data)
64 #define CACHENAME(X)	(KCMCACHE(X)->name)
65 #define KCMCURSOR(C)	((krb5_kcm_cursor)(C))
66 
67 static HEIMDAL_MUTEX kcm_mutex = HEIMDAL_MUTEX_INITIALIZER;
68 static heim_ipc kcm_ipc = NULL;
69 
70 static krb5_error_code
kcm_send_request(krb5_context context,krb5_storage * request,krb5_data * response_data)71 kcm_send_request(krb5_context context,
72 		 krb5_storage *request,
73 		 krb5_data *response_data)
74 {
75     krb5_error_code ret = 0;
76     krb5_data request_data;
77 
78     HEIMDAL_MUTEX_lock(&kcm_mutex);
79     if (kcm_ipc == NULL)
80 	ret = heim_ipc_init_context(kcm_ipc_name, &kcm_ipc);
81     HEIMDAL_MUTEX_unlock(&kcm_mutex);
82     if (ret)
83 	return KRB5_CC_NOSUPP;
84 
85     ret = krb5_storage_to_data(request, &request_data);
86     if (ret) {
87 	krb5_clear_error_message(context);
88 	return KRB5_CC_NOMEM;
89     }
90 
91     ret = heim_ipc_call(kcm_ipc, &request_data, response_data, NULL);
92     krb5_data_free(&request_data);
93 
94     if (ret) {
95 	krb5_clear_error_message(context);
96 	ret = KRB5_CC_NOSUPP;
97     }
98 
99     return ret;
100 }
101 
102 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_kcm_storage_request(krb5_context context,uint16_t opcode,krb5_storage ** storage_p)103 krb5_kcm_storage_request(krb5_context context,
104 			 uint16_t opcode,
105 			 krb5_storage **storage_p)
106 {
107     krb5_storage *sp;
108     krb5_error_code ret;
109 
110     *storage_p = NULL;
111 
112     sp = krb5_storage_emem();
113     if (sp == NULL) {
114 	krb5_set_error_message(context, KRB5_CC_NOMEM, N_("malloc: out of memory", ""));
115 	return KRB5_CC_NOMEM;
116     }
117 
118     /* Send MAJOR | VERSION | OPCODE */
119     ret  = krb5_store_int8(sp, KCM_PROTOCOL_VERSION_MAJOR);
120     if (ret)
121 	goto fail;
122     ret = krb5_store_int8(sp, KCM_PROTOCOL_VERSION_MINOR);
123     if (ret)
124 	goto fail;
125     ret = krb5_store_int16(sp, opcode);
126     if (ret)
127 	goto fail;
128 
129     *storage_p = sp;
130  fail:
131     if (ret) {
132 	krb5_set_error_message(context, ret,
133 			       N_("Failed to encode KCM request", ""));
134 	krb5_storage_free(sp);
135     }
136 
137     return ret;
138 }
139 
140 static krb5_error_code
kcm_alloc(krb5_context context,const char * name,krb5_ccache * id)141 kcm_alloc(krb5_context context, const char *name, krb5_ccache *id)
142 {
143     krb5_kcmcache *k;
144 
145     k = malloc(sizeof(*k));
146     if (k == NULL) {
147 	krb5_set_error_message(context, KRB5_CC_NOMEM,
148 			       N_("malloc: out of memory", ""));
149 	return KRB5_CC_NOMEM;
150     }
151 
152     if (name != NULL) {
153 	k->name = strdup(name);
154 	if (k->name == NULL) {
155 	    free(k);
156 	    krb5_set_error_message(context, KRB5_CC_NOMEM,
157 				   N_("malloc: out of memory", ""));
158 	    return KRB5_CC_NOMEM;
159 	}
160     } else
161 	k->name = NULL;
162 
163     (*id)->data.data = k;
164     (*id)->data.length = sizeof(*k);
165 
166     return 0;
167 }
168 
169 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_kcm_call(krb5_context context,krb5_storage * request,krb5_storage ** response_p,krb5_data * response_data_p)170 krb5_kcm_call(krb5_context context,
171 	      krb5_storage *request,
172 	      krb5_storage **response_p,
173 	      krb5_data *response_data_p)
174 {
175     krb5_data response_data;
176     krb5_error_code ret;
177     int32_t status;
178     krb5_storage *response;
179 
180     if (response_p != NULL)
181 	*response_p = NULL;
182 
183     krb5_data_zero(&response_data);
184 
185     ret = kcm_send_request(context, request, &response_data);
186     if (ret)
187 	return ret;
188 
189     response = krb5_storage_from_data(&response_data);
190     if (response == NULL) {
191 	krb5_data_free(&response_data);
192 	return KRB5_CC_IO;
193     }
194 
195     ret = krb5_ret_int32(response, &status);
196     if (ret) {
197 	krb5_storage_free(response);
198 	krb5_data_free(&response_data);
199 	return KRB5_CC_FORMAT;
200     }
201 
202     if (status) {
203 	krb5_storage_free(response);
204 	krb5_data_free(&response_data);
205 	return status;
206     }
207 
208     if (response_p != NULL) {
209 	*response_data_p = response_data;
210 	*response_p = response;
211 
212 	return 0;
213     }
214 
215     krb5_storage_free(response);
216     krb5_data_free(&response_data);
217 
218     return 0;
219 }
220 
221 static void
kcm_free(krb5_context context,krb5_ccache * id)222 kcm_free(krb5_context context, krb5_ccache *id)
223 {
224     krb5_kcmcache *k = KCMCACHE(*id);
225 
226     if (k != NULL) {
227 	if (k->name != NULL)
228 	    free(k->name);
229 	memset(k, 0, sizeof(*k));
230 	krb5_data_free(&(*id)->data);
231     }
232 }
233 
234 static const char *
kcm_get_name(krb5_context context,krb5_ccache id)235 kcm_get_name(krb5_context context,
236 	     krb5_ccache id)
237 {
238     return CACHENAME(id);
239 }
240 
241 static krb5_error_code
kcm_resolve(krb5_context context,krb5_ccache * id,const char * res)242 kcm_resolve(krb5_context context, krb5_ccache *id, const char *res)
243 {
244     return kcm_alloc(context, res, id);
245 }
246 
247 /*
248  * Request:
249  *
250  * Response:
251  *      NameZ
252  */
253 static krb5_error_code
kcm_gen_new(krb5_context context,krb5_ccache * id)254 kcm_gen_new(krb5_context context, krb5_ccache *id)
255 {
256     krb5_kcmcache *k;
257     krb5_error_code ret;
258     krb5_storage *request, *response;
259     krb5_data response_data;
260 
261     ret = kcm_alloc(context, NULL, id);
262     if (ret)
263 	return ret;
264 
265     k = KCMCACHE(*id);
266 
267     ret = krb5_kcm_storage_request(context, KCM_OP_GEN_NEW, &request);
268     if (ret) {
269 	kcm_free(context, id);
270 	return ret;
271     }
272 
273     ret = krb5_kcm_call(context, request, &response, &response_data);
274     if (ret) {
275 	krb5_storage_free(request);
276 	kcm_free(context, id);
277 	return ret;
278     }
279 
280     ret = krb5_ret_stringz(response, &k->name);
281     if (ret)
282 	ret = KRB5_CC_IO;
283 
284     krb5_storage_free(request);
285     krb5_storage_free(response);
286     krb5_data_free(&response_data);
287 
288     if (ret)
289 	kcm_free(context, id);
290 
291     return ret;
292 }
293 
294 /*
295  * Request:
296  *      NameZ
297  *      Principal
298  *
299  * Response:
300  *
301  */
302 static krb5_error_code
kcm_initialize(krb5_context context,krb5_ccache id,krb5_principal primary_principal)303 kcm_initialize(krb5_context context,
304 	       krb5_ccache id,
305 	       krb5_principal primary_principal)
306 {
307     krb5_error_code ret;
308     krb5_kcmcache *k = KCMCACHE(id);
309     krb5_storage *request;
310 
311     ret = krb5_kcm_storage_request(context, KCM_OP_INITIALIZE, &request);
312     if (ret)
313 	return ret;
314 
315     ret = krb5_store_stringz(request, k->name);
316     if (ret) {
317 	krb5_storage_free(request);
318 	return ret;
319     }
320 
321     ret = krb5_store_principal(request, primary_principal);
322     if (ret) {
323 	krb5_storage_free(request);
324 	return ret;
325     }
326 
327     ret = krb5_kcm_call(context, request, NULL, NULL);
328 
329     krb5_storage_free(request);
330 
331     if (context->kdc_sec_offset)
332 	kcm_set_kdc_offset(context, id, context->kdc_sec_offset);
333 
334     return ret;
335 }
336 
337 static krb5_error_code
kcm_close(krb5_context context,krb5_ccache id)338 kcm_close(krb5_context context,
339 	  krb5_ccache id)
340 {
341     kcm_free(context, &id);
342     return 0;
343 }
344 
345 /*
346  * Request:
347  *      NameZ
348  *
349  * Response:
350  *
351  */
352 static krb5_error_code
kcm_destroy(krb5_context context,krb5_ccache id)353 kcm_destroy(krb5_context context,
354 	    krb5_ccache id)
355 {
356     krb5_error_code ret;
357     krb5_kcmcache *k = KCMCACHE(id);
358     krb5_storage *request;
359 
360     ret = krb5_kcm_storage_request(context, KCM_OP_DESTROY, &request);
361     if (ret)
362 	return ret;
363 
364     ret = krb5_store_stringz(request, k->name);
365     if (ret) {
366 	krb5_storage_free(request);
367 	return ret;
368     }
369 
370     ret = krb5_kcm_call(context, request, NULL, NULL);
371 
372     krb5_storage_free(request);
373     return ret;
374 }
375 
376 /*
377  * Request:
378  *      NameZ
379  *      Creds
380  *
381  * Response:
382  *
383  */
384 static krb5_error_code
kcm_store_cred(krb5_context context,krb5_ccache id,krb5_creds * creds)385 kcm_store_cred(krb5_context context,
386 	       krb5_ccache id,
387 	       krb5_creds *creds)
388 {
389     krb5_error_code ret;
390     krb5_kcmcache *k = KCMCACHE(id);
391     krb5_storage *request;
392 
393     ret = krb5_kcm_storage_request(context, KCM_OP_STORE, &request);
394     if (ret)
395 	return ret;
396 
397     ret = krb5_store_stringz(request, k->name);
398     if (ret) {
399 	krb5_storage_free(request);
400 	return ret;
401     }
402 
403     ret = krb5_store_creds(request, creds);
404     if (ret) {
405 	krb5_storage_free(request);
406 	return ret;
407     }
408 
409     ret = krb5_kcm_call(context, request, NULL, NULL);
410 
411     krb5_storage_free(request);
412     return ret;
413 }
414 
415 #if 0
416 /*
417  * Request:
418  *      NameZ
419  *      WhichFields
420  *      MatchCreds
421  *
422  * Response:
423  *      Creds
424  *
425  */
426 static krb5_error_code
427 kcm_retrieve(krb5_context context,
428 	     krb5_ccache id,
429 	     krb5_flags which,
430 	     const krb5_creds *mcred,
431 	     krb5_creds *creds)
432 {
433     krb5_error_code ret;
434     krb5_kcmcache *k = KCMCACHE(id);
435     krb5_storage *request, *response;
436     krb5_data response_data;
437 
438     ret = krb5_kcm_storage_request(context, KCM_OP_RETRIEVE, &request);
439     if (ret)
440 	return ret;
441 
442     ret = krb5_store_stringz(request, k->name);
443     if (ret) {
444 	krb5_storage_free(request);
445 	return ret;
446     }
447 
448     ret = krb5_store_int32(request, which);
449     if (ret) {
450 	krb5_storage_free(request);
451 	return ret;
452     }
453 
454     ret = krb5_store_creds_tag(request, rk_UNCONST(mcred));
455     if (ret) {
456 	krb5_storage_free(request);
457 	return ret;
458     }
459 
460     ret = krb5_kcm_call(context, request, &response, &response_data);
461     if (ret) {
462 	krb5_storage_free(request);
463 	return ret;
464     }
465 
466     ret = krb5_ret_creds(response, creds);
467     if (ret)
468 	ret = KRB5_CC_IO;
469 
470     krb5_storage_free(request);
471     krb5_storage_free(response);
472     krb5_data_free(&response_data);
473 
474     return ret;
475 }
476 #endif
477 
478 /*
479  * Request:
480  *      NameZ
481  *
482  * Response:
483  *      Principal
484  */
485 static krb5_error_code
kcm_get_principal(krb5_context context,krb5_ccache id,krb5_principal * principal)486 kcm_get_principal(krb5_context context,
487 		  krb5_ccache id,
488 		  krb5_principal *principal)
489 {
490     krb5_error_code ret;
491     krb5_kcmcache *k = KCMCACHE(id);
492     krb5_storage *request, *response;
493     krb5_data response_data;
494 
495     ret = krb5_kcm_storage_request(context, KCM_OP_GET_PRINCIPAL, &request);
496     if (ret)
497 	return ret;
498 
499     ret = krb5_store_stringz(request, k->name);
500     if (ret) {
501 	krb5_storage_free(request);
502 	return ret;
503     }
504 
505     ret = krb5_kcm_call(context, request, &response, &response_data);
506     if (ret) {
507 	krb5_storage_free(request);
508 	return ret;
509     }
510 
511     ret = krb5_ret_principal(response, principal);
512     if (ret)
513 	ret = KRB5_CC_IO;
514 
515     krb5_storage_free(request);
516     krb5_storage_free(response);
517     krb5_data_free(&response_data);
518 
519     return ret;
520 }
521 
522 /*
523  * Request:
524  *      NameZ
525  *
526  * Response:
527  *      Cursor
528  *
529  */
530 static krb5_error_code
kcm_get_first(krb5_context context,krb5_ccache id,krb5_cc_cursor * cursor)531 kcm_get_first (krb5_context context,
532 	       krb5_ccache id,
533 	       krb5_cc_cursor *cursor)
534 {
535     krb5_error_code ret;
536     krb5_kcm_cursor c;
537     krb5_kcmcache *k = KCMCACHE(id);
538     krb5_storage *request, *response;
539     krb5_data response_data;
540 
541     ret = krb5_kcm_storage_request(context, KCM_OP_GET_CRED_UUID_LIST, &request);
542     if (ret)
543 	return ret;
544 
545     ret = krb5_store_stringz(request, k->name);
546     if (ret) {
547 	krb5_storage_free(request);
548 	return ret;
549     }
550 
551     ret = krb5_kcm_call(context, request, &response, &response_data);
552     krb5_storage_free(request);
553     if (ret)
554 	return ret;
555 
556     c = calloc(1, sizeof(*c));
557     if (c == NULL) {
558 	ret = ENOMEM;
559 	krb5_set_error_message(context, ret,
560 			       N_("malloc: out of memory", ""));
561 	return ret;
562     }
563 
564     while (1) {
565 	ssize_t sret;
566 	kcmuuid_t uuid;
567 	void *ptr;
568 
569 	sret = krb5_storage_read(response, &uuid, sizeof(uuid));
570 	if (sret == 0) {
571 	    ret = 0;
572 	    break;
573 	} else if (sret != sizeof(uuid)) {
574 	    ret = EINVAL;
575 	    break;
576 	}
577 
578 	ptr = realloc(c->uuids, sizeof(c->uuids[0]) * (c->length + 1));
579 	if (ptr == NULL) {
580 	    free(c->uuids);
581 	    free(c);
582 	    krb5_set_error_message(context, ENOMEM,
583 				   N_("malloc: out of memory", ""));
584 	    return ENOMEM;
585 	}
586 	c->uuids = ptr;
587 
588 	memcpy(&c->uuids[c->length], &uuid, sizeof(uuid));
589 	c->length += 1;
590     }
591 
592     krb5_storage_free(response);
593     krb5_data_free(&response_data);
594 
595     if (ret) {
596         free(c->uuids);
597         free(c);
598 	return ret;
599     }
600 
601     *cursor = c;
602 
603     return 0;
604 }
605 
606 /*
607  * Request:
608  *      NameZ
609  *      Cursor
610  *
611  * Response:
612  *      Creds
613  */
614 static krb5_error_code
kcm_get_next(krb5_context context,krb5_ccache id,krb5_cc_cursor * cursor,krb5_creds * creds)615 kcm_get_next (krb5_context context,
616 		krb5_ccache id,
617 		krb5_cc_cursor *cursor,
618 		krb5_creds *creds)
619 {
620     krb5_error_code ret;
621     krb5_kcmcache *k = KCMCACHE(id);
622     krb5_kcm_cursor c = KCMCURSOR(*cursor);
623     krb5_storage *request, *response;
624     krb5_data response_data;
625     ssize_t sret;
626 
627  again:
628 
629     if (c->offset >= c->length)
630 	return KRB5_CC_END;
631 
632     ret = krb5_kcm_storage_request(context, KCM_OP_GET_CRED_BY_UUID, &request);
633     if (ret)
634 	return ret;
635 
636     ret = krb5_store_stringz(request, k->name);
637     if (ret) {
638 	krb5_storage_free(request);
639 	return ret;
640     }
641 
642     sret = krb5_storage_write(request,
643 			      &c->uuids[c->offset],
644 			      sizeof(c->uuids[c->offset]));
645     c->offset++;
646     if (sret != sizeof(c->uuids[c->offset])) {
647 	krb5_storage_free(request);
648 	krb5_clear_error_message(context);
649 	return ENOMEM;
650     }
651 
652     ret = krb5_kcm_call(context, request, &response, &response_data);
653     krb5_storage_free(request);
654     if (ret == KRB5_CC_END) {
655 	goto again;
656     }
657 
658     ret = krb5_ret_creds(response, creds);
659     if (ret)
660 	ret = KRB5_CC_IO;
661 
662     krb5_storage_free(response);
663     krb5_data_free(&response_data);
664 
665     return ret;
666 }
667 
668 /*
669  * Request:
670  *      NameZ
671  *      Cursor
672  *
673  * Response:
674  *
675  */
676 static krb5_error_code
kcm_end_get(krb5_context context,krb5_ccache id,krb5_cc_cursor * cursor)677 kcm_end_get (krb5_context context,
678 	     krb5_ccache id,
679 	     krb5_cc_cursor *cursor)
680 {
681     krb5_kcm_cursor c = KCMCURSOR(*cursor);
682 
683     free(c->uuids);
684     free(c);
685 
686     *cursor = NULL;
687 
688     return 0;
689 }
690 
691 /*
692  * Request:
693  *      NameZ
694  *      WhichFields
695  *      MatchCreds
696  *
697  * Response:
698  *
699  */
700 static krb5_error_code
kcm_remove_cred(krb5_context context,krb5_ccache id,krb5_flags which,krb5_creds * cred)701 kcm_remove_cred(krb5_context context,
702 		krb5_ccache id,
703 		krb5_flags which,
704 		krb5_creds *cred)
705 {
706     krb5_error_code ret;
707     krb5_kcmcache *k = KCMCACHE(id);
708     krb5_storage *request;
709 
710     ret = krb5_kcm_storage_request(context, KCM_OP_REMOVE_CRED, &request);
711     if (ret)
712 	return ret;
713 
714     ret = krb5_store_stringz(request, k->name);
715     if (ret) {
716 	krb5_storage_free(request);
717 	return ret;
718     }
719 
720     ret = krb5_store_int32(request, which);
721     if (ret) {
722 	krb5_storage_free(request);
723 	return ret;
724     }
725 
726     ret = krb5_store_creds_tag(request, cred);
727     if (ret) {
728 	krb5_storage_free(request);
729 	return ret;
730     }
731 
732     ret = krb5_kcm_call(context, request, NULL, NULL);
733 
734     krb5_storage_free(request);
735     return ret;
736 }
737 
738 static krb5_error_code
kcm_set_flags(krb5_context context,krb5_ccache id,krb5_flags flags)739 kcm_set_flags(krb5_context context,
740 	      krb5_ccache id,
741 	      krb5_flags flags)
742 {
743     krb5_error_code ret;
744     krb5_kcmcache *k = KCMCACHE(id);
745     krb5_storage *request;
746 
747     ret = krb5_kcm_storage_request(context, KCM_OP_SET_FLAGS, &request);
748     if (ret)
749 	return ret;
750 
751     ret = krb5_store_stringz(request, k->name);
752     if (ret) {
753 	krb5_storage_free(request);
754 	return ret;
755     }
756 
757     ret = krb5_store_int32(request, flags);
758     if (ret) {
759 	krb5_storage_free(request);
760 	return ret;
761     }
762 
763     ret = krb5_kcm_call(context, request, NULL, NULL);
764 
765     krb5_storage_free(request);
766     return ret;
767 }
768 
769 static int
kcm_get_version(krb5_context context,krb5_ccache id)770 kcm_get_version(krb5_context context,
771 		krb5_ccache id)
772 {
773     return 0;
774 }
775 
776 /*
777  * Send nothing
778  * get back list of uuids
779  */
780 
781 static krb5_error_code
kcm_get_cache_first(krb5_context context,krb5_cc_cursor * cursor)782 kcm_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
783 {
784     krb5_error_code ret;
785     krb5_kcm_cursor c;
786     krb5_storage *request, *response;
787     krb5_data response_data;
788 
789     *cursor = NULL;
790 
791     c = calloc(1, sizeof(*c));
792     if (c == NULL) {
793 	ret = ENOMEM;
794 	krb5_set_error_message(context, ret,
795 			       N_("malloc: out of memory", ""));
796 	goto out;
797     }
798 
799     ret = krb5_kcm_storage_request(context, KCM_OP_GET_CACHE_UUID_LIST, &request);
800     if (ret)
801 	goto out;
802 
803     ret = krb5_kcm_call(context, request, &response, &response_data);
804     krb5_storage_free(request);
805     if (ret)
806 	goto out;
807 
808     while (1) {
809 	ssize_t sret;
810 	kcmuuid_t uuid;
811 	void *ptr;
812 
813 	sret = krb5_storage_read(response, &uuid, sizeof(uuid));
814 	if (sret == 0) {
815 	    ret = 0;
816 	    break;
817 	} else if (sret != sizeof(uuid)) {
818 	    ret = EINVAL;
819 	    goto out;
820 	}
821 
822 	ptr = realloc(c->uuids, sizeof(c->uuids[0]) * (c->length + 1));
823 	if (ptr == NULL) {
824 	    ret = ENOMEM;
825 	    krb5_set_error_message(context, ret,
826 				   N_("malloc: out of memory", ""));
827 	    goto out;
828 	}
829 	c->uuids = ptr;
830 
831 	memcpy(&c->uuids[c->length], &uuid, sizeof(uuid));
832 	c->length += 1;
833     }
834 
835     krb5_storage_free(response);
836     krb5_data_free(&response_data);
837 
838  out:
839     if (ret && c) {
840         free(c->uuids);
841         free(c);
842     } else
843 	*cursor = c;
844 
845     return ret;
846 }
847 
848 /*
849  * Send uuid
850  * Recv cache name
851  */
852 
853 static krb5_error_code
kcm_get_cache_next(krb5_context context,krb5_cc_cursor cursor,const krb5_cc_ops * ops,krb5_ccache * id)854 kcm_get_cache_next(krb5_context context, krb5_cc_cursor cursor, const krb5_cc_ops *ops, krb5_ccache *id)
855 {
856     krb5_error_code ret;
857     krb5_kcm_cursor c = KCMCURSOR(cursor);
858     krb5_storage *request, *response;
859     krb5_data response_data;
860     ssize_t sret;
861     char *name;
862 
863     *id = NULL;
864 
865  again:
866 
867     if (c->offset >= c->length)
868 	return KRB5_CC_END;
869 
870     ret = krb5_kcm_storage_request(context, KCM_OP_GET_CACHE_BY_UUID, &request);
871     if (ret)
872 	return ret;
873 
874     sret = krb5_storage_write(request,
875 			      &c->uuids[c->offset],
876 			      sizeof(c->uuids[c->offset]));
877     c->offset++;
878     if (sret != sizeof(c->uuids[c->offset])) {
879 	krb5_storage_free(request);
880 	krb5_clear_error_message(context);
881 	return ENOMEM;
882     }
883 
884     ret = krb5_kcm_call(context, request, &response, &response_data);
885     krb5_storage_free(request);
886     if (ret == KRB5_CC_END)
887 	goto again;
888 
889     ret = krb5_ret_stringz(response, &name);
890     krb5_storage_free(response);
891     krb5_data_free(&response_data);
892 
893     if (ret == 0) {
894 	ret = _krb5_cc_allocate(context, ops, id);
895 	if (ret == 0)
896 	    ret = kcm_alloc(context, name, id);
897 	krb5_xfree(name);
898     }
899 
900     return ret;
901 }
902 
903 static krb5_error_code
kcm_get_cache_next_kcm(krb5_context context,krb5_cc_cursor cursor,krb5_ccache * id)904 kcm_get_cache_next_kcm(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
905 {
906 #ifndef KCM_IS_API_CACHE
907     return kcm_get_cache_next(context, cursor, &krb5_kcm_ops, id);
908 #else
909     return KRB5_CC_END;
910 #endif
911 }
912 
913 static krb5_error_code
kcm_get_cache_next_api(krb5_context context,krb5_cc_cursor cursor,krb5_ccache * id)914 kcm_get_cache_next_api(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
915 {
916     return kcm_get_cache_next(context, cursor, &krb5_akcm_ops, id);
917 }
918 
919 
920 static krb5_error_code
kcm_end_cache_get(krb5_context context,krb5_cc_cursor cursor)921 kcm_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
922 {
923     krb5_kcm_cursor c = KCMCURSOR(cursor);
924 
925     free(c->uuids);
926     free(c);
927     return 0;
928 }
929 
930 
931 static krb5_error_code
kcm_move(krb5_context context,krb5_ccache from,krb5_ccache to)932 kcm_move(krb5_context context, krb5_ccache from, krb5_ccache to)
933 {
934     krb5_error_code ret;
935     krb5_kcmcache *oldk = KCMCACHE(from);
936     krb5_kcmcache *newk = KCMCACHE(to);
937     krb5_storage *request;
938 
939     ret = krb5_kcm_storage_request(context, KCM_OP_MOVE_CACHE, &request);
940     if (ret)
941 	return ret;
942 
943     ret = krb5_store_stringz(request, oldk->name);
944     if (ret) {
945 	krb5_storage_free(request);
946 	return ret;
947     }
948 
949     ret = krb5_store_stringz(request, newk->name);
950     if (ret) {
951 	krb5_storage_free(request);
952 	return ret;
953     }
954     ret = krb5_kcm_call(context, request, NULL, NULL);
955 
956     krb5_storage_free(request);
957     return ret;
958 }
959 
960 static krb5_error_code
kcm_get_default_name(krb5_context context,const krb5_cc_ops * ops,const char * defstr,char ** str)961 kcm_get_default_name(krb5_context context, const krb5_cc_ops *ops,
962 		     const char *defstr, char **str)
963 {
964     krb5_error_code ret;
965     krb5_storage *request, *response;
966     krb5_data response_data;
967     char *name;
968 
969     *str = NULL;
970 
971     ret = krb5_kcm_storage_request(context, KCM_OP_GET_DEFAULT_CACHE, &request);
972     if (ret)
973 	return ret;
974 
975     ret = krb5_kcm_call(context, request, &response, &response_data);
976     krb5_storage_free(request);
977     if (ret)
978 	return _krb5_expand_default_cc_name(context, defstr, str);
979 
980     ret = krb5_ret_stringz(response, &name);
981     krb5_storage_free(response);
982     krb5_data_free(&response_data);
983     if (ret)
984 	return ret;
985 
986     asprintf(str, "%s:%s", ops->prefix, name);
987     free(name);
988     if (str == NULL)
989 	return ENOMEM;
990 
991     return 0;
992 }
993 
994 static krb5_error_code
kcm_get_default_name_api(krb5_context context,char ** str)995 kcm_get_default_name_api(krb5_context context, char **str)
996 {
997     return kcm_get_default_name(context, &krb5_akcm_ops,
998 				KRB5_DEFAULT_CCNAME_KCM_API, str);
999 }
1000 
1001 static krb5_error_code
kcm_get_default_name_kcm(krb5_context context,char ** str)1002 kcm_get_default_name_kcm(krb5_context context, char **str)
1003 {
1004     return kcm_get_default_name(context, &krb5_kcm_ops,
1005 				KRB5_DEFAULT_CCNAME_KCM_KCM, str);
1006 }
1007 
1008 static krb5_error_code
kcm_set_default(krb5_context context,krb5_ccache id)1009 kcm_set_default(krb5_context context, krb5_ccache id)
1010 {
1011     krb5_error_code ret;
1012     krb5_storage *request;
1013     krb5_kcmcache *k = KCMCACHE(id);
1014 
1015     ret = krb5_kcm_storage_request(context, KCM_OP_SET_DEFAULT_CACHE, &request);
1016     if (ret)
1017 	return ret;
1018 
1019     ret = krb5_store_stringz(request, k->name);
1020     if (ret) {
1021 	krb5_storage_free(request);
1022 	return ret;
1023     }
1024 
1025     ret = krb5_kcm_call(context, request, NULL, NULL);
1026     krb5_storage_free(request);
1027 
1028     return ret;
1029 }
1030 
1031 static krb5_error_code
kcm_lastchange(krb5_context context,krb5_ccache id,krb5_timestamp * mtime)1032 kcm_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime)
1033 {
1034     *mtime = time(NULL);
1035     return 0;
1036 }
1037 
1038 static krb5_error_code
kcm_set_kdc_offset(krb5_context context,krb5_ccache id,krb5_deltat kdc_offset)1039 kcm_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat kdc_offset)
1040 {
1041     krb5_kcmcache *k = KCMCACHE(id);
1042     krb5_error_code ret;
1043     krb5_storage *request;
1044 
1045     ret = krb5_kcm_storage_request(context, KCM_OP_SET_KDC_OFFSET, &request);
1046     if (ret)
1047 	return ret;
1048 
1049     ret = krb5_store_stringz(request, k->name);
1050     if (ret) {
1051 	krb5_storage_free(request);
1052 	return ret;
1053     }
1054     ret = krb5_store_int32(request, kdc_offset);
1055     if (ret) {
1056 	krb5_storage_free(request);
1057 	return ret;
1058     }
1059 
1060     ret = krb5_kcm_call(context, request, NULL, NULL);
1061     krb5_storage_free(request);
1062 
1063     return ret;
1064 }
1065 
1066 static krb5_error_code
kcm_get_kdc_offset(krb5_context context,krb5_ccache id,krb5_deltat * kdc_offset)1067 kcm_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *kdc_offset)
1068 {
1069     krb5_kcmcache *k = KCMCACHE(id);
1070     krb5_error_code ret;
1071     krb5_storage *request, *response;
1072     krb5_data response_data;
1073     int32_t offset;
1074 
1075     ret = krb5_kcm_storage_request(context, KCM_OP_GET_KDC_OFFSET, &request);
1076     if (ret)
1077 	return ret;
1078 
1079     ret = krb5_store_stringz(request, k->name);
1080     if (ret) {
1081 	krb5_storage_free(request);
1082 	return ret;
1083     }
1084 
1085     ret = krb5_kcm_call(context, request, &response, &response_data);
1086     krb5_storage_free(request);
1087     if (ret)
1088 	return ret;
1089 
1090     ret = krb5_ret_int32(response, &offset);
1091     krb5_storage_free(response);
1092     krb5_data_free(&response_data);
1093     if (ret)
1094 	return ret;
1095 
1096     *kdc_offset = offset;
1097 
1098     return 0;
1099 }
1100 
1101 /**
1102  * Variable containing the KCM based credential cache implemention.
1103  *
1104  * @ingroup krb5_ccache
1105  */
1106 
1107 KRB5_LIB_VARIABLE const krb5_cc_ops krb5_kcm_ops = {
1108     KRB5_CC_OPS_VERSION,
1109     "KCM",
1110     kcm_get_name,
1111     kcm_resolve,
1112     kcm_gen_new,
1113     kcm_initialize,
1114     kcm_destroy,
1115     kcm_close,
1116     kcm_store_cred,
1117     NULL /* kcm_retrieve */,
1118     kcm_get_principal,
1119     kcm_get_first,
1120     kcm_get_next,
1121     kcm_end_get,
1122     kcm_remove_cred,
1123     kcm_set_flags,
1124     kcm_get_version,
1125     kcm_get_cache_first,
1126     kcm_get_cache_next_kcm,
1127     kcm_end_cache_get,
1128     kcm_move,
1129     kcm_get_default_name_kcm,
1130     kcm_set_default,
1131     kcm_lastchange,
1132     kcm_set_kdc_offset,
1133     kcm_get_kdc_offset
1134 };
1135 
1136 KRB5_LIB_VARIABLE const krb5_cc_ops krb5_akcm_ops = {
1137     KRB5_CC_OPS_VERSION,
1138     "API",
1139     kcm_get_name,
1140     kcm_resolve,
1141     kcm_gen_new,
1142     kcm_initialize,
1143     kcm_destroy,
1144     kcm_close,
1145     kcm_store_cred,
1146     NULL /* kcm_retrieve */,
1147     kcm_get_principal,
1148     kcm_get_first,
1149     kcm_get_next,
1150     kcm_end_get,
1151     kcm_remove_cred,
1152     kcm_set_flags,
1153     kcm_get_version,
1154     kcm_get_cache_first,
1155     kcm_get_cache_next_api,
1156     kcm_end_cache_get,
1157     kcm_move,
1158     kcm_get_default_name_api,
1159     kcm_set_default,
1160     kcm_lastchange,
1161     NULL,
1162     NULL
1163 };
1164 
1165 
1166 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
_krb5_kcm_is_running(krb5_context context)1167 _krb5_kcm_is_running(krb5_context context)
1168 {
1169     krb5_error_code ret;
1170     krb5_ccache_data ccdata;
1171     krb5_ccache id = &ccdata;
1172     krb5_boolean running;
1173 
1174     ret = kcm_alloc(context, NULL, &id);
1175     if (ret)
1176 	return 0;
1177 
1178     running = (_krb5_kcm_noop(context, id) == 0);
1179 
1180     kcm_free(context, &id);
1181 
1182     return running;
1183 }
1184 
1185 /*
1186  * Request:
1187  *
1188  * Response:
1189  *
1190  */
1191 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_kcm_noop(krb5_context context,krb5_ccache id)1192 _krb5_kcm_noop(krb5_context context,
1193 	       krb5_ccache id)
1194 {
1195     krb5_error_code ret;
1196     krb5_storage *request;
1197 
1198     ret = krb5_kcm_storage_request(context, KCM_OP_NOOP, &request);
1199     if (ret)
1200 	return ret;
1201 
1202     ret = krb5_kcm_call(context, request, NULL, NULL);
1203 
1204     krb5_storage_free(request);
1205     return ret;
1206 }
1207 
1208 
1209 /*
1210  * Request:
1211  *      NameZ
1212  *      ServerPrincipalPresent
1213  *      ServerPrincipal OPTIONAL
1214  *      Key
1215  *
1216  * Repsonse:
1217  *
1218  */
1219 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_kcm_get_initial_ticket(krb5_context context,krb5_ccache id,krb5_principal server,krb5_keyblock * key)1220 _krb5_kcm_get_initial_ticket(krb5_context context,
1221 			     krb5_ccache id,
1222 			     krb5_principal server,
1223 			     krb5_keyblock *key)
1224 {
1225     krb5_kcmcache *k = KCMCACHE(id);
1226     krb5_error_code ret;
1227     krb5_storage *request;
1228 
1229     ret = krb5_kcm_storage_request(context, KCM_OP_GET_INITIAL_TICKET, &request);
1230     if (ret)
1231 	return ret;
1232 
1233     ret = krb5_store_stringz(request, k->name);
1234     if (ret) {
1235 	krb5_storage_free(request);
1236 	return ret;
1237     }
1238 
1239     ret = krb5_store_int8(request, (server == NULL) ? 0 : 1);
1240     if (ret) {
1241 	krb5_storage_free(request);
1242 	return ret;
1243     }
1244 
1245     if (server != NULL) {
1246 	ret = krb5_store_principal(request, server);
1247 	if (ret) {
1248 	    krb5_storage_free(request);
1249 	    return ret;
1250 	}
1251     }
1252 
1253     ret = krb5_store_keyblock(request, *key);
1254     if (ret) {
1255 	krb5_storage_free(request);
1256 	return ret;
1257     }
1258 
1259     ret = krb5_kcm_call(context, request, NULL, NULL);
1260 
1261     krb5_storage_free(request);
1262     return ret;
1263 }
1264 
1265 
1266 /*
1267  * Request:
1268  *      NameZ
1269  *      KDCFlags
1270  *      EncryptionType
1271  *      ServerPrincipal
1272  *
1273  * Repsonse:
1274  *
1275  */
1276 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_kcm_get_ticket(krb5_context context,krb5_ccache id,krb5_kdc_flags flags,krb5_enctype enctype,krb5_principal server)1277 _krb5_kcm_get_ticket(krb5_context context,
1278 		     krb5_ccache id,
1279 		     krb5_kdc_flags flags,
1280 		     krb5_enctype enctype,
1281 		     krb5_principal server)
1282 {
1283     krb5_error_code ret;
1284     krb5_kcmcache *k = KCMCACHE(id);
1285     krb5_storage *request;
1286 
1287     ret = krb5_kcm_storage_request(context, KCM_OP_GET_TICKET, &request);
1288     if (ret)
1289 	return ret;
1290 
1291     ret = krb5_store_stringz(request, k->name);
1292     if (ret) {
1293 	krb5_storage_free(request);
1294 	return ret;
1295     }
1296 
1297     ret = krb5_store_int32(request, flags.i);
1298     if (ret) {
1299 	krb5_storage_free(request);
1300 	return ret;
1301     }
1302 
1303     ret = krb5_store_int32(request, enctype);
1304     if (ret) {
1305 	krb5_storage_free(request);
1306 	return ret;
1307     }
1308 
1309     ret = krb5_store_principal(request, server);
1310     if (ret) {
1311 	krb5_storage_free(request);
1312 	return ret;
1313     }
1314 
1315     ret = krb5_kcm_call(context, request, NULL, NULL);
1316 
1317     krb5_storage_free(request);
1318     return ret;
1319 }
1320 
1321 #endif /* HAVE_KCM */
1322