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