Home | History | Annotate | Line # | Download | only in kcm
      1 /*	$NetBSD: protocol.c,v 1.3 2023/06/19 21:41:41 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 "kcm_locl.h"
     38 #include <krb5/heimntlm.h>
     39 
     40 static void
     41 kcm_drop_default_cache(krb5_context context, kcm_client *client, char *name);
     42 
     43 
     44 int
     45 kcm_is_same_session(kcm_client *client, uid_t uid, pid_t session)
     46 {
     47 #if 0 /* XXX pppd is running in diffrent session the user */
     48     if (session != -1)
     49 	return (client->session == session);
     50     else
     51 #endif
     52 	return  (client->uid == uid);
     53 }
     54 
     55 static krb5_error_code
     56 kcm_op_noop(krb5_context context,
     57 	    kcm_client *client,
     58 	    kcm_operation opcode,
     59 	    krb5_storage *request,
     60 	    krb5_storage *response)
     61 {
     62     KCM_LOG_REQUEST(context, client, opcode);
     63 
     64     return 0;
     65 }
     66 
     67 /*
     68  * Request:
     69  *	NameZ
     70  * Response:
     71  *	NameZ
     72  *
     73  */
     74 static krb5_error_code
     75 kcm_op_get_name(krb5_context context,
     76 		kcm_client *client,
     77 		kcm_operation opcode,
     78 		krb5_storage *request,
     79 		krb5_storage *response)
     80 
     81 {
     82     krb5_error_code ret;
     83     char *name = NULL;
     84     kcm_ccache ccache;
     85 
     86     ret = krb5_ret_stringz(request, &name);
     87     if (ret)
     88 	return ret;
     89 
     90     KCM_LOG_REQUEST_NAME(context, client, opcode, name);
     91 
     92     ret = kcm_ccache_resolve_client(context, client, opcode,
     93 				    name, &ccache);
     94     if (ret) {
     95 	free(name);
     96 	return ret;
     97     }
     98 
     99     ret = krb5_store_stringz(response, ccache->name);
    100     if (ret) {
    101 	kcm_release_ccache(context, ccache);
    102 	free(name);
    103 	return ret;
    104     }
    105 
    106     free(name);
    107     kcm_release_ccache(context, ccache);
    108     return 0;
    109 }
    110 
    111 /*
    112  * Request:
    113  *
    114  * Response:
    115  *	NameZ
    116  */
    117 static krb5_error_code
    118 kcm_op_gen_new(krb5_context context,
    119 	       kcm_client *client,
    120 	       kcm_operation opcode,
    121 	       krb5_storage *request,
    122 	       krb5_storage *response)
    123 {
    124     krb5_error_code ret;
    125     char *name;
    126 
    127     KCM_LOG_REQUEST(context, client, opcode);
    128 
    129     name = kcm_ccache_nextid(client->pid, client->uid, client->gid);
    130     if (name == NULL) {
    131 	return KRB5_CC_NOMEM;
    132     }
    133 
    134     ret = krb5_store_stringz(response, name);
    135     free(name);
    136 
    137     return ret;
    138 }
    139 
    140 /*
    141  * Request:
    142  *	NameZ
    143  *	Principal
    144  *
    145  * Response:
    146  *
    147  */
    148 static krb5_error_code
    149 kcm_op_initialize(krb5_context context,
    150 		  kcm_client *client,
    151 		  kcm_operation opcode,
    152 		  krb5_storage *request,
    153 		  krb5_storage *response)
    154 {
    155     kcm_ccache ccache;
    156     krb5_principal principal;
    157     krb5_error_code ret;
    158     char *name;
    159 #if 0
    160     kcm_event event;
    161 #endif
    162 
    163     KCM_LOG_REQUEST(context, client, opcode);
    164 
    165     ret = krb5_ret_stringz(request, &name);
    166     if (ret)
    167 	return ret;
    168 
    169     ret = krb5_ret_principal(request, &principal);
    170     if (ret) {
    171 	free(name);
    172 	return ret;
    173     }
    174 
    175     ret = kcm_ccache_new_client(context, client, name, &ccache);
    176     if (ret) {
    177 	free(name);
    178 	krb5_free_principal(context, principal);
    179 	return ret;
    180     }
    181 
    182     ccache->client = principal;
    183 
    184     free(name);
    185 
    186 #if 0
    187     /*
    188      * Create a new credentials cache. To mitigate DoS attacks we will
    189      * expire it in 30 minutes unless it has some credentials added
    190      * to it
    191      */
    192 
    193     event.fire_time = 30 * 60;
    194     event.expire_time = 0;
    195     event.backoff_time = 0;
    196     event.action = KCM_EVENT_DESTROY_EMPTY_CACHE;
    197     event.ccache = ccache;
    198 
    199     ret = kcm_enqueue_event_relative(context, &event);
    200 #endif
    201 
    202     kcm_release_ccache(context, ccache);
    203 
    204     return ret;
    205 }
    206 
    207 /*
    208  * Request:
    209  *	NameZ
    210  *
    211  * Response:
    212  *
    213  */
    214 static krb5_error_code
    215 kcm_op_destroy(krb5_context context,
    216 	       kcm_client *client,
    217 	       kcm_operation opcode,
    218 	       krb5_storage *request,
    219 	       krb5_storage *response)
    220 {
    221     krb5_error_code ret;
    222     char *name;
    223 
    224     ret = krb5_ret_stringz(request, &name);
    225     if (ret)
    226 	return ret;
    227 
    228     KCM_LOG_REQUEST_NAME(context, client, opcode, name);
    229 
    230     ret = kcm_ccache_destroy_client(context, client, name);
    231     if (ret == 0)
    232 	kcm_drop_default_cache(context, client, name);
    233 
    234     free(name);
    235 
    236     return ret;
    237 }
    238 
    239 /*
    240  * Request:
    241  *	NameZ
    242  *	Creds
    243  *
    244  * Response:
    245  *
    246  */
    247 static krb5_error_code
    248 kcm_op_store(krb5_context context,
    249 	     kcm_client *client,
    250 	     kcm_operation opcode,
    251 	     krb5_storage *request,
    252 	     krb5_storage *response)
    253 {
    254     krb5_creds creds;
    255     krb5_error_code ret;
    256     kcm_ccache ccache;
    257     char *name;
    258 
    259     ret = krb5_ret_stringz(request, &name);
    260     if (ret)
    261 	return ret;
    262 
    263     KCM_LOG_REQUEST_NAME(context, client, opcode, name);
    264 
    265     ret = krb5_ret_creds(request, &creds);
    266     if (ret) {
    267 	free(name);
    268 	return ret;
    269     }
    270 
    271     ret = kcm_ccache_resolve_client(context, client, opcode,
    272 				    name, &ccache);
    273     if (ret) {
    274 	free(name);
    275 	krb5_free_cred_contents(context, &creds);
    276 	return ret;
    277     }
    278 
    279     ret = kcm_ccache_store_cred(context, ccache, &creds, 0);
    280     if (ret) {
    281 	free(name);
    282 	krb5_free_cred_contents(context, &creds);
    283 	kcm_release_ccache(context, ccache);
    284 	return ret;
    285     }
    286 
    287     kcm_ccache_enqueue_default(context, ccache, &creds);
    288 
    289     free(name);
    290     kcm_release_ccache(context, ccache);
    291 
    292     return 0;
    293 }
    294 
    295 /*
    296  * Request:
    297  *	NameZ
    298  *	WhichFields
    299  *	MatchCreds
    300  *
    301  * Response:
    302  *	Creds
    303  *
    304  */
    305 static krb5_error_code
    306 kcm_op_retrieve(krb5_context context,
    307 		kcm_client *client,
    308 		kcm_operation opcode,
    309 		krb5_storage *request,
    310 		krb5_storage *response)
    311 {
    312     uint32_t flags;
    313     krb5_creds mcreds;
    314     krb5_error_code ret;
    315     kcm_ccache ccache;
    316     char *name;
    317     krb5_creds *credp;
    318     int free_creds = 0;
    319 
    320     ret = krb5_ret_stringz(request, &name);
    321     if (ret)
    322 	return ret;
    323 
    324     KCM_LOG_REQUEST_NAME(context, client, opcode, name);
    325 
    326     ret = krb5_ret_uint32(request, &flags);
    327     if (ret) {
    328 	free(name);
    329 	return ret;
    330     }
    331 
    332     ret = krb5_ret_creds_tag(request, &mcreds);
    333     if (ret) {
    334 	free(name);
    335 	return ret;
    336     }
    337 
    338     if (disallow_getting_krbtgt &&
    339 	mcreds.server->name.name_string.len == 2 &&
    340 	strcmp(mcreds.server->name.name_string.val[0], KRB5_TGS_NAME) == 0)
    341     {
    342 	free(name);
    343 	krb5_free_cred_contents(context, &mcreds);
    344 	return KRB5_FCC_PERM;
    345     }
    346 
    347     ret = kcm_ccache_resolve_client(context, client, opcode,
    348 				    name, &ccache);
    349     if (ret) {
    350 	free(name);
    351 	krb5_free_cred_contents(context, &mcreds);
    352 	return ret;
    353     }
    354 
    355     ret = kcm_ccache_retrieve_cred(context, ccache, flags,
    356 				   &mcreds, &credp);
    357     if (ret && ((flags & KRB5_GC_CACHED) == 0) &&
    358 	!krb5_is_config_principal(context, mcreds.server)) {
    359 	krb5_ccache_data ccdata;
    360 
    361 	/* try and acquire */
    362 	HEIMDAL_MUTEX_lock(&ccache->mutex);
    363 
    364 	/* Fake up an internal ccache */
    365 	kcm_internal_ccache(context, ccache, &ccdata);
    366 
    367 	/* glue cc layer will store creds */
    368 	ret = krb5_get_credentials(context, 0, &ccdata, &mcreds, &credp);
    369 	if (ret == 0)
    370 	    free_creds = 1;
    371 
    372 	HEIMDAL_MUTEX_unlock(&ccache->mutex);
    373     }
    374 
    375     if (ret == 0) {
    376 	ret = krb5_store_creds(response, credp);
    377     }
    378 
    379     free(name);
    380     krb5_free_cred_contents(context, &mcreds);
    381     kcm_release_ccache(context, ccache);
    382 
    383     if (free_creds)
    384 	krb5_free_cred_contents(context, credp);
    385 
    386     return ret;
    387 }
    388 
    389 /*
    390  * Request:
    391  *	NameZ
    392  *
    393  * Response:
    394  *	Principal
    395  */
    396 static krb5_error_code
    397 kcm_op_get_principal(krb5_context context,
    398 		     kcm_client *client,
    399 		     kcm_operation opcode,
    400 		     krb5_storage *request,
    401 		     krb5_storage *response)
    402 {
    403     krb5_error_code ret;
    404     kcm_ccache ccache;
    405     char *name;
    406 
    407     ret = krb5_ret_stringz(request, &name);
    408     if (ret)
    409 	return ret;
    410 
    411     KCM_LOG_REQUEST_NAME(context, client, opcode, name);
    412 
    413     ret = kcm_ccache_resolve_client(context, client, opcode,
    414 				    name, &ccache);
    415     if (ret) {
    416 	free(name);
    417 	return ret;
    418     }
    419 
    420     if (ccache->client == NULL)
    421 	ret = KRB5_CC_NOTFOUND;
    422     else
    423 	ret = krb5_store_principal(response, ccache->client);
    424 
    425     free(name);
    426     kcm_release_ccache(context, ccache);
    427 
    428     return ret;
    429 }
    430 
    431 /*
    432  * Request:
    433  *	NameZ
    434  *
    435  * Response:
    436  *	UUIDs
    437  *
    438  */
    439 static krb5_error_code
    440 kcm_op_get_cred_uuid_list(krb5_context context,
    441 			  kcm_client *client,
    442 			  kcm_operation opcode,
    443 			  krb5_storage *request,
    444 			  krb5_storage *response)
    445 {
    446     struct kcm_creds *creds;
    447     krb5_error_code ret;
    448     kcm_ccache ccache;
    449     char *name;
    450 
    451     ret = krb5_ret_stringz(request, &name);
    452     if (ret)
    453 	return ret;
    454 
    455     KCM_LOG_REQUEST_NAME(context, client, opcode, name);
    456 
    457     ret = kcm_ccache_resolve_client(context, client, opcode,
    458 				    name, &ccache);
    459     free(name);
    460     if (ret)
    461 	return ret;
    462 
    463     for (creds = ccache->creds ; creds ; creds = creds->next) {
    464 	ssize_t sret;
    465 	sret = krb5_storage_write(response, &creds->uuid, sizeof(creds->uuid));
    466 	if (sret != sizeof(creds->uuid)) {
    467 	    ret = ENOMEM;
    468 	    break;
    469 	}
    470     }
    471 
    472     kcm_release_ccache(context, ccache);
    473 
    474     return ret;
    475 }
    476 
    477 /*
    478  * Request:
    479  *	NameZ
    480  *	Cursor
    481  *
    482  * Response:
    483  *	Creds
    484  */
    485 static krb5_error_code
    486 kcm_op_get_cred_by_uuid(krb5_context context,
    487 			kcm_client *client,
    488 			kcm_operation opcode,
    489 			krb5_storage *request,
    490 			krb5_storage *response)
    491 {
    492     krb5_error_code ret;
    493     kcm_ccache ccache;
    494     char *name;
    495     struct kcm_creds *c;
    496     kcmuuid_t uuid;
    497     ssize_t sret;
    498 
    499     ret = krb5_ret_stringz(request, &name);
    500     if (ret)
    501 	return ret;
    502 
    503     KCM_LOG_REQUEST_NAME(context, client, opcode, name);
    504 
    505     ret = kcm_ccache_resolve_client(context, client, opcode,
    506 				    name, &ccache);
    507     free(name);
    508     if (ret)
    509 	return ret;
    510 
    511     sret = krb5_storage_read(request, &uuid, sizeof(uuid));
    512     if (sret != sizeof(uuid)) {
    513 	kcm_release_ccache(context, ccache);
    514 	krb5_clear_error_message(context);
    515 	return KRB5_CC_IO;
    516     }
    517 
    518     c = kcm_ccache_find_cred_uuid(context, ccache, uuid);
    519     if (c == NULL) {
    520 	kcm_release_ccache(context, ccache);
    521 	return KRB5_CC_END;
    522     }
    523 
    524     HEIMDAL_MUTEX_lock(&ccache->mutex);
    525     ret = krb5_store_creds(response, &c->cred);
    526     HEIMDAL_MUTEX_unlock(&ccache->mutex);
    527 
    528     kcm_release_ccache(context, ccache);
    529 
    530     return ret;
    531 }
    532 
    533 /*
    534  * Request:
    535  *	NameZ
    536  *	WhichFields
    537  *	MatchCreds
    538  *
    539  * Response:
    540  *
    541  */
    542 static krb5_error_code
    543 kcm_op_remove_cred(krb5_context context,
    544 		   kcm_client *client,
    545 		   kcm_operation opcode,
    546 		   krb5_storage *request,
    547 		   krb5_storage *response)
    548 {
    549     uint32_t whichfields;
    550     krb5_creds mcreds;
    551     krb5_error_code ret;
    552     kcm_ccache ccache;
    553     char *name;
    554 
    555     ret = krb5_ret_stringz(request, &name);
    556     if (ret)
    557 	return ret;
    558 
    559     KCM_LOG_REQUEST_NAME(context, client, opcode, name);
    560 
    561     ret = krb5_ret_uint32(request, &whichfields);
    562     if (ret) {
    563 	free(name);
    564 	return ret;
    565     }
    566 
    567     ret = krb5_ret_creds_tag(request, &mcreds);
    568     if (ret) {
    569 	free(name);
    570 	return ret;
    571     }
    572 
    573     ret = kcm_ccache_resolve_client(context, client, opcode,
    574 				    name, &ccache);
    575     if (ret) {
    576 	free(name);
    577 	krb5_free_cred_contents(context, &mcreds);
    578 	return ret;
    579     }
    580 
    581     ret = kcm_ccache_remove_cred(context, ccache, whichfields, &mcreds);
    582 
    583     /* XXX need to remove any events that match */
    584 
    585     free(name);
    586     krb5_free_cred_contents(context, &mcreds);
    587     kcm_release_ccache(context, ccache);
    588 
    589     return ret;
    590 }
    591 
    592 /*
    593  * Request:
    594  *	NameZ
    595  *	Flags
    596  *
    597  * Response:
    598  *
    599  */
    600 static krb5_error_code
    601 kcm_op_set_flags(krb5_context context,
    602 		 kcm_client *client,
    603 		 kcm_operation opcode,
    604 		 krb5_storage *request,
    605 		 krb5_storage *response)
    606 {
    607     uint32_t flags;
    608     krb5_error_code ret;
    609     kcm_ccache ccache;
    610     char *name;
    611 
    612     ret = krb5_ret_stringz(request, &name);
    613     if (ret)
    614 	return ret;
    615 
    616     KCM_LOG_REQUEST_NAME(context, client, opcode, name);
    617 
    618     ret = krb5_ret_uint32(request, &flags);
    619     if (ret) {
    620 	free(name);
    621 	return ret;
    622     }
    623 
    624     ret = kcm_ccache_resolve_client(context, client, opcode,
    625 				    name, &ccache);
    626     if (ret) {
    627 	free(name);
    628 	return ret;
    629     }
    630 
    631     /* we don't really support any flags yet */
    632     free(name);
    633     kcm_release_ccache(context, ccache);
    634 
    635     return 0;
    636 }
    637 
    638 /*
    639  * Request:
    640  *	NameZ
    641  *	UID
    642  *	GID
    643  *
    644  * Response:
    645  *
    646  */
    647 static krb5_error_code
    648 kcm_op_chown(krb5_context context,
    649 	     kcm_client *client,
    650 	     kcm_operation opcode,
    651 	     krb5_storage *request,
    652 	     krb5_storage *response)
    653 {
    654     uint32_t uid;
    655     uint32_t gid;
    656     krb5_error_code ret;
    657     kcm_ccache ccache;
    658     char *name;
    659 
    660     ret = krb5_ret_stringz(request, &name);
    661     if (ret)
    662 	return ret;
    663 
    664     KCM_LOG_REQUEST_NAME(context, client, opcode, name);
    665 
    666     ret = krb5_ret_uint32(request, &uid);
    667     if (ret) {
    668 	free(name);
    669 	return ret;
    670     }
    671 
    672     ret = krb5_ret_uint32(request, &gid);
    673     if (ret) {
    674 	free(name);
    675 	return ret;
    676     }
    677 
    678     ret = kcm_ccache_resolve_client(context, client, opcode,
    679 				    name, &ccache);
    680     if (ret) {
    681 	free(name);
    682 	return ret;
    683     }
    684 
    685     ret = kcm_chown(context, client, ccache, uid, gid);
    686 
    687     free(name);
    688     kcm_release_ccache(context, ccache);
    689 
    690     return ret;
    691 }
    692 
    693 /*
    694  * Request:
    695  *	NameZ
    696  *	Mode
    697  *
    698  * Response:
    699  *
    700  */
    701 static krb5_error_code
    702 kcm_op_chmod(krb5_context context,
    703 	     kcm_client *client,
    704 	     kcm_operation opcode,
    705 	     krb5_storage *request,
    706 	     krb5_storage *response)
    707 {
    708     uint16_t mode;
    709     krb5_error_code ret;
    710     kcm_ccache ccache;
    711     char *name;
    712 
    713     ret = krb5_ret_stringz(request, &name);
    714     if (ret)
    715 	return ret;
    716 
    717     KCM_LOG_REQUEST_NAME(context, client, opcode, name);
    718 
    719     ret = krb5_ret_uint16(request, &mode);
    720     if (ret) {
    721 	free(name);
    722 	return ret;
    723     }
    724 
    725     ret = kcm_ccache_resolve_client(context, client, opcode,
    726 				    name, &ccache);
    727     if (ret) {
    728 	free(name);
    729 	return ret;
    730     }
    731 
    732     ret = kcm_chmod(context, client, ccache, mode);
    733 
    734     free(name);
    735     kcm_release_ccache(context, ccache);
    736 
    737     return ret;
    738 }
    739 
    740 /*
    741  * Protocol extensions for moving ticket acquisition responsibility
    742  * from client to KCM follow.
    743  */
    744 
    745 /*
    746  * Request:
    747  *	NameZ
    748  *	ServerPrincipalPresent
    749  *	ServerPrincipal OPTIONAL
    750  *	Key
    751  *
    752  * Repsonse:
    753  *
    754  */
    755 static krb5_error_code
    756 kcm_op_get_initial_ticket(krb5_context context,
    757 			  kcm_client *client,
    758 			  kcm_operation opcode,
    759 			  krb5_storage *request,
    760 			  krb5_storage *response)
    761 {
    762     krb5_error_code ret;
    763     kcm_ccache ccache;
    764     char *name;
    765     int8_t not_tgt = 0;
    766     krb5_principal server = NULL;
    767     krb5_keyblock key;
    768 
    769     krb5_keyblock_zero(&key);
    770 
    771     ret = krb5_ret_stringz(request, &name);
    772     if (ret)
    773 	return ret;
    774 
    775     KCM_LOG_REQUEST_NAME(context, client, opcode, name);
    776 
    777     ret = krb5_ret_int8(request, &not_tgt);
    778     if (ret) {
    779 	free(name);
    780 	return ret;
    781     }
    782 
    783     if (not_tgt) {
    784 	ret = krb5_ret_principal(request, &server);
    785 	if (ret) {
    786 	    free(name);
    787 	    return ret;
    788 	}
    789     }
    790 
    791     ret = krb5_ret_keyblock(request, &key);
    792     if (ret) {
    793 	free(name);
    794 	if (server != NULL)
    795 	    krb5_free_principal(context, server);
    796 	return ret;
    797     }
    798 
    799     ret = kcm_ccache_resolve_client(context, client, opcode,
    800 				    name, &ccache);
    801     if (ret == 0) {
    802 	HEIMDAL_MUTEX_lock(&ccache->mutex);
    803 
    804 	if (ccache->server != NULL) {
    805 	    krb5_free_principal(context, ccache->server);
    806 	    ccache->server = NULL;
    807 	}
    808 
    809 	krb5_free_keyblock(context, &ccache->key.keyblock);
    810 
    811 	ccache->server = server;
    812 	ccache->key.keyblock = key;
    813     	ccache->flags |= KCM_FLAGS_USE_CACHED_KEY;
    814 
    815 	ret = kcm_ccache_enqueue_default(context, ccache, NULL);
    816 	if (ret) {
    817 	    ccache->server = NULL;
    818 	    krb5_keyblock_zero(&ccache->key.keyblock);
    819 	    ccache->flags &= ~(KCM_FLAGS_USE_CACHED_KEY);
    820 	}
    821 
    822 	HEIMDAL_MUTEX_unlock(&ccache->mutex);
    823     }
    824 
    825     free(name);
    826 
    827     if (ret != 0) {
    828 	krb5_free_principal(context, server);
    829 	krb5_free_keyblock_contents(context, &key);
    830     }
    831 
    832     kcm_release_ccache(context, ccache);
    833 
    834     return ret;
    835 }
    836 
    837 /*
    838  * Request:
    839  *	NameZ
    840  *	ServerPrincipal
    841  *	KDCFlags
    842  *	EncryptionType
    843  *
    844  * Repsonse:
    845  *
    846  */
    847 static krb5_error_code
    848 kcm_op_get_ticket(krb5_context context,
    849 		  kcm_client *client,
    850 		  kcm_operation opcode,
    851 		  krb5_storage *request,
    852 		  krb5_storage *response)
    853 {
    854     krb5_error_code ret;
    855     kcm_ccache ccache;
    856     char *name;
    857     krb5_principal server = NULL;
    858     krb5_ccache_data ccdata;
    859     krb5_creds in, *out;
    860     krb5_kdc_flags flags;
    861 
    862     memset(&in, 0, sizeof(in));
    863 
    864     ret = krb5_ret_stringz(request, &name);
    865     if (ret)
    866 	return ret;
    867 
    868     KCM_LOG_REQUEST_NAME(context, client, opcode, name);
    869 
    870     ret = krb5_ret_uint32(request, &flags.i);
    871     if (ret) {
    872 	free(name);
    873 	return ret;
    874     }
    875 
    876     ret = krb5_ret_int32(request, &in.session.keytype);
    877     if (ret) {
    878 	free(name);
    879 	return ret;
    880     }
    881 
    882     ret = krb5_ret_principal(request, &server);
    883     if (ret) {
    884 	free(name);
    885 	return ret;
    886     }
    887 
    888     ret = kcm_ccache_resolve_client(context, client, opcode,
    889 				    name, &ccache);
    890     if (ret) {
    891 	krb5_free_principal(context, server);
    892 	free(name);
    893 	return ret;
    894     }
    895 
    896     HEIMDAL_MUTEX_lock(&ccache->mutex);
    897 
    898     /* Fake up an internal ccache */
    899     kcm_internal_ccache(context, ccache, &ccdata);
    900 
    901     in.client = ccache->client;
    902     in.server = server;
    903     in.times.endtime = 0;
    904 
    905     /* glue cc layer will store creds */
    906     ret = krb5_get_credentials_with_flags(context, 0, flags,
    907 					  &ccdata, &in, &out);
    908 
    909     HEIMDAL_MUTEX_unlock(&ccache->mutex);
    910 
    911     krb5_free_principal(context, server);
    912 
    913     if (ret == 0)
    914 	krb5_free_cred_contents(context, out);
    915 
    916     kcm_release_ccache(context, ccache);
    917     free(name);
    918 
    919     return ret;
    920 }
    921 
    922 /*
    923  * Request:
    924  *	OldNameZ
    925  *	NewNameZ
    926  *
    927  * Repsonse:
    928  *
    929  */
    930 static krb5_error_code
    931 kcm_op_move_cache(krb5_context context,
    932 		  kcm_client *client,
    933 		  kcm_operation opcode,
    934 		  krb5_storage *request,
    935 		  krb5_storage *response)
    936 {
    937     krb5_error_code ret;
    938     kcm_ccache oldid, newid;
    939     char *oldname, *newname;
    940 
    941     ret = krb5_ret_stringz(request, &oldname);
    942     if (ret)
    943 	return ret;
    944 
    945     KCM_LOG_REQUEST_NAME(context, client, opcode, oldname);
    946 
    947     ret = krb5_ret_stringz(request, &newname);
    948     if (ret) {
    949 	free(oldname);
    950 	return ret;
    951     }
    952 
    953     /* move to ourself is simple, done! */
    954     if (strcmp(oldname, newname) == 0) {
    955 	free(oldname);
    956 	free(newname);
    957 	return 0;
    958     }
    959 
    960     ret = kcm_ccache_resolve_client(context, client, opcode, oldname, &oldid);
    961     if (ret) {
    962 	free(oldname);
    963 	free(newname);
    964 	return ret;
    965     }
    966 
    967     /* Check if new credential cache exists, if not create one. */
    968     ret = kcm_ccache_resolve_client(context, client, opcode, newname, &newid);
    969     if (ret == KRB5_FCC_NOFILE)
    970 	ret = kcm_ccache_new_client(context, client, newname, &newid);
    971     free(newname);
    972 
    973     if (ret) {
    974 	free(oldname);
    975 	kcm_release_ccache(context, oldid);
    976 	return ret;
    977     }
    978 
    979     HEIMDAL_MUTEX_lock(&oldid->mutex);
    980     HEIMDAL_MUTEX_lock(&newid->mutex);
    981 
    982     /* move content */
    983     {
    984 	kcm_ccache_data tmp;
    985 
    986 #define MOVE(n,o,f) { tmp.f = n->f ; n->f = o->f; o->f = tmp.f; }
    987 
    988 	MOVE(newid, oldid, flags);
    989 	MOVE(newid, oldid, client);
    990 	MOVE(newid, oldid, server);
    991 	MOVE(newid, oldid, creds);
    992 	MOVE(newid, oldid, tkt_life);
    993 	MOVE(newid, oldid, renew_life);
    994 	MOVE(newid, oldid, key);
    995 	MOVE(newid, oldid, kdc_offset);
    996 #undef MOVE
    997     }
    998 
    999     HEIMDAL_MUTEX_unlock(&oldid->mutex);
   1000     HEIMDAL_MUTEX_unlock(&newid->mutex);
   1001 
   1002     kcm_release_ccache(context, oldid);
   1003     kcm_release_ccache(context, newid);
   1004 
   1005     ret = kcm_ccache_destroy_client(context, client, oldname);
   1006     if (ret == 0)
   1007 	kcm_drop_default_cache(context, client, oldname);
   1008 
   1009     free(oldname);
   1010 
   1011     return ret;
   1012 }
   1013 
   1014 static krb5_error_code
   1015 kcm_op_get_cache_uuid_list(krb5_context context,
   1016 			   kcm_client *client,
   1017 			   kcm_operation opcode,
   1018 			   krb5_storage *request,
   1019 			   krb5_storage *response)
   1020 {
   1021     KCM_LOG_REQUEST(context, client, opcode);
   1022 
   1023     return kcm_ccache_get_uuids(context, client, opcode, response);
   1024 }
   1025 
   1026 static krb5_error_code
   1027 kcm_op_get_cache_by_uuid(krb5_context context,
   1028 			 kcm_client *client,
   1029 			 kcm_operation opcode,
   1030 			 krb5_storage *request,
   1031 			 krb5_storage *response)
   1032 {
   1033     krb5_error_code ret;
   1034     kcmuuid_t uuid;
   1035     ssize_t sret;
   1036     kcm_ccache cache;
   1037 
   1038     KCM_LOG_REQUEST(context, client, opcode);
   1039 
   1040     sret = krb5_storage_read(request, &uuid, sizeof(uuid));
   1041     if (sret != sizeof(uuid)) {
   1042 	krb5_clear_error_message(context);
   1043 	return KRB5_CC_IO;
   1044     }
   1045 
   1046     ret = kcm_ccache_resolve_by_uuid(context, uuid, &cache);
   1047     if (ret)
   1048 	return ret;
   1049 
   1050     ret = kcm_access(context, client, opcode, cache);
   1051     if (ret)
   1052 	ret = KRB5_FCC_NOFILE;
   1053 
   1054     if (ret == 0)
   1055 	ret = krb5_store_stringz(response, cache->name);
   1056 
   1057     kcm_release_ccache(context, cache);
   1058 
   1059     return ret;
   1060 }
   1061 
   1062 struct kcm_default_cache *default_caches;
   1063 
   1064 static krb5_error_code
   1065 kcm_op_get_default_cache(krb5_context context,
   1066 			 kcm_client *client,
   1067 			 kcm_operation opcode,
   1068 			 krb5_storage *request,
   1069 			 krb5_storage *response)
   1070 {
   1071     struct kcm_default_cache *c;
   1072     krb5_error_code ret;
   1073     const char *name = NULL;
   1074     char *n = NULL;
   1075     int aret;
   1076 
   1077     KCM_LOG_REQUEST(context, client, opcode);
   1078 
   1079     for (c = default_caches; c != NULL; c = c->next) {
   1080 	if (kcm_is_same_session(client, c->uid, c->session)) {
   1081 	    name = c->name;
   1082 	    break;
   1083 	}
   1084     }
   1085     if (name == NULL)
   1086 	name = n = kcm_ccache_first_name(client);
   1087 
   1088     if (name == NULL) {
   1089 	aret = asprintf(&n, "%d", (int)client->uid);
   1090 	if (aret != -1)
   1091 	    name = n;
   1092     }
   1093     if (name == NULL)
   1094 	return ENOMEM;
   1095     ret = krb5_store_stringz(response, name);
   1096     if (n)
   1097 	free(n);
   1098     return ret;
   1099 }
   1100 
   1101 static void
   1102 kcm_drop_default_cache(krb5_context context, kcm_client *client, char *name)
   1103 {
   1104     struct kcm_default_cache **c;
   1105 
   1106     for (c = &default_caches; *c != NULL; c = &(*c)->next) {
   1107 	if (!kcm_is_same_session(client, (*c)->uid, (*c)->session))
   1108 	    continue;
   1109 	if (strcmp((*c)->name, name) == 0) {
   1110 	    struct kcm_default_cache *h = *c;
   1111 	    *c = (*c)->next;
   1112 	    free(h->name);
   1113 	    free(h);
   1114 	    break;
   1115 	}
   1116     }
   1117 }
   1118 
   1119 static krb5_error_code
   1120 kcm_op_set_default_cache(krb5_context context,
   1121 			 kcm_client *client,
   1122 			 kcm_operation opcode,
   1123 			 krb5_storage *request,
   1124 			 krb5_storage *response)
   1125 {
   1126     struct kcm_default_cache *c;
   1127     krb5_error_code ret;
   1128     char *name;
   1129 
   1130     ret = krb5_ret_stringz(request, &name);
   1131     if (ret)
   1132 	return ret;
   1133 
   1134     KCM_LOG_REQUEST_NAME(context, client, opcode, name);
   1135 
   1136     for (c = default_caches; c != NULL; c = c->next) {
   1137 	if (kcm_is_same_session(client, c->uid, c->session))
   1138 	    break;
   1139     }
   1140     if (c == NULL) {
   1141 	c = malloc(sizeof(*c));
   1142 	if (c == NULL) {
   1143             free(name);
   1144 	    return ENOMEM;
   1145         }
   1146 	c->session = client->session;
   1147 	c->uid = client->uid;
   1148 	c->name = name;
   1149 
   1150 	c->next = default_caches;
   1151 	default_caches = c;
   1152     } else {
   1153 	free(c->name);
   1154 	c->name = name;
   1155     }
   1156 
   1157     return 0;
   1158 }
   1159 
   1160 static krb5_error_code
   1161 kcm_op_get_kdc_offset(krb5_context context,
   1162 		      kcm_client *client,
   1163 		      kcm_operation opcode,
   1164 		      krb5_storage *request,
   1165 		      krb5_storage *response)
   1166 {
   1167     krb5_error_code ret;
   1168     kcm_ccache ccache;
   1169     char *name;
   1170 
   1171     ret = krb5_ret_stringz(request, &name);
   1172     if (ret)
   1173 	return ret;
   1174 
   1175     KCM_LOG_REQUEST_NAME(context, client, opcode, name);
   1176 
   1177     ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache);
   1178     free(name);
   1179     if (ret)
   1180 	return ret;
   1181 
   1182     HEIMDAL_MUTEX_lock(&ccache->mutex);
   1183     ret = krb5_store_int32(response, ccache->kdc_offset);
   1184     HEIMDAL_MUTEX_unlock(&ccache->mutex);
   1185 
   1186     kcm_release_ccache(context, ccache);
   1187 
   1188     return ret;
   1189 }
   1190 
   1191 static krb5_error_code
   1192 kcm_op_set_kdc_offset(krb5_context context,
   1193 		      kcm_client *client,
   1194 		      kcm_operation opcode,
   1195 		      krb5_storage *request,
   1196 		      krb5_storage *response)
   1197 {
   1198     krb5_error_code ret;
   1199     kcm_ccache ccache;
   1200     int32_t offset;
   1201     char *name;
   1202 
   1203     ret = krb5_ret_stringz(request, &name);
   1204     if (ret)
   1205 	return ret;
   1206 
   1207     KCM_LOG_REQUEST_NAME(context, client, opcode, name);
   1208 
   1209     ret = krb5_ret_int32(request, &offset);
   1210     if (ret) {
   1211 	free(name);
   1212 	return ret;
   1213     }
   1214 
   1215     ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache);
   1216     free(name);
   1217     if (ret)
   1218 	return ret;
   1219 
   1220     HEIMDAL_MUTEX_lock(&ccache->mutex);
   1221     ccache->kdc_offset = offset;
   1222     HEIMDAL_MUTEX_unlock(&ccache->mutex);
   1223 
   1224     kcm_release_ccache(context, ccache);
   1225 
   1226     return ret;
   1227 }
   1228 
   1229 struct kcm_ntlm_cred {
   1230     kcmuuid_t uuid;
   1231     char *user;
   1232     char *domain;
   1233     krb5_data nthash;
   1234     uid_t uid;
   1235     pid_t session;
   1236     struct kcm_ntlm_cred *next;
   1237 };
   1238 
   1239 static struct kcm_ntlm_cred *ntlm_head;
   1240 
   1241 static void
   1242 free_cred(struct kcm_ntlm_cred *cred)
   1243 {
   1244     free(cred->user);
   1245     free(cred->domain);
   1246     krb5_data_free(&cred->nthash);
   1247     free(cred);
   1248 }
   1249 
   1250 
   1251 /*
   1252  * name
   1253  * domain
   1254  * ntlm hash
   1255  *
   1256  * Reply:
   1257  *   uuid
   1258  */
   1259 
   1260 static struct kcm_ntlm_cred *
   1261 find_ntlm_cred(const char *user, const char *domain, kcm_client *client)
   1262 {
   1263     struct kcm_ntlm_cred *c;
   1264 
   1265     for (c = ntlm_head; c != NULL; c = c->next)
   1266 	if ((user[0] == '\0' || strcmp(user, c->user) == 0) &&
   1267 	    (domain == NULL || strcmp(domain, c->domain) == 0) &&
   1268 	    kcm_is_same_session(client, c->uid, c->session))
   1269 	    return c;
   1270 
   1271     return NULL;
   1272 }
   1273 
   1274 static krb5_error_code
   1275 kcm_op_add_ntlm_cred(krb5_context context,
   1276 		     kcm_client *client,
   1277 		     kcm_operation opcode,
   1278 		     krb5_storage *request,
   1279 		     krb5_storage *response)
   1280 {
   1281     struct kcm_ntlm_cred *cred, *c;
   1282     krb5_error_code ret;
   1283 
   1284     cred = calloc(1, sizeof(*cred));
   1285     if (cred == NULL)
   1286 	return ENOMEM;
   1287 
   1288     RAND_bytes(cred->uuid, sizeof(cred->uuid));
   1289 
   1290     ret = krb5_ret_stringz(request, &cred->user);
   1291     if (ret)
   1292 	goto error;
   1293 
   1294     ret = krb5_ret_stringz(request, &cred->domain);
   1295     if (ret)
   1296 	goto error;
   1297 
   1298     ret = krb5_ret_data(request, &cred->nthash);
   1299     if (ret)
   1300 	goto error;
   1301 
   1302     /* search for dups */
   1303     c = find_ntlm_cred(cred->user, cred->domain, client);
   1304     if (c) {
   1305 	krb5_data hash = c->nthash;
   1306 	c->nthash = cred->nthash;
   1307 	cred->nthash = hash;
   1308 	free_cred(cred);
   1309 	cred = c;
   1310     } else {
   1311 	cred->next = ntlm_head;
   1312 	ntlm_head = cred;
   1313     }
   1314 
   1315     cred->uid = client->uid;
   1316     cred->session = client->session;
   1317 
   1318     /* write response */
   1319     (void)krb5_storage_write(response, &cred->uuid, sizeof(cred->uuid));
   1320 
   1321     return 0;
   1322 
   1323  error:
   1324     free_cred(cred);
   1325 
   1326     return ret;
   1327 }
   1328 
   1329 /*
   1330  * { "HAVE_NTLM_CRED",		NULL },
   1331  *
   1332  * input:
   1333  *  name
   1334  *  domain
   1335  */
   1336 
   1337 static krb5_error_code
   1338 kcm_op_have_ntlm_cred(krb5_context context,
   1339 		     kcm_client *client,
   1340 		     kcm_operation opcode,
   1341 		     krb5_storage *request,
   1342 		     krb5_storage *response)
   1343 {
   1344     struct kcm_ntlm_cred *c;
   1345     char *user = NULL, *domain = NULL;
   1346     krb5_error_code ret;
   1347 
   1348     ret = krb5_ret_stringz(request, &user);
   1349     if (ret)
   1350 	goto error;
   1351 
   1352     ret = krb5_ret_stringz(request, &domain);
   1353     if (ret)
   1354 	goto error;
   1355 
   1356     if (domain[0] == '\0') {
   1357 	free(domain);
   1358 	domain = NULL;
   1359     }
   1360 
   1361     c = find_ntlm_cred(user, domain, client);
   1362     if (c == NULL)
   1363 	ret = ENOENT;
   1364 
   1365  error:
   1366     free(user);
   1367     if (domain)
   1368 	free(domain);
   1369 
   1370     return ret;
   1371 }
   1372 
   1373 /*
   1374  * { "DEL_NTLM_CRED",		NULL },
   1375  *
   1376  * input:
   1377  *  name
   1378  *  domain
   1379  */
   1380 
   1381 static krb5_error_code
   1382 kcm_op_del_ntlm_cred(krb5_context context,
   1383 		     kcm_client *client,
   1384 		     kcm_operation opcode,
   1385 		     krb5_storage *request,
   1386 		     krb5_storage *response)
   1387 {
   1388     struct kcm_ntlm_cred **cp, *c;
   1389     char *user = NULL, *domain = NULL;
   1390     krb5_error_code ret;
   1391 
   1392     ret = krb5_ret_stringz(request, &user);
   1393     if (ret)
   1394 	goto error;
   1395 
   1396     ret = krb5_ret_stringz(request, &domain);
   1397     if (ret)
   1398 	goto error;
   1399 
   1400     for (cp = &ntlm_head; *cp != NULL; cp = &(*cp)->next) {
   1401 	if (strcmp(user, (*cp)->user) == 0 && strcmp(domain, (*cp)->domain) == 0 &&
   1402 	    kcm_is_same_session(client, (*cp)->uid, (*cp)->session))
   1403 	{
   1404 	    c = *cp;
   1405 	    *cp = c->next;
   1406 
   1407 	    free_cred(c);
   1408 	    break;
   1409 	}
   1410     }
   1411 
   1412  error:
   1413     free(user);
   1414     free(domain);
   1415 
   1416     return ret;
   1417 }
   1418 
   1419 /*
   1420  * { "DO_NTLM_AUTH",		NULL },
   1421  *
   1422  * input:
   1423  *  name:string
   1424  *  domain:string
   1425  *  type2:data
   1426  *
   1427  * reply:
   1428  *  type3:data
   1429  *  flags:int32
   1430  *  session-key:data
   1431  */
   1432 
   1433 #define NTLM_FLAG_SESSIONKEY 1
   1434 #define NTLM_FLAG_NTLM2_SESSION 2
   1435 #define NTLM_FLAG_KEYEX 4
   1436 
   1437 static krb5_error_code
   1438 kcm_op_do_ntlm(krb5_context context,
   1439 	       kcm_client *client,
   1440 	       kcm_operation opcode,
   1441 	       krb5_storage *request,
   1442 	       krb5_storage *response)
   1443 {
   1444     struct kcm_ntlm_cred *c;
   1445     struct ntlm_type2 type2;
   1446     struct ntlm_type3 type3;
   1447     char *user = NULL, *domain = NULL;
   1448     struct ntlm_buf ndata, sessionkey;
   1449     krb5_data data;
   1450     krb5_error_code ret;
   1451     uint32_t flags = 0;
   1452 
   1453     memset(&type2, 0, sizeof(type2));
   1454     memset(&type3, 0, sizeof(type3));
   1455     sessionkey.data = NULL;
   1456     sessionkey.length = 0;
   1457 
   1458     ret = krb5_ret_stringz(request, &user);
   1459     if (ret)
   1460 	goto error;
   1461 
   1462     ret = krb5_ret_stringz(request, &domain);
   1463     if (ret)
   1464 	goto error;
   1465 
   1466     if (domain[0] == '\0') {
   1467 	free(domain);
   1468 	domain = NULL;
   1469     }
   1470 
   1471     c = find_ntlm_cred(user, domain, client);
   1472     if (c == NULL) {
   1473 	ret = EINVAL;
   1474 	goto error;
   1475     }
   1476 
   1477     ret = krb5_ret_data(request, &data);
   1478     if (ret)
   1479 	goto error;
   1480 
   1481     ndata.data = data.data;
   1482     ndata.length = data.length;
   1483 
   1484     ret = heim_ntlm_decode_type2(&ndata, &type2);
   1485     krb5_data_free(&data);
   1486     if (ret)
   1487 	goto error;
   1488 
   1489     if (domain && strcmp(domain, type2.targetname) == 0) {
   1490 	ret = EINVAL;
   1491 	goto error;
   1492     }
   1493 
   1494     type3.username = c->user;
   1495     type3.flags = type2.flags;
   1496     type3.targetname = type2.targetname;
   1497     type3.ws = rk_UNCONST("workstation");
   1498 
   1499     /*
   1500      * NTLM Version 1 if no targetinfo buffer.
   1501      */
   1502 
   1503     if (1 || type2.targetinfo.length == 0) {
   1504 	struct ntlm_buf tmpsesskey;
   1505 
   1506 	if (type2.flags & NTLM_NEG_NTLM2_SESSION) {
   1507 	    unsigned char nonce[8];
   1508 
   1509 	    if (RAND_bytes(nonce, sizeof(nonce)) != 1) {
   1510 		ret = EINVAL;
   1511 		goto error;
   1512 	    }
   1513 
   1514 	    ret = heim_ntlm_calculate_ntlm2_sess(nonce,
   1515 						 type2.challenge,
   1516 						 c->nthash.data,
   1517 						 &type3.lm,
   1518 						 &type3.ntlm);
   1519 	} else {
   1520 	    ret = heim_ntlm_calculate_ntlm1(c->nthash.data,
   1521 					    c->nthash.length,
   1522 					    type2.challenge,
   1523 					    &type3.ntlm);
   1524 
   1525 	}
   1526 	if (ret)
   1527 	    goto error;
   1528 
   1529 	ret = heim_ntlm_build_ntlm1_master(c->nthash.data,
   1530 					   c->nthash.length,
   1531 					   &tmpsesskey,
   1532 					   &type3.sessionkey);
   1533 	if (ret) {
   1534 	    if (type3.lm.data)
   1535 		free(type3.lm.data);
   1536 	    if (type3.ntlm.data)
   1537 		free(type3.ntlm.data);
   1538 	    goto error;
   1539 	}
   1540 
   1541 	free(tmpsesskey.data);
   1542 	if (ret) {
   1543 	    if (type3.lm.data)
   1544 		free(type3.lm.data);
   1545 	    if (type3.ntlm.data)
   1546 		free(type3.ntlm.data);
   1547 	    goto error;
   1548 	}
   1549 	flags |= NTLM_FLAG_SESSIONKEY;
   1550 #if 0
   1551     } else {
   1552 	struct ntlm_buf sessionkey;
   1553 	unsigned char ntlmv2[16];
   1554 	struct ntlm_targetinfo ti;
   1555 
   1556 	/* verify infotarget */
   1557 
   1558 	ret = heim_ntlm_decode_targetinfo(&type2.targetinfo, 1, &ti);
   1559 	if(ret) {
   1560 	    _gss_ntlm_delete_sec_context(minor_status,
   1561 					 context_handle, NULL);
   1562 	    *minor_status = ret;
   1563 	    return GSS_S_FAILURE;
   1564 	}
   1565 
   1566 	if (ti.domainname && strcmp(ti.domainname, name->domain) != 0) {
   1567 	    _gss_ntlm_delete_sec_context(minor_status,
   1568 					 context_handle, NULL);
   1569 	    *minor_status = EINVAL;
   1570 	    return GSS_S_FAILURE;
   1571 	}
   1572 
   1573 	ret = heim_ntlm_calculate_ntlm2(ctx->client->key.data,
   1574 					ctx->client->key.length,
   1575 					type3.username,
   1576 					name->domain,
   1577 					type2.challenge,
   1578 					&type2.targetinfo,
   1579 					ntlmv2,
   1580 					&type3.ntlm);
   1581 	if (ret) {
   1582 	    _gss_ntlm_delete_sec_context(minor_status,
   1583 					 context_handle, NULL);
   1584 	    *minor_status = ret;
   1585 	    return GSS_S_FAILURE;
   1586 	}
   1587 
   1588 	ret = heim_ntlm_build_ntlm1_master(ntlmv2, sizeof(ntlmv2),
   1589 					   &sessionkey,
   1590 					   &type3.sessionkey);
   1591 	memset(ntlmv2, 0, sizeof(ntlmv2));
   1592 	if (ret) {
   1593 	    _gss_ntlm_delete_sec_context(minor_status,
   1594 					 context_handle, NULL);
   1595 	    *minor_status = ret;
   1596 	    return GSS_S_FAILURE;
   1597 	}
   1598 
   1599 	flags |= NTLM_FLAG_NTLM2_SESSION |
   1600 	         NTLM_FLAG_SESSION;
   1601 
   1602 	if (type3.flags & NTLM_NEG_KEYEX)
   1603 	    flags |= NTLM_FLAG_KEYEX;
   1604 
   1605 	ret = krb5_data_copy(&ctx->sessionkey,
   1606 			     sessionkey.data, sessionkey.length);
   1607 	free(sessionkey.data);
   1608 	if (ret) {
   1609 	    _gss_ntlm_delete_sec_context(minor_status,
   1610 					 context_handle, NULL);
   1611 	    *minor_status = ret;
   1612 	    return GSS_S_FAILURE;
   1613 	}
   1614 #endif
   1615     }
   1616 
   1617 #if 0
   1618     if (flags & NTLM_FLAG_NTLM2_SESSION) {
   1619 	_gss_ntlm_set_key(&ctx->u.v2.send, 0, (ctx->flags & NTLM_NEG_KEYEX),
   1620 			  ctx->sessionkey.data,
   1621 			  ctx->sessionkey.length);
   1622 	_gss_ntlm_set_key(&ctx->u.v2.recv, 1, (ctx->flags & NTLM_NEG_KEYEX),
   1623 			  ctx->sessionkey.data,
   1624 			  ctx->sessionkey.length);
   1625     } else {
   1626 	flags |= NTLM_FLAG_SESSION;
   1627 	RC4_set_key(&ctx->u.v1.crypto_recv.key,
   1628 		    ctx->sessionkey.length,
   1629 		    ctx->sessionkey.data);
   1630 	RC4_set_key(&ctx->u.v1.crypto_send.key,
   1631 		    ctx->sessionkey.length,
   1632 		    ctx->sessionkey.data);
   1633     }
   1634 #endif
   1635 
   1636     ret = heim_ntlm_encode_type3(&type3, &ndata, NULL);
   1637     if (ret)
   1638 	goto error;
   1639 
   1640     data.data = ndata.data;
   1641     data.length = ndata.length;
   1642     ret = krb5_store_data(response, data);
   1643     heim_ntlm_free_buf(&ndata);
   1644     if (ret) goto error;
   1645 
   1646     ret = krb5_store_int32(response, flags);
   1647     if (ret) goto error;
   1648 
   1649     data.data = sessionkey.data;
   1650     data.length = sessionkey.length;
   1651 
   1652     ret = krb5_store_data(response, data);
   1653     if (ret) goto error;
   1654 
   1655  error:
   1656     free(type3.username);
   1657     heim_ntlm_free_type2(&type2);
   1658     free(user);
   1659     if (domain)
   1660 	free(domain);
   1661 
   1662     return ret;
   1663 }
   1664 
   1665 
   1666 /*
   1667  * { "GET_NTLM_UUID_LIST",	NULL }
   1668  *
   1669  * reply:
   1670  *   1 user domain
   1671  *   0 [ end of list ]
   1672  */
   1673 
   1674 static krb5_error_code
   1675 kcm_op_get_ntlm_user_list(krb5_context context,
   1676 			  kcm_client *client,
   1677 			  kcm_operation opcode,
   1678 			  krb5_storage *request,
   1679 			  krb5_storage *response)
   1680 {
   1681     struct kcm_ntlm_cred *c;
   1682     krb5_error_code ret;
   1683 
   1684     for (c = ntlm_head; c != NULL; c = c->next) {
   1685 	if (!kcm_is_same_session(client, c->uid, c->session))
   1686 	    continue;
   1687 
   1688 	ret = krb5_store_uint32(response, 1);
   1689 	if (ret)
   1690 	    return ret;
   1691 	ret = krb5_store_stringz(response, c->user);
   1692 	if (ret)
   1693 	    return ret;
   1694 	ret = krb5_store_stringz(response, c->domain);
   1695 	if (ret)
   1696 	    return ret;
   1697     }
   1698     return krb5_store_uint32(response, 0);
   1699 }
   1700 
   1701 /*
   1702  *
   1703  */
   1704 
   1705 static struct kcm_op kcm_ops[] = {
   1706     { "NOOP", 			kcm_op_noop },
   1707     { "GET_NAME",		kcm_op_get_name },
   1708     { "RESOLVE",		kcm_op_noop },
   1709     { "GEN_NEW", 		kcm_op_gen_new },
   1710     { "INITIALIZE",		kcm_op_initialize },
   1711     { "DESTROY",		kcm_op_destroy },
   1712     { "STORE",			kcm_op_store },
   1713     { "RETRIEVE",		kcm_op_retrieve },
   1714     { "GET_PRINCIPAL",		kcm_op_get_principal },
   1715     { "GET_CRED_UUID_LIST",	kcm_op_get_cred_uuid_list },
   1716     { "GET_CRED_BY_UUID",	kcm_op_get_cred_by_uuid },
   1717     { "REMOVE_CRED",		kcm_op_remove_cred },
   1718     { "SET_FLAGS",		kcm_op_set_flags },
   1719     { "CHOWN",			kcm_op_chown },
   1720     { "CHMOD",			kcm_op_chmod },
   1721     { "GET_INITIAL_TICKET",	kcm_op_get_initial_ticket },
   1722     { "GET_TICKET",		kcm_op_get_ticket },
   1723     { "MOVE_CACHE",		kcm_op_move_cache },
   1724     { "GET_CACHE_UUID_LIST",	kcm_op_get_cache_uuid_list },
   1725     { "GET_CACHE_BY_UUID",	kcm_op_get_cache_by_uuid },
   1726     { "GET_DEFAULT_CACHE",      kcm_op_get_default_cache },
   1727     { "SET_DEFAULT_CACHE",      kcm_op_set_default_cache },
   1728     { "GET_KDC_OFFSET",      	kcm_op_get_kdc_offset },
   1729     { "SET_KDC_OFFSET",      	kcm_op_set_kdc_offset },
   1730     { "ADD_NTLM_CRED",		kcm_op_add_ntlm_cred },
   1731     { "HAVE_USER_CRED",		kcm_op_have_ntlm_cred },
   1732     { "DEL_NTLM_CRED",		kcm_op_del_ntlm_cred },
   1733     { "DO_NTLM_AUTH",		kcm_op_do_ntlm },
   1734     { "GET_NTLM_USER_LIST",	kcm_op_get_ntlm_user_list }
   1735 };
   1736 
   1737 
   1738 const char *
   1739 kcm_op2string(kcm_operation opcode)
   1740 {
   1741     if (opcode >= sizeof(kcm_ops)/sizeof(kcm_ops[0]))
   1742 	return "Unknown operation";
   1743 
   1744     return kcm_ops[opcode].name;
   1745 }
   1746 
   1747 krb5_error_code
   1748 kcm_dispatch(krb5_context context,
   1749 	     kcm_client *client,
   1750 	     krb5_data *req_data,
   1751 	     krb5_data *resp_data)
   1752 {
   1753     krb5_error_code ret;
   1754     kcm_method method;
   1755     krb5_storage *req_sp = NULL;
   1756     krb5_storage *resp_sp = NULL;
   1757     uint16_t opcode;
   1758 
   1759     resp_sp = krb5_storage_emem();
   1760     if (resp_sp == NULL) {
   1761 	return ENOMEM;
   1762     }
   1763 
   1764     if (client->pid == -1) {
   1765 	kcm_log(0, "Client had invalid process number");
   1766 	ret = KRB5_FCC_INTERNAL;
   1767 	goto out;
   1768     }
   1769 
   1770     req_sp = krb5_storage_from_data(req_data);
   1771     if (req_sp == NULL) {
   1772 	kcm_log(0, "Process %d: failed to initialize storage from data",
   1773 		client->pid);
   1774 	ret = KRB5_CC_IO;
   1775 	goto out;
   1776     }
   1777 
   1778     ret = krb5_ret_uint16(req_sp, &opcode);
   1779     if (ret) {
   1780 	kcm_log(0, "Process %d: didn't send a message", client->pid);
   1781 	goto out;
   1782     }
   1783 
   1784     if (opcode >= sizeof(kcm_ops)/sizeof(kcm_ops[0])) {
   1785 	kcm_log(0, "Process %d: invalid operation code %d",
   1786 		client->pid, opcode);
   1787 	ret = KRB5_FCC_INTERNAL;
   1788 	goto out;
   1789     }
   1790     method = kcm_ops[opcode].method;
   1791     if (method == NULL) {
   1792 	kcm_log(0, "Process %d: operation code %s not implemented",
   1793 		client->pid, kcm_op2string(opcode));
   1794 	ret = KRB5_FCC_INTERNAL;
   1795 	goto out;
   1796     }
   1797 
   1798     /* seek past place for status code */
   1799     krb5_storage_seek(resp_sp, 4, SEEK_SET);
   1800 
   1801     ret = (*method)(context, client, opcode, req_sp, resp_sp);
   1802 
   1803 out:
   1804     if (req_sp != NULL) {
   1805 	krb5_storage_free(req_sp);
   1806     }
   1807 
   1808     krb5_storage_seek(resp_sp, 0, SEEK_SET);
   1809     krb5_store_int32(resp_sp, ret);
   1810 
   1811     ret = krb5_storage_to_data(resp_sp, resp_data);
   1812     krb5_storage_free(resp_sp);
   1813 
   1814     return ret;
   1815 }
   1816 
   1817