Home | History | Annotate | Line # | Download | only in krb5
kcm.c revision 1.1
      1 /*	$NetBSD: kcm.c,v 1.1 2011/04/13 18:15:34 elric 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(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 = ENOMEM;
    559 	krb5_set_error_message(context, ret,
    560 			       N_("malloc: out of memory", ""));
    561 	return ret;
    562     }
    563 
    564     while (1) {
    565 	ssize_t sret;
    566 	kcmuuid_t uuid;
    567 	void *ptr;
    568 
    569 	sret = krb5_storage_read(response, &uuid, sizeof(uuid));
    570 	if (sret == 0) {
    571 	    ret = 0;
    572 	    break;
    573 	} else if (sret != sizeof(uuid)) {
    574 	    ret = EINVAL;
    575 	    break;
    576 	}
    577 
    578 	ptr = realloc(c->uuids, sizeof(c->uuids[0]) * (c->length + 1));
    579 	if (ptr == NULL) {
    580 	    free(c->uuids);
    581 	    free(c);
    582 	    krb5_set_error_message(context, ENOMEM,
    583 				   N_("malloc: out of memory", ""));
    584 	    return ENOMEM;
    585 	}
    586 	c->uuids = ptr;
    587 
    588 	memcpy(&c->uuids[c->length], &uuid, sizeof(uuid));
    589 	c->length += 1;
    590     }
    591 
    592     krb5_storage_free(response);
    593     krb5_data_free(&response_data);
    594 
    595     if (ret) {
    596         free(c->uuids);
    597         free(c);
    598 	return ret;
    599     }
    600 
    601     *cursor = c;
    602 
    603     return 0;
    604 }
    605 
    606 /*
    607  * Request:
    608  *      NameZ
    609  *      Cursor
    610  *
    611  * Response:
    612  *      Creds
    613  */
    614 static krb5_error_code
    615 kcm_get_next (krb5_context context,
    616 		krb5_ccache id,
    617 		krb5_cc_cursor *cursor,
    618 		krb5_creds *creds)
    619 {
    620     krb5_error_code ret;
    621     krb5_kcmcache *k = KCMCACHE(id);
    622     krb5_kcm_cursor c = KCMCURSOR(*cursor);
    623     krb5_storage *request, *response;
    624     krb5_data response_data;
    625     ssize_t sret;
    626 
    627  again:
    628 
    629     if (c->offset >= c->length)
    630 	return KRB5_CC_END;
    631 
    632     ret = krb5_kcm_storage_request(context, KCM_OP_GET_CRED_BY_UUID, &request);
    633     if (ret)
    634 	return ret;
    635 
    636     ret = krb5_store_stringz(request, k->name);
    637     if (ret) {
    638 	krb5_storage_free(request);
    639 	return ret;
    640     }
    641 
    642     sret = krb5_storage_write(request,
    643 			      &c->uuids[c->offset],
    644 			      sizeof(c->uuids[c->offset]));
    645     c->offset++;
    646     if (sret != sizeof(c->uuids[c->offset])) {
    647 	krb5_storage_free(request);
    648 	krb5_clear_error_message(context);
    649 	return ENOMEM;
    650     }
    651 
    652     ret = krb5_kcm_call(context, request, &response, &response_data);
    653     krb5_storage_free(request);
    654     if (ret == KRB5_CC_END) {
    655 	goto again;
    656     }
    657 
    658     ret = krb5_ret_creds(response, creds);
    659     if (ret)
    660 	ret = KRB5_CC_IO;
    661 
    662     krb5_storage_free(response);
    663     krb5_data_free(&response_data);
    664 
    665     return ret;
    666 }
    667 
    668 /*
    669  * Request:
    670  *      NameZ
    671  *      Cursor
    672  *
    673  * Response:
    674  *
    675  */
    676 static krb5_error_code
    677 kcm_end_get (krb5_context context,
    678 	     krb5_ccache id,
    679 	     krb5_cc_cursor *cursor)
    680 {
    681     krb5_kcm_cursor c = KCMCURSOR(*cursor);
    682 
    683     free(c->uuids);
    684     free(c);
    685 
    686     *cursor = NULL;
    687 
    688     return 0;
    689 }
    690 
    691 /*
    692  * Request:
    693  *      NameZ
    694  *      WhichFields
    695  *      MatchCreds
    696  *
    697  * Response:
    698  *
    699  */
    700 static krb5_error_code
    701 kcm_remove_cred(krb5_context context,
    702 		krb5_ccache id,
    703 		krb5_flags which,
    704 		krb5_creds *cred)
    705 {
    706     krb5_error_code ret;
    707     krb5_kcmcache *k = KCMCACHE(id);
    708     krb5_storage *request;
    709 
    710     ret = krb5_kcm_storage_request(context, KCM_OP_REMOVE_CRED, &request);
    711     if (ret)
    712 	return ret;
    713 
    714     ret = krb5_store_stringz(request, k->name);
    715     if (ret) {
    716 	krb5_storage_free(request);
    717 	return ret;
    718     }
    719 
    720     ret = krb5_store_int32(request, which);
    721     if (ret) {
    722 	krb5_storage_free(request);
    723 	return ret;
    724     }
    725 
    726     ret = krb5_store_creds_tag(request, cred);
    727     if (ret) {
    728 	krb5_storage_free(request);
    729 	return ret;
    730     }
    731 
    732     ret = krb5_kcm_call(context, request, NULL, NULL);
    733 
    734     krb5_storage_free(request);
    735     return ret;
    736 }
    737 
    738 static krb5_error_code
    739 kcm_set_flags(krb5_context context,
    740 	      krb5_ccache id,
    741 	      krb5_flags flags)
    742 {
    743     krb5_error_code ret;
    744     krb5_kcmcache *k = KCMCACHE(id);
    745     krb5_storage *request;
    746 
    747     ret = krb5_kcm_storage_request(context, KCM_OP_SET_FLAGS, &request);
    748     if (ret)
    749 	return ret;
    750 
    751     ret = krb5_store_stringz(request, k->name);
    752     if (ret) {
    753 	krb5_storage_free(request);
    754 	return ret;
    755     }
    756 
    757     ret = krb5_store_int32(request, flags);
    758     if (ret) {
    759 	krb5_storage_free(request);
    760 	return ret;
    761     }
    762 
    763     ret = krb5_kcm_call(context, request, NULL, NULL);
    764 
    765     krb5_storage_free(request);
    766     return ret;
    767 }
    768 
    769 static int
    770 kcm_get_version(krb5_context context,
    771 		krb5_ccache id)
    772 {
    773     return 0;
    774 }
    775 
    776 /*
    777  * Send nothing
    778  * get back list of uuids
    779  */
    780 
    781 static krb5_error_code
    782 kcm_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
    783 {
    784     krb5_error_code ret;
    785     krb5_kcm_cursor c;
    786     krb5_storage *request, *response;
    787     krb5_data response_data;
    788 
    789     *cursor = NULL;
    790 
    791     c = calloc(1, sizeof(*c));
    792     if (c == NULL) {
    793 	ret = ENOMEM;
    794 	krb5_set_error_message(context, ret,
    795 			       N_("malloc: out of memory", ""));
    796 	goto out;
    797     }
    798 
    799     ret = krb5_kcm_storage_request(context, KCM_OP_GET_CACHE_UUID_LIST, &request);
    800     if (ret)
    801 	goto out;
    802 
    803     ret = krb5_kcm_call(context, request, &response, &response_data);
    804     krb5_storage_free(request);
    805     if (ret)
    806 	goto out;
    807 
    808     while (1) {
    809 	ssize_t sret;
    810 	kcmuuid_t uuid;
    811 	void *ptr;
    812 
    813 	sret = krb5_storage_read(response, &uuid, sizeof(uuid));
    814 	if (sret == 0) {
    815 	    ret = 0;
    816 	    break;
    817 	} else if (sret != sizeof(uuid)) {
    818 	    ret = EINVAL;
    819 	    goto out;
    820 	}
    821 
    822 	ptr = realloc(c->uuids, sizeof(c->uuids[0]) * (c->length + 1));
    823 	if (ptr == NULL) {
    824 	    ret = ENOMEM;
    825 	    krb5_set_error_message(context, ret,
    826 				   N_("malloc: out of memory", ""));
    827 	    goto out;
    828 	}
    829 	c->uuids = ptr;
    830 
    831 	memcpy(&c->uuids[c->length], &uuid, sizeof(uuid));
    832 	c->length += 1;
    833     }
    834 
    835     krb5_storage_free(response);
    836     krb5_data_free(&response_data);
    837 
    838  out:
    839     if (ret && c) {
    840         free(c->uuids);
    841         free(c);
    842     } else
    843 	*cursor = c;
    844 
    845     return ret;
    846 }
    847 
    848 /*
    849  * Send uuid
    850  * Recv cache name
    851  */
    852 
    853 static krb5_error_code
    854 kcm_get_cache_next(krb5_context context, krb5_cc_cursor cursor, const krb5_cc_ops *ops, krb5_ccache *id)
    855 {
    856     krb5_error_code ret;
    857     krb5_kcm_cursor c = KCMCURSOR(cursor);
    858     krb5_storage *request, *response;
    859     krb5_data response_data;
    860     ssize_t sret;
    861     char *name;
    862 
    863     *id = NULL;
    864 
    865  again:
    866 
    867     if (c->offset >= c->length)
    868 	return KRB5_CC_END;
    869 
    870     ret = krb5_kcm_storage_request(context, KCM_OP_GET_CACHE_BY_UUID, &request);
    871     if (ret)
    872 	return ret;
    873 
    874     sret = krb5_storage_write(request,
    875 			      &c->uuids[c->offset],
    876 			      sizeof(c->uuids[c->offset]));
    877     c->offset++;
    878     if (sret != sizeof(c->uuids[c->offset])) {
    879 	krb5_storage_free(request);
    880 	krb5_clear_error_message(context);
    881 	return ENOMEM;
    882     }
    883 
    884     ret = krb5_kcm_call(context, request, &response, &response_data);
    885     krb5_storage_free(request);
    886     if (ret == KRB5_CC_END)
    887 	goto again;
    888 
    889     ret = krb5_ret_stringz(response, &name);
    890     krb5_storage_free(response);
    891     krb5_data_free(&response_data);
    892 
    893     if (ret == 0) {
    894 	ret = _krb5_cc_allocate(context, ops, id);
    895 	if (ret == 0)
    896 	    ret = kcm_alloc(context, name, id);
    897 	krb5_xfree(name);
    898     }
    899 
    900     return ret;
    901 }
    902 
    903 static krb5_error_code
    904 kcm_get_cache_next_kcm(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
    905 {
    906 #ifndef KCM_IS_API_CACHE
    907     return kcm_get_cache_next(context, cursor, &krb5_kcm_ops, id);
    908 #else
    909     return KRB5_CC_END;
    910 #endif
    911 }
    912 
    913 static krb5_error_code
    914 kcm_get_cache_next_api(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
    915 {
    916     return kcm_get_cache_next(context, cursor, &krb5_akcm_ops, id);
    917 }
    918 
    919 
    920 static krb5_error_code
    921 kcm_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
    922 {
    923     krb5_kcm_cursor c = KCMCURSOR(cursor);
    924 
    925     free(c->uuids);
    926     free(c);
    927     return 0;
    928 }
    929 
    930 
    931 static krb5_error_code
    932 kcm_move(krb5_context context, krb5_ccache from, krb5_ccache to)
    933 {
    934     krb5_error_code ret;
    935     krb5_kcmcache *oldk = KCMCACHE(from);
    936     krb5_kcmcache *newk = KCMCACHE(to);
    937     krb5_storage *request;
    938 
    939     ret = krb5_kcm_storage_request(context, KCM_OP_MOVE_CACHE, &request);
    940     if (ret)
    941 	return ret;
    942 
    943     ret = krb5_store_stringz(request, oldk->name);
    944     if (ret) {
    945 	krb5_storage_free(request);
    946 	return ret;
    947     }
    948 
    949     ret = krb5_store_stringz(request, newk->name);
    950     if (ret) {
    951 	krb5_storage_free(request);
    952 	return ret;
    953     }
    954     ret = krb5_kcm_call(context, request, NULL, NULL);
    955 
    956     krb5_storage_free(request);
    957     return ret;
    958 }
    959 
    960 static krb5_error_code
    961 kcm_get_default_name(krb5_context context, const krb5_cc_ops *ops,
    962 		     const char *defstr, char **str)
    963 {
    964     krb5_error_code ret;
    965     krb5_storage *request, *response;
    966     krb5_data response_data;
    967     char *name;
    968 
    969     *str = NULL;
    970 
    971     ret = krb5_kcm_storage_request(context, KCM_OP_GET_DEFAULT_CACHE, &request);
    972     if (ret)
    973 	return ret;
    974 
    975     ret = krb5_kcm_call(context, request, &response, &response_data);
    976     krb5_storage_free(request);
    977     if (ret)
    978 	return _krb5_expand_default_cc_name(context, defstr, str);
    979 
    980     ret = krb5_ret_stringz(response, &name);
    981     krb5_storage_free(response);
    982     krb5_data_free(&response_data);
    983     if (ret)
    984 	return ret;
    985 
    986     asprintf(str, "%s:%s", ops->prefix, name);
    987     free(name);
    988     if (str == NULL)
    989 	return ENOMEM;
    990 
    991     return 0;
    992 }
    993 
    994 static krb5_error_code
    995 kcm_get_default_name_api(krb5_context context, char **str)
    996 {
    997     return kcm_get_default_name(context, &krb5_akcm_ops,
    998 				KRB5_DEFAULT_CCNAME_KCM_API, str);
    999 }
   1000 
   1001 static krb5_error_code
   1002 kcm_get_default_name_kcm(krb5_context context, char **str)
   1003 {
   1004     return kcm_get_default_name(context, &krb5_kcm_ops,
   1005 				KRB5_DEFAULT_CCNAME_KCM_KCM, str);
   1006 }
   1007 
   1008 static krb5_error_code
   1009 kcm_set_default(krb5_context context, krb5_ccache id)
   1010 {
   1011     krb5_error_code ret;
   1012     krb5_storage *request;
   1013     krb5_kcmcache *k = KCMCACHE(id);
   1014 
   1015     ret = krb5_kcm_storage_request(context, KCM_OP_SET_DEFAULT_CACHE, &request);
   1016     if (ret)
   1017 	return ret;
   1018 
   1019     ret = krb5_store_stringz(request, k->name);
   1020     if (ret) {
   1021 	krb5_storage_free(request);
   1022 	return ret;
   1023     }
   1024 
   1025     ret = krb5_kcm_call(context, request, NULL, NULL);
   1026     krb5_storage_free(request);
   1027 
   1028     return ret;
   1029 }
   1030 
   1031 static krb5_error_code
   1032 kcm_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime)
   1033 {
   1034     *mtime = time(NULL);
   1035     return 0;
   1036 }
   1037 
   1038 static krb5_error_code
   1039 kcm_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat kdc_offset)
   1040 {
   1041     krb5_kcmcache *k = KCMCACHE(id);
   1042     krb5_error_code ret;
   1043     krb5_storage *request;
   1044 
   1045     ret = krb5_kcm_storage_request(context, KCM_OP_SET_KDC_OFFSET, &request);
   1046     if (ret)
   1047 	return ret;
   1048 
   1049     ret = krb5_store_stringz(request, k->name);
   1050     if (ret) {
   1051 	krb5_storage_free(request);
   1052 	return ret;
   1053     }
   1054     ret = krb5_store_int32(request, kdc_offset);
   1055     if (ret) {
   1056 	krb5_storage_free(request);
   1057 	return ret;
   1058     }
   1059 
   1060     ret = krb5_kcm_call(context, request, NULL, NULL);
   1061     krb5_storage_free(request);
   1062 
   1063     return ret;
   1064 }
   1065 
   1066 static krb5_error_code
   1067 kcm_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *kdc_offset)
   1068 {
   1069     krb5_kcmcache *k = KCMCACHE(id);
   1070     krb5_error_code ret;
   1071     krb5_storage *request, *response;
   1072     krb5_data response_data;
   1073     int32_t offset;
   1074 
   1075     ret = krb5_kcm_storage_request(context, KCM_OP_GET_KDC_OFFSET, &request);
   1076     if (ret)
   1077 	return ret;
   1078 
   1079     ret = krb5_store_stringz(request, k->name);
   1080     if (ret) {
   1081 	krb5_storage_free(request);
   1082 	return ret;
   1083     }
   1084 
   1085     ret = krb5_kcm_call(context, request, &response, &response_data);
   1086     krb5_storage_free(request);
   1087     if (ret)
   1088 	return ret;
   1089 
   1090     ret = krb5_ret_int32(response, &offset);
   1091     krb5_storage_free(response);
   1092     krb5_data_free(&response_data);
   1093     if (ret)
   1094 	return ret;
   1095 
   1096     *kdc_offset = offset;
   1097 
   1098     return 0;
   1099 }
   1100 
   1101 /**
   1102  * Variable containing the KCM based credential cache implemention.
   1103  *
   1104  * @ingroup krb5_ccache
   1105  */
   1106 
   1107 KRB5_LIB_VARIABLE const krb5_cc_ops krb5_kcm_ops = {
   1108     KRB5_CC_OPS_VERSION,
   1109     "KCM",
   1110     kcm_get_name,
   1111     kcm_resolve,
   1112     kcm_gen_new,
   1113     kcm_initialize,
   1114     kcm_destroy,
   1115     kcm_close,
   1116     kcm_store_cred,
   1117     NULL /* kcm_retrieve */,
   1118     kcm_get_principal,
   1119     kcm_get_first,
   1120     kcm_get_next,
   1121     kcm_end_get,
   1122     kcm_remove_cred,
   1123     kcm_set_flags,
   1124     kcm_get_version,
   1125     kcm_get_cache_first,
   1126     kcm_get_cache_next_kcm,
   1127     kcm_end_cache_get,
   1128     kcm_move,
   1129     kcm_get_default_name_kcm,
   1130     kcm_set_default,
   1131     kcm_lastchange,
   1132     kcm_set_kdc_offset,
   1133     kcm_get_kdc_offset
   1134 };
   1135 
   1136 KRB5_LIB_VARIABLE const krb5_cc_ops krb5_akcm_ops = {
   1137     KRB5_CC_OPS_VERSION,
   1138     "API",
   1139     kcm_get_name,
   1140     kcm_resolve,
   1141     kcm_gen_new,
   1142     kcm_initialize,
   1143     kcm_destroy,
   1144     kcm_close,
   1145     kcm_store_cred,
   1146     NULL /* kcm_retrieve */,
   1147     kcm_get_principal,
   1148     kcm_get_first,
   1149     kcm_get_next,
   1150     kcm_end_get,
   1151     kcm_remove_cred,
   1152     kcm_set_flags,
   1153     kcm_get_version,
   1154     kcm_get_cache_first,
   1155     kcm_get_cache_next_api,
   1156     kcm_end_cache_get,
   1157     kcm_move,
   1158     kcm_get_default_name_api,
   1159     kcm_set_default,
   1160     kcm_lastchange
   1161 };
   1162 
   1163 
   1164 krb5_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  */
   1189 krb5_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  */
   1217 krb5_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  */
   1274 krb5_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