1 /* $NetBSD: kcm.c,v 1.3 2019/12/15 22:50:50 christos 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_s(k, sizeof(*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 = krb5_enomem(context);
559 return ret;
560 }
561
562 while (1) {
563 ssize_t sret;
564 kcmuuid_t uuid;
565 void *ptr;
566
567 sret = krb5_storage_read(response, &uuid, sizeof(uuid));
568 if (sret == 0) {
569 ret = 0;
570 break;
571 } else if (sret != sizeof(uuid)) {
572 ret = EINVAL;
573 break;
574 }
575
576 ptr = realloc(c->uuids, sizeof(c->uuids[0]) * (c->length + 1));
577 if (ptr == NULL) {
578 free(c->uuids);
579 free(c);
580 return krb5_enomem(context);
581 }
582 c->uuids = ptr;
583
584 memcpy(&c->uuids[c->length], &uuid, sizeof(uuid));
585 c->length += 1;
586 }
587
588 krb5_storage_free(response);
589 krb5_data_free(&response_data);
590
591 if (ret) {
592 free(c->uuids);
593 free(c);
594 return ret;
595 }
596
597 *cursor = c;
598
599 return 0;
600 }
601
602 /*
603 * Request:
604 * NameZ
605 * Cursor
606 *
607 * Response:
608 * Creds
609 */
610 static krb5_error_code
kcm_get_next(krb5_context context,krb5_ccache id,krb5_cc_cursor * cursor,krb5_creds * creds)611 kcm_get_next (krb5_context context,
612 krb5_ccache id,
613 krb5_cc_cursor *cursor,
614 krb5_creds *creds)
615 {
616 krb5_error_code ret;
617 krb5_kcmcache *k = KCMCACHE(id);
618 krb5_kcm_cursor c = KCMCURSOR(*cursor);
619 krb5_storage *request, *response;
620 krb5_data response_data;
621 ssize_t sret;
622
623 again:
624
625 if (c->offset >= c->length)
626 return KRB5_CC_END;
627
628 ret = krb5_kcm_storage_request(context, KCM_OP_GET_CRED_BY_UUID, &request);
629 if (ret)
630 return ret;
631
632 ret = krb5_store_stringz(request, k->name);
633 if (ret) {
634 krb5_storage_free(request);
635 return ret;
636 }
637
638 sret = krb5_storage_write(request,
639 &c->uuids[c->offset],
640 sizeof(c->uuids[c->offset]));
641 c->offset++;
642 if (sret != sizeof(c->uuids[c->offset])) {
643 krb5_storage_free(request);
644 krb5_clear_error_message(context);
645 return ENOMEM;
646 }
647
648 ret = krb5_kcm_call(context, request, &response, &response_data);
649 krb5_storage_free(request);
650 if (ret == KRB5_CC_END) {
651 goto again;
652 }
653
654 ret = krb5_ret_creds(response, creds);
655 if (ret)
656 ret = KRB5_CC_IO;
657
658 krb5_storage_free(response);
659 krb5_data_free(&response_data);
660
661 return ret;
662 }
663
664 /*
665 * Request:
666 * NameZ
667 * Cursor
668 *
669 * Response:
670 *
671 */
672 static krb5_error_code
kcm_end_get(krb5_context context,krb5_ccache id,krb5_cc_cursor * cursor)673 kcm_end_get (krb5_context context,
674 krb5_ccache id,
675 krb5_cc_cursor *cursor)
676 {
677 krb5_kcm_cursor c = KCMCURSOR(*cursor);
678
679 free(c->uuids);
680 free(c);
681
682 *cursor = NULL;
683
684 return 0;
685 }
686
687 /*
688 * Request:
689 * NameZ
690 * WhichFields
691 * MatchCreds
692 *
693 * Response:
694 *
695 */
696 static krb5_error_code
kcm_remove_cred(krb5_context context,krb5_ccache id,krb5_flags which,krb5_creds * cred)697 kcm_remove_cred(krb5_context context,
698 krb5_ccache id,
699 krb5_flags which,
700 krb5_creds *cred)
701 {
702 krb5_error_code ret;
703 krb5_kcmcache *k = KCMCACHE(id);
704 krb5_storage *request;
705
706 ret = krb5_kcm_storage_request(context, KCM_OP_REMOVE_CRED, &request);
707 if (ret)
708 return ret;
709
710 ret = krb5_store_stringz(request, k->name);
711 if (ret) {
712 krb5_storage_free(request);
713 return ret;
714 }
715
716 ret = krb5_store_int32(request, which);
717 if (ret) {
718 krb5_storage_free(request);
719 return ret;
720 }
721
722 ret = krb5_store_creds_tag(request, cred);
723 if (ret) {
724 krb5_storage_free(request);
725 return ret;
726 }
727
728 ret = krb5_kcm_call(context, request, NULL, NULL);
729
730 krb5_storage_free(request);
731 return ret;
732 }
733
734 static krb5_error_code
kcm_set_flags(krb5_context context,krb5_ccache id,krb5_flags flags)735 kcm_set_flags(krb5_context context,
736 krb5_ccache id,
737 krb5_flags flags)
738 {
739 krb5_error_code ret;
740 krb5_kcmcache *k = KCMCACHE(id);
741 krb5_storage *request;
742
743 ret = krb5_kcm_storage_request(context, KCM_OP_SET_FLAGS, &request);
744 if (ret)
745 return ret;
746
747 ret = krb5_store_stringz(request, k->name);
748 if (ret) {
749 krb5_storage_free(request);
750 return ret;
751 }
752
753 ret = krb5_store_int32(request, flags);
754 if (ret) {
755 krb5_storage_free(request);
756 return ret;
757 }
758
759 ret = krb5_kcm_call(context, request, NULL, NULL);
760
761 krb5_storage_free(request);
762 return ret;
763 }
764
765 static int
kcm_get_version(krb5_context context,krb5_ccache id)766 kcm_get_version(krb5_context context,
767 krb5_ccache id)
768 {
769 return 0;
770 }
771
772 /*
773 * Send nothing
774 * get back list of uuids
775 */
776
777 static krb5_error_code
kcm_get_cache_first(krb5_context context,krb5_cc_cursor * cursor)778 kcm_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
779 {
780 krb5_error_code ret;
781 krb5_kcm_cursor c;
782 krb5_storage *request, *response;
783 krb5_data response_data;
784
785 *cursor = NULL;
786
787 c = calloc(1, sizeof(*c));
788 if (c == NULL) {
789 ret = krb5_enomem(context);
790 goto out;
791 }
792
793 ret = krb5_kcm_storage_request(context, KCM_OP_GET_CACHE_UUID_LIST, &request);
794 if (ret)
795 goto out;
796
797 ret = krb5_kcm_call(context, request, &response, &response_data);
798 krb5_storage_free(request);
799 if (ret)
800 goto out;
801
802 while (1) {
803 ssize_t sret;
804 kcmuuid_t uuid;
805 void *ptr;
806
807 sret = krb5_storage_read(response, &uuid, sizeof(uuid));
808 if (sret == 0) {
809 ret = 0;
810 break;
811 } else if (sret != sizeof(uuid)) {
812 ret = EINVAL;
813 goto out;
814 }
815
816 ptr = realloc(c->uuids, sizeof(c->uuids[0]) * (c->length + 1));
817 if (ptr == NULL) {
818 ret = krb5_enomem(context);
819 goto out;
820 }
821 c->uuids = ptr;
822
823 memcpy(&c->uuids[c->length], &uuid, sizeof(uuid));
824 c->length += 1;
825 }
826
827 krb5_storage_free(response);
828 krb5_data_free(&response_data);
829
830 out:
831 if (ret && c) {
832 free(c->uuids);
833 free(c);
834 } else
835 *cursor = c;
836
837 return ret;
838 }
839
840 /*
841 * Send uuid
842 * Recv cache name
843 */
844
845 static krb5_error_code
kcm_get_cache_next(krb5_context context,krb5_cc_cursor cursor,const krb5_cc_ops * ops,krb5_ccache * id)846 kcm_get_cache_next(krb5_context context, krb5_cc_cursor cursor, const krb5_cc_ops *ops, krb5_ccache *id)
847 {
848 krb5_error_code ret;
849 krb5_kcm_cursor c = KCMCURSOR(cursor);
850 krb5_storage *request, *response;
851 krb5_data response_data;
852 ssize_t sret;
853 char *name;
854
855 *id = NULL;
856
857 again:
858
859 if (c->offset >= c->length)
860 return KRB5_CC_END;
861
862 ret = krb5_kcm_storage_request(context, KCM_OP_GET_CACHE_BY_UUID, &request);
863 if (ret)
864 return ret;
865
866 sret = krb5_storage_write(request,
867 &c->uuids[c->offset],
868 sizeof(c->uuids[c->offset]));
869 c->offset++;
870 if (sret != sizeof(c->uuids[c->offset])) {
871 krb5_storage_free(request);
872 krb5_clear_error_message(context);
873 return ENOMEM;
874 }
875
876 ret = krb5_kcm_call(context, request, &response, &response_data);
877 krb5_storage_free(request);
878 if (ret == KRB5_CC_END)
879 goto again;
880
881 ret = krb5_ret_stringz(response, &name);
882 krb5_storage_free(response);
883 krb5_data_free(&response_data);
884
885 if (ret == 0) {
886 ret = _krb5_cc_allocate(context, ops, id);
887 if (ret == 0)
888 ret = kcm_alloc(context, name, id);
889 krb5_xfree(name);
890 }
891
892 return ret;
893 }
894
895 static krb5_error_code
kcm_get_cache_next_kcm(krb5_context context,krb5_cc_cursor cursor,krb5_ccache * id)896 kcm_get_cache_next_kcm(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
897 {
898 #ifndef KCM_IS_API_CACHE
899 return kcm_get_cache_next(context, cursor, &krb5_kcm_ops, id);
900 #else
901 return KRB5_CC_END;
902 #endif
903 }
904
905 static krb5_error_code
kcm_get_cache_next_api(krb5_context context,krb5_cc_cursor cursor,krb5_ccache * id)906 kcm_get_cache_next_api(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
907 {
908 return kcm_get_cache_next(context, cursor, &krb5_akcm_ops, id);
909 }
910
911
912 static krb5_error_code
kcm_end_cache_get(krb5_context context,krb5_cc_cursor cursor)913 kcm_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
914 {
915 krb5_kcm_cursor c = KCMCURSOR(cursor);
916
917 free(c->uuids);
918 free(c);
919 return 0;
920 }
921
922
923 static krb5_error_code
kcm_move(krb5_context context,krb5_ccache from,krb5_ccache to)924 kcm_move(krb5_context context, krb5_ccache from, krb5_ccache to)
925 {
926 krb5_error_code ret;
927 krb5_kcmcache *oldk = KCMCACHE(from);
928 krb5_kcmcache *newk = KCMCACHE(to);
929 krb5_storage *request;
930
931 ret = krb5_kcm_storage_request(context, KCM_OP_MOVE_CACHE, &request);
932 if (ret)
933 return ret;
934
935 ret = krb5_store_stringz(request, oldk->name);
936 if (ret) {
937 krb5_storage_free(request);
938 return ret;
939 }
940
941 ret = krb5_store_stringz(request, newk->name);
942 if (ret) {
943 krb5_storage_free(request);
944 return ret;
945 }
946 ret = krb5_kcm_call(context, request, NULL, NULL);
947
948 krb5_storage_free(request);
949 return ret;
950 }
951
952 static krb5_error_code
kcm_get_default_name(krb5_context context,const krb5_cc_ops * ops,const char * defstr,char ** str)953 kcm_get_default_name(krb5_context context, const krb5_cc_ops *ops,
954 const char *defstr, char **str)
955 {
956 krb5_error_code ret;
957 krb5_storage *request, *response;
958 krb5_data response_data;
959 char *name;
960 int aret;
961
962 *str = NULL;
963
964 ret = krb5_kcm_storage_request(context, KCM_OP_GET_DEFAULT_CACHE, &request);
965 if (ret)
966 return ret;
967
968 ret = krb5_kcm_call(context, request, &response, &response_data);
969 krb5_storage_free(request);
970 if (ret)
971 return _krb5_expand_default_cc_name(context, defstr, str);
972
973 ret = krb5_ret_stringz(response, &name);
974 krb5_storage_free(response);
975 krb5_data_free(&response_data);
976 if (ret)
977 return ret;
978
979 aret = asprintf(str, "%s:%s", ops->prefix, name);
980 free(name);
981 if (aret == -1 || str == NULL)
982 return ENOMEM;
983
984 return 0;
985 }
986
987 static krb5_error_code
kcm_get_default_name_api(krb5_context context,char ** str)988 kcm_get_default_name_api(krb5_context context, char **str)
989 {
990 return kcm_get_default_name(context, &krb5_akcm_ops,
991 KRB5_DEFAULT_CCNAME_KCM_API, str);
992 }
993
994 static krb5_error_code
kcm_get_default_name_kcm(krb5_context context,char ** str)995 kcm_get_default_name_kcm(krb5_context context, char **str)
996 {
997 return kcm_get_default_name(context, &krb5_kcm_ops,
998 KRB5_DEFAULT_CCNAME_KCM_KCM, str);
999 }
1000
1001 static krb5_error_code
kcm_set_default(krb5_context context,krb5_ccache id)1002 kcm_set_default(krb5_context context, krb5_ccache id)
1003 {
1004 krb5_error_code ret;
1005 krb5_storage *request;
1006 krb5_kcmcache *k = KCMCACHE(id);
1007
1008 ret = krb5_kcm_storage_request(context, KCM_OP_SET_DEFAULT_CACHE, &request);
1009 if (ret)
1010 return ret;
1011
1012 ret = krb5_store_stringz(request, k->name);
1013 if (ret) {
1014 krb5_storage_free(request);
1015 return ret;
1016 }
1017
1018 ret = krb5_kcm_call(context, request, NULL, NULL);
1019 krb5_storage_free(request);
1020
1021 return ret;
1022 }
1023
1024 static krb5_error_code
kcm_lastchange(krb5_context context,krb5_ccache id,krb5_timestamp * mtime)1025 kcm_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime)
1026 {
1027 *mtime = time(NULL);
1028 return 0;
1029 }
1030
1031 static krb5_error_code
kcm_set_kdc_offset(krb5_context context,krb5_ccache id,krb5_deltat kdc_offset)1032 kcm_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat kdc_offset)
1033 {
1034 krb5_kcmcache *k = KCMCACHE(id);
1035 krb5_error_code ret;
1036 krb5_storage *request;
1037
1038 ret = krb5_kcm_storage_request(context, KCM_OP_SET_KDC_OFFSET, &request);
1039 if (ret)
1040 return ret;
1041
1042 ret = krb5_store_stringz(request, k->name);
1043 if (ret) {
1044 krb5_storage_free(request);
1045 return ret;
1046 }
1047 ret = krb5_store_int32(request, kdc_offset);
1048 if (ret) {
1049 krb5_storage_free(request);
1050 return ret;
1051 }
1052
1053 ret = krb5_kcm_call(context, request, NULL, NULL);
1054 krb5_storage_free(request);
1055
1056 return ret;
1057 }
1058
1059 static krb5_error_code
kcm_get_kdc_offset(krb5_context context,krb5_ccache id,krb5_deltat * kdc_offset)1060 kcm_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *kdc_offset)
1061 {
1062 krb5_kcmcache *k = KCMCACHE(id);
1063 krb5_error_code ret;
1064 krb5_storage *request, *response;
1065 krb5_data response_data;
1066 int32_t offset;
1067
1068 ret = krb5_kcm_storage_request(context, KCM_OP_GET_KDC_OFFSET, &request);
1069 if (ret)
1070 return ret;
1071
1072 ret = krb5_store_stringz(request, k->name);
1073 if (ret) {
1074 krb5_storage_free(request);
1075 return ret;
1076 }
1077
1078 ret = krb5_kcm_call(context, request, &response, &response_data);
1079 krb5_storage_free(request);
1080 if (ret)
1081 return ret;
1082
1083 ret = krb5_ret_int32(response, &offset);
1084 krb5_storage_free(response);
1085 krb5_data_free(&response_data);
1086 if (ret)
1087 return ret;
1088
1089 *kdc_offset = offset;
1090
1091 return 0;
1092 }
1093
1094 /**
1095 * Variable containing the KCM based credential cache implemention.
1096 *
1097 * @ingroup krb5_ccache
1098 */
1099
1100 KRB5_LIB_VARIABLE const krb5_cc_ops krb5_kcm_ops = {
1101 KRB5_CC_OPS_VERSION,
1102 "KCM",
1103 kcm_get_name,
1104 kcm_resolve,
1105 kcm_gen_new,
1106 kcm_initialize,
1107 kcm_destroy,
1108 kcm_close,
1109 kcm_store_cred,
1110 NULL /* kcm_retrieve */,
1111 kcm_get_principal,
1112 kcm_get_first,
1113 kcm_get_next,
1114 kcm_end_get,
1115 kcm_remove_cred,
1116 kcm_set_flags,
1117 kcm_get_version,
1118 kcm_get_cache_first,
1119 kcm_get_cache_next_kcm,
1120 kcm_end_cache_get,
1121 kcm_move,
1122 kcm_get_default_name_kcm,
1123 kcm_set_default,
1124 kcm_lastchange,
1125 kcm_set_kdc_offset,
1126 kcm_get_kdc_offset
1127 };
1128
1129 KRB5_LIB_VARIABLE const krb5_cc_ops krb5_akcm_ops = {
1130 KRB5_CC_OPS_VERSION,
1131 "API",
1132 kcm_get_name,
1133 kcm_resolve,
1134 kcm_gen_new,
1135 kcm_initialize,
1136 kcm_destroy,
1137 kcm_close,
1138 kcm_store_cred,
1139 NULL /* kcm_retrieve */,
1140 kcm_get_principal,
1141 kcm_get_first,
1142 kcm_get_next,
1143 kcm_end_get,
1144 kcm_remove_cred,
1145 kcm_set_flags,
1146 kcm_get_version,
1147 kcm_get_cache_first,
1148 kcm_get_cache_next_api,
1149 kcm_end_cache_get,
1150 kcm_move,
1151 kcm_get_default_name_api,
1152 kcm_set_default,
1153 kcm_lastchange,
1154 NULL,
1155 NULL
1156 };
1157
1158
1159 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
_krb5_kcm_is_running(krb5_context context)1160 _krb5_kcm_is_running(krb5_context context)
1161 {
1162 krb5_error_code ret;
1163 krb5_ccache_data ccdata;
1164 krb5_ccache id = &ccdata;
1165 krb5_boolean running;
1166
1167 ret = kcm_alloc(context, NULL, &id);
1168 if (ret)
1169 return 0;
1170
1171 running = (_krb5_kcm_noop(context, id) == 0);
1172
1173 kcm_free(context, &id);
1174
1175 return running;
1176 }
1177
1178 /*
1179 * Request:
1180 *
1181 * Response:
1182 *
1183 */
1184 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_kcm_noop(krb5_context context,krb5_ccache id)1185 _krb5_kcm_noop(krb5_context context,
1186 krb5_ccache id)
1187 {
1188 krb5_error_code ret;
1189 krb5_storage *request;
1190
1191 ret = krb5_kcm_storage_request(context, KCM_OP_NOOP, &request);
1192 if (ret)
1193 return ret;
1194
1195 ret = krb5_kcm_call(context, request, NULL, NULL);
1196
1197 krb5_storage_free(request);
1198 return ret;
1199 }
1200
1201
1202 /*
1203 * Request:
1204 * NameZ
1205 * ServerPrincipalPresent
1206 * ServerPrincipal OPTIONAL
1207 * Key
1208 *
1209 * Repsonse:
1210 *
1211 */
1212 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)1213 _krb5_kcm_get_initial_ticket(krb5_context context,
1214 krb5_ccache id,
1215 krb5_principal server,
1216 krb5_keyblock *key)
1217 {
1218 krb5_kcmcache *k = KCMCACHE(id);
1219 krb5_error_code ret;
1220 krb5_storage *request;
1221
1222 ret = krb5_kcm_storage_request(context, KCM_OP_GET_INITIAL_TICKET, &request);
1223 if (ret)
1224 return ret;
1225
1226 ret = krb5_store_stringz(request, k->name);
1227 if (ret) {
1228 krb5_storage_free(request);
1229 return ret;
1230 }
1231
1232 ret = krb5_store_int8(request, (server == NULL) ? 0 : 1);
1233 if (ret) {
1234 krb5_storage_free(request);
1235 return ret;
1236 }
1237
1238 if (server != NULL) {
1239 ret = krb5_store_principal(request, server);
1240 if (ret) {
1241 krb5_storage_free(request);
1242 return ret;
1243 }
1244 }
1245
1246 ret = krb5_store_keyblock(request, *key);
1247 if (ret) {
1248 krb5_storage_free(request);
1249 return ret;
1250 }
1251
1252 ret = krb5_kcm_call(context, request, NULL, NULL);
1253
1254 krb5_storage_free(request);
1255 return ret;
1256 }
1257
1258
1259 /*
1260 * Request:
1261 * NameZ
1262 * KDCFlags
1263 * EncryptionType
1264 * ServerPrincipal
1265 *
1266 * Repsonse:
1267 *
1268 */
1269 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)1270 _krb5_kcm_get_ticket(krb5_context context,
1271 krb5_ccache id,
1272 krb5_kdc_flags flags,
1273 krb5_enctype enctype,
1274 krb5_principal server)
1275 {
1276 krb5_error_code ret;
1277 krb5_kcmcache *k = KCMCACHE(id);
1278 krb5_storage *request;
1279
1280 ret = krb5_kcm_storage_request(context, KCM_OP_GET_TICKET, &request);
1281 if (ret)
1282 return ret;
1283
1284 ret = krb5_store_stringz(request, k->name);
1285 if (ret) {
1286 krb5_storage_free(request);
1287 return ret;
1288 }
1289
1290 ret = krb5_store_int32(request, flags.i);
1291 if (ret) {
1292 krb5_storage_free(request);
1293 return ret;
1294 }
1295
1296 ret = krb5_store_int32(request, enctype);
1297 if (ret) {
1298 krb5_storage_free(request);
1299 return ret;
1300 }
1301
1302 ret = krb5_store_principal(request, server);
1303 if (ret) {
1304 krb5_storage_free(request);
1305 return ret;
1306 }
1307
1308 ret = krb5_kcm_call(context, request, NULL, NULL);
1309
1310 krb5_storage_free(request);
1311 return ret;
1312 }
1313
1314 #endif /* HAVE_KCM */
1315