11590Srgrimes/*	$NetBSD$	*/
21590Srgrimes
31590Srgrimes/*
41590Srgrimes * Copyright (c) 2005, PADL Software Pty Ltd.
51590Srgrimes * All rights reserved.
61590Srgrimes *
71590Srgrimes * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
81590Srgrimes *
91590Srgrimes * Redistribution and use in source and binary forms, with or without
101590Srgrimes * modification, are permitted provided that the following conditions
111590Srgrimes * are met:
121590Srgrimes *
131590Srgrimes * 1. Redistributions of source code must retain the above copyright
141590Srgrimes *    notice, this list of conditions and the following disclaimer.
151590Srgrimes *
161590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
171590Srgrimes *    notice, this list of conditions and the following disclaimer in the
181590Srgrimes *    documentation and/or other materials provided with the distribution.
191590Srgrimes *
201590Srgrimes * 3. Neither the name of PADL Software nor the names of its contributors
211590Srgrimes *    may be used to endorse or promote products derived from this software
221590Srgrimes *    without specific prior written permission.
231590Srgrimes *
241590Srgrimes * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
251590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
261590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
271590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
281590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
291590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
301590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
311590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
321590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
331590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
341590Srgrimes * SUCH DAMAGE.
351590Srgrimes */
361590Srgrimes
371590Srgrimes#include "krb5_locl.h"
3828794Scharnier
391590Srgrimes#ifdef HAVE_KCM
401590Srgrimes/*
411590Srgrimes * Client library for Kerberos Credentials Manager (KCM) daemon
421590Srgrimes */
431590Srgrimes
4428794Scharnier#include <krb5/kcm.h>
451590Srgrimes#include <heim-ipc.h>
4628794Scharnier
4728794Scharnierstatic krb5_error_code
4848566Sbillfkcm_set_kdc_offset(krb5_context, krb5_ccache, krb5_deltat);
491590Srgrimes
501590Srgrimesstatic const char *kcm_ipc_name = "ANY:org.h5l.kcm";
511590Srgrimes
521590Srgrimestypedef struct krb5_kcmcache {
531590Srgrimes    char *name;
541590Srgrimes} krb5_kcmcache;
551590Srgrimes
561590Srgrimestypedef struct krb5_kcm_cursor {
5728794Scharnier    unsigned long offset;
5829430Sache    unsigned long length;
5919190Salex    kcmuuid_t *uuids;
601590Srgrimes} *krb5_kcm_cursor;
611590Srgrimes
621590Srgrimes
6328794Scharnier#define KCMCACHE(X)	((krb5_kcmcache *)(X)->data.data)
6428794Scharnier#define CACHENAME(X)	(KCMCACHE(X)->name)
651590Srgrimes#define KCMCURSOR(C)	((krb5_kcm_cursor)(C))
6629430Sache
6728794Scharnierstatic HEIMDAL_MUTEX kcm_mutex = HEIMDAL_MUTEX_INITIALIZER;
6828794Scharnierstatic heim_ipc kcm_ipc = NULL;
6928794Scharnier
7028794Scharnierstatic krb5_error_code
7128794Scharnierkcm_send_request(krb5_context context,
7228794Scharnier		 krb5_storage *request,
731590Srgrimes		 krb5_data *response_data)
7428794Scharnier{
751590Srgrimes    krb5_error_code ret = 0;
761590Srgrimes    krb5_data request_data;
771590Srgrimes
781590Srgrimes    HEIMDAL_MUTEX_lock(&kcm_mutex);
791590Srgrimes    if (kcm_ipc == NULL)
801590Srgrimes	ret = heim_ipc_init_context(kcm_ipc_name, &kcm_ipc);
811590Srgrimes    HEIMDAL_MUTEX_unlock(&kcm_mutex);
821590Srgrimes    if (ret)
8328794Scharnier	return KRB5_CC_NOSUPP;
841590Srgrimes
8529430Sache    ret = krb5_storage_to_data(request, &request_data);
8629430Sache    if (ret) {
871590Srgrimes	krb5_clear_error_message(context);
881590Srgrimes	return KRB5_CC_NOMEM;
891590Srgrimes    }
901590Srgrimes
911590Srgrimes    ret = heim_ipc_call(kcm_ipc, &request_data, response_data, NULL);
921590Srgrimes    krb5_data_free(&request_data);
931590Srgrimes
9428794Scharnier    if (ret) {
9528794Scharnier	krb5_clear_error_message(context);
9628794Scharnier	ret = KRB5_CC_NOSUPP;
9728794Scharnier    }
9828794Scharnier
991590Srgrimes    return ret;
1001590Srgrimes}
1011590Srgrimes
10228794ScharnierKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
10328794Scharnierkrb5_kcm_storage_request(krb5_context context,
1041590Srgrimes			 uint16_t opcode,
1051590Srgrimes			 krb5_storage **storage_p)
1061590Srgrimes{
1071590Srgrimes    krb5_storage *sp;
1081590Srgrimes    krb5_error_code ret;
1091590Srgrimes
1101590Srgrimes    *storage_p = NULL;
1111590Srgrimes
1121590Srgrimes    sp = krb5_storage_emem();
1131590Srgrimes    if (sp == NULL) {
11419190Salex	krb5_set_error_message(context, KRB5_CC_NOMEM, N_("malloc: out of memory", ""));
11519193Salex	return KRB5_CC_NOMEM;
11628794Scharnier    }
11728794Scharnier
1181590Srgrimes    /* Send MAJOR | VERSION | OPCODE */
1191590Srgrimes    ret  = krb5_store_int8(sp, KCM_PROTOCOL_VERSION_MAJOR);
12028794Scharnier    if (ret)
12128794Scharnier	goto fail;
1221590Srgrimes    ret = krb5_store_int8(sp, KCM_PROTOCOL_VERSION_MINOR);
1231590Srgrimes    if (ret)
1241590Srgrimes	goto fail;
12528794Scharnier    ret = krb5_store_int16(sp, opcode);
1261590Srgrimes    if (ret)
12729430Sache	goto fail;
12828794Scharnier
1291590Srgrimes    *storage_p = sp;
1301590Srgrimes fail:
13128794Scharnier    if (ret) {
13228794Scharnier	krb5_set_error_message(context, ret,
13328794Scharnier			       N_("Failed to encode KCM request", ""));
13428794Scharnier	krb5_storage_free(sp);
13528794Scharnier    }
13628794Scharnier
13728794Scharnier    return ret;
1381590Srgrimes}
1391590Srgrimes
1401590Srgrimesstatic krb5_error_code
1411590Srgrimeskcm_alloc(krb5_context context, const char *name, krb5_ccache *id)
14228794Scharnier{
1431590Srgrimes    krb5_kcmcache *k;
1441590Srgrimes
1451590Srgrimes    k = malloc(sizeof(*k));
1461590Srgrimes    if (k == NULL) {
1471590Srgrimes	krb5_set_error_message(context, KRB5_CC_NOMEM,
1481590Srgrimes			       N_("malloc: out of memory", ""));
1491590Srgrimes	return KRB5_CC_NOMEM;
1501590Srgrimes    }
1511590Srgrimes
1521590Srgrimes    if (name != NULL) {
1531590Srgrimes	k->name = strdup(name);
1541590Srgrimes	if (k->name == NULL) {
1551590Srgrimes	    free(k);
1561590Srgrimes	    krb5_set_error_message(context, KRB5_CC_NOMEM,
1571590Srgrimes				   N_("malloc: out of memory", ""));
1581590Srgrimes	    return KRB5_CC_NOMEM;
1591590Srgrimes	}
1601590Srgrimes    } else
1611590Srgrimes	k->name = NULL;
1621590Srgrimes
1631590Srgrimes    (*id)->data.data = k;
1641590Srgrimes    (*id)->data.length = sizeof(*k);
1651590Srgrimes
1661590Srgrimes    return 0;
1671590Srgrimes}
1681590Srgrimes
1691590SrgrimesKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1701590Srgrimeskrb5_kcm_call(krb5_context context,
1711590Srgrimes	      krb5_storage *request,
1721590Srgrimes	      krb5_storage **response_p,
1731590Srgrimes	      krb5_data *response_data_p)
17428794Scharnier{
1751590Srgrimes    krb5_data response_data;
1761590Srgrimes    krb5_error_code ret;
1771590Srgrimes    int32_t status;
1781590Srgrimes    krb5_storage *response;
1791590Srgrimes
1801590Srgrimes    if (response_p != NULL)
1811590Srgrimes	*response_p = NULL;
1821590Srgrimes
1831590Srgrimes    krb5_data_zero(&response_data);
18428794Scharnier
18528794Scharnier    ret = kcm_send_request(context, request, &response_data);
1861590Srgrimes    if (ret)
1871590Srgrimes	return ret;
1881590Srgrimes
1891590Srgrimes    response = krb5_storage_from_data(&response_data);
1901590Srgrimes    if (response == NULL) {
1911590Srgrimes	krb5_data_free(&response_data);
1921590Srgrimes	return KRB5_CC_IO;
1931590Srgrimes    }
1941590Srgrimes
1951590Srgrimes    ret = krb5_ret_int32(response, &status);
1961590Srgrimes    if (ret) {
1971590Srgrimes	krb5_storage_free(response);
1981590Srgrimes	krb5_data_free(&response_data);
1991590Srgrimes	return KRB5_CC_FORMAT;
2001590Srgrimes    }
2011590Srgrimes
2021590Srgrimes    if (status) {
2031590Srgrimes	krb5_storage_free(response);
2041590Srgrimes	krb5_data_free(&response_data);
2051590Srgrimes	return status;
2061590Srgrimes    }
2071590Srgrimes
2081590Srgrimes    if (response_p != NULL) {
2091590Srgrimes	*response_data_p = response_data;
2101590Srgrimes	*response_p = response;
21128794Scharnier
21228794Scharnier	return 0;
2131590Srgrimes    }
2141590Srgrimes
2151590Srgrimes    krb5_storage_free(response);
2161590Srgrimes    krb5_data_free(&response_data);
2171590Srgrimes
21828794Scharnier    return 0;
2191590Srgrimes}
22028794Scharnier
2211590Srgrimesstatic void
2221590Srgrimeskcm_free(krb5_context context, krb5_ccache *id)
2231590Srgrimes{
2241590Srgrimes    krb5_kcmcache *k = KCMCACHE(*id);
2251590Srgrimes
2261590Srgrimes    if (k != NULL) {
2271590Srgrimes	if (k->name != NULL)
22828794Scharnier	    free(k->name);
2291590Srgrimes	memset(k, 0, sizeof(*k));
2301590Srgrimes	krb5_data_free(&(*id)->data);
2311590Srgrimes    }
2321590Srgrimes}
2331590Srgrimes
2341590Srgrimesstatic const char *
2351590Srgrimeskcm_get_name(krb5_context context,
2361590Srgrimes	     krb5_ccache id)
23719190Salex{
2381590Srgrimes    return CACHENAME(id);
2391590Srgrimes}
24028794Scharnier
2411590Srgrimesstatic krb5_error_code
2421590Srgrimeskcm_resolve(krb5_context context, krb5_ccache *id, const char *res)
2431590Srgrimes{
2441590Srgrimes    return kcm_alloc(context, res, id);
2451590Srgrimes}
2461590Srgrimes
2471590Srgrimes/*
2481590Srgrimes * Request:
2491590Srgrimes *
2501590Srgrimes * Response:
25128794Scharnier *      NameZ
2521590Srgrimes */
2531590Srgrimesstatic krb5_error_code
2541590Srgrimeskcm_gen_new(krb5_context context, krb5_ccache *id)
2551590Srgrimes{
2561590Srgrimes    krb5_kcmcache *k;
2571590Srgrimes    krb5_error_code ret;
25828794Scharnier    krb5_storage *request, *response;
25928794Scharnier    krb5_data response_data;
2601590Srgrimes
2611590Srgrimes    ret = kcm_alloc(context, NULL, id);
26248566Sbillf    if (ret)
26328794Scharnier	return ret;
2641590Srgrimes
2651590Srgrimes    k = KCMCACHE(*id);
2661590Srgrimes
26748566Sbillf    ret = krb5_kcm_storage_request(context, KCM_OP_GEN_NEW, &request);
2681590Srgrimes    if (ret) {
26919190Salex	kcm_free(context, id);
27028794Scharnier	return ret;
27128794Scharnier    }
2721590Srgrimes
2731590Srgrimes    ret = krb5_kcm_call(context, request, &response, &response_data);
2741590Srgrimes    if (ret) {
2751590Srgrimes	krb5_storage_free(request);
2761590Srgrimes	kcm_free(context, id);
2771590Srgrimes	return ret;
2781590Srgrimes    }
2791590Srgrimes
2801590Srgrimes    ret = krb5_ret_stringz(response, &k->name);
2811590Srgrimes    if (ret)
2821590Srgrimes	ret = KRB5_CC_IO;
2831590Srgrimes
2841590Srgrimes    krb5_storage_free(request);
2851590Srgrimes    krb5_storage_free(response);
2861590Srgrimes    krb5_data_free(&response_data);
2871590Srgrimes
2881590Srgrimes    if (ret)
2891590Srgrimes	kcm_free(context, id);
2901590Srgrimes
2911590Srgrimes    return ret;
2921590Srgrimes}
29329430Sache
29429430Sache/*
2951590Srgrimes * Request:
2961590Srgrimes *      NameZ
2971590Srgrimes *      Principal
2981590Srgrimes *
2991590Srgrimes * Response:
3001590Srgrimes *
3011590Srgrimes */
3021590Srgrimesstatic krb5_error_code
3031590Srgrimeskcm_initialize(krb5_context context,
30428794Scharnier	       krb5_ccache id,
3051590Srgrimes	       krb5_principal primary_principal)
30612097Sache{
3071590Srgrimes    krb5_error_code ret;
3081590Srgrimes    krb5_kcmcache *k = KCMCACHE(id);
30928794Scharnier    krb5_storage *request;
3101590Srgrimes
3111590Srgrimes    ret = krb5_kcm_storage_request(context, KCM_OP_INITIALIZE, &request);
31211916Sache    if (ret)
3131590Srgrimes	return ret;
31429431Sache
31529431Sache    ret = krb5_store_stringz(request, k->name);
31629433Sache    if (ret) {
31729433Sache	krb5_storage_free(request);
31829430Sache	return ret;
31912097Sache    }
32012097Sache
32112097Sache    ret = krb5_store_principal(request, primary_principal);
32212097Sache    if (ret) {
32312097Sache	krb5_storage_free(request);
32412097Sache	return ret;
32512097Sache    }
32612097Sache
32712097Sache    ret = krb5_kcm_call(context, request, NULL, NULL);
32812097Sache
32912097Sache    krb5_storage_free(request);
3301590Srgrimes
3311590Srgrimes    if (context->kdc_sec_offset)
3321590Srgrimes	kcm_set_kdc_offset(context, id, context->kdc_sec_offset);
3331590Srgrimes
334    return ret;
335}
336
337static krb5_error_code
338kcm_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 */
352static krb5_error_code
353kcm_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 */
384static krb5_error_code
385kcm_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 */
426static krb5_error_code
427kcm_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 */
485static krb5_error_code
486kcm_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 */
530static krb5_error_code
531kcm_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 */
614static krb5_error_code
615kcm_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 */
676static krb5_error_code
677kcm_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 */
700static krb5_error_code
701kcm_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
738static krb5_error_code
739kcm_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
769static int
770kcm_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
781static krb5_error_code
782kcm_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
853static krb5_error_code
854kcm_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
903static krb5_error_code
904kcm_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
913static krb5_error_code
914kcm_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
920static krb5_error_code
921kcm_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
931static krb5_error_code
932kcm_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
960static krb5_error_code
961kcm_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
994static krb5_error_code
995kcm_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
1001static krb5_error_code
1002kcm_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
1008static krb5_error_code
1009kcm_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
1031static krb5_error_code
1032kcm_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime)
1033{
1034    *mtime = time(NULL);
1035    return 0;
1036}
1037
1038static krb5_error_code
1039kcm_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
1066static krb5_error_code
1067kcm_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
1107KRB5_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
1136KRB5_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};
1162
1163
1164krb5_boolean
1165_krb5_kcm_is_running(krb5_context context)
1166{
1167    krb5_error_code ret;
1168    krb5_ccache_data ccdata;
1169    krb5_ccache id = &ccdata;
1170    krb5_boolean running;
1171
1172    ret = kcm_alloc(context, NULL, &id);
1173    if (ret)
1174	return 0;
1175
1176    running = (_krb5_kcm_noop(context, id) == 0);
1177
1178    kcm_free(context, &id);
1179
1180    return running;
1181}
1182
1183/*
1184 * Request:
1185 *
1186 * Response:
1187 *
1188 */
1189krb5_error_code
1190_krb5_kcm_noop(krb5_context context,
1191	       krb5_ccache id)
1192{
1193    krb5_error_code ret;
1194    krb5_storage *request;
1195
1196    ret = krb5_kcm_storage_request(context, KCM_OP_NOOP, &request);
1197    if (ret)
1198	return ret;
1199
1200    ret = krb5_kcm_call(context, request, NULL, NULL);
1201
1202    krb5_storage_free(request);
1203    return ret;
1204}
1205
1206
1207/*
1208 * Request:
1209 *      NameZ
1210 *      ServerPrincipalPresent
1211 *      ServerPrincipal OPTIONAL
1212 *      Key
1213 *
1214 * Repsonse:
1215 *
1216 */
1217krb5_error_code
1218_krb5_kcm_get_initial_ticket(krb5_context context,
1219			     krb5_ccache id,
1220			     krb5_principal server,
1221			     krb5_keyblock *key)
1222{
1223    krb5_kcmcache *k = KCMCACHE(id);
1224    krb5_error_code ret;
1225    krb5_storage *request;
1226
1227    ret = krb5_kcm_storage_request(context, KCM_OP_GET_INITIAL_TICKET, &request);
1228    if (ret)
1229	return ret;
1230
1231    ret = krb5_store_stringz(request, k->name);
1232    if (ret) {
1233	krb5_storage_free(request);
1234	return ret;
1235    }
1236
1237    ret = krb5_store_int8(request, (server == NULL) ? 0 : 1);
1238    if (ret) {
1239	krb5_storage_free(request);
1240	return ret;
1241    }
1242
1243    if (server != NULL) {
1244	ret = krb5_store_principal(request, server);
1245	if (ret) {
1246	    krb5_storage_free(request);
1247	    return ret;
1248	}
1249    }
1250
1251    ret = krb5_store_keyblock(request, *key);
1252    if (ret) {
1253	krb5_storage_free(request);
1254	return ret;
1255    }
1256
1257    ret = krb5_kcm_call(context, request, NULL, NULL);
1258
1259    krb5_storage_free(request);
1260    return ret;
1261}
1262
1263
1264/*
1265 * Request:
1266 *      NameZ
1267 *      KDCFlags
1268 *      EncryptionType
1269 *      ServerPrincipal
1270 *
1271 * Repsonse:
1272 *
1273 */
1274krb5_error_code
1275_krb5_kcm_get_ticket(krb5_context context,
1276		     krb5_ccache id,
1277		     krb5_kdc_flags flags,
1278		     krb5_enctype enctype,
1279		     krb5_principal server)
1280{
1281    krb5_error_code ret;
1282    krb5_kcmcache *k = KCMCACHE(id);
1283    krb5_storage *request;
1284
1285    ret = krb5_kcm_storage_request(context, KCM_OP_GET_TICKET, &request);
1286    if (ret)
1287	return ret;
1288
1289    ret = krb5_store_stringz(request, k->name);
1290    if (ret) {
1291	krb5_storage_free(request);
1292	return ret;
1293    }
1294
1295    ret = krb5_store_int32(request, flags.i);
1296    if (ret) {
1297	krb5_storage_free(request);
1298	return ret;
1299    }
1300
1301    ret = krb5_store_int32(request, enctype);
1302    if (ret) {
1303	krb5_storage_free(request);
1304	return ret;
1305    }
1306
1307    ret = krb5_store_principal(request, server);
1308    if (ret) {
1309	krb5_storage_free(request);
1310	return ret;
1311    }
1312
1313    ret = krb5_kcm_call(context, request, NULL, NULL);
1314
1315    krb5_storage_free(request);
1316    return ret;
1317}
1318
1319#endif /* HAVE_KCM */
1320