Home | History | Annotate | Line # | Download | only in krb5
kcm.c revision 1.3
      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
     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
    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
    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
    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
    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 *
    235 kcm_get_name(krb5_context context,
    236 	     krb5_ccache id)
    237 {
    238     return CACHENAME(id);
    239 }
    240 
    241 static krb5_error_code
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
   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
   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
   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
   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
   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
   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
   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
   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