1 /*	$NetBSD: protocol.c,v 1.1.1.1 2011/04/13 18:14:36 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 "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 0;
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(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 
1076     KCM_LOG_REQUEST(context, client, opcode);
1077 
1078     for (c = default_caches; c != NULL; c = c->next) {
1079 	if (kcm_is_same_session(client, c->uid, c->session)) {
1080 	    name = c->name;
1081 	    break;
1082 	}
1083     }
1084     if (name == NULL)
1085 	name = n = kcm_ccache_first_name(client);
1086 
1087     if (name == NULL) {
1088 	asprintf(&n, "%d", (int)client->uid);
1089 	name = n;
1090     }
1091     if (name == NULL)
1092 	return ENOMEM;
1093     ret = krb5_store_stringz(response, name);
1094     if (n)
1095 	free(n);
1096     return ret;
1097 }
1098 
1099 static void
1100 kcm_drop_default_cache(krb5_context context, kcm_client *client, char *name)
1101 {
1102     struct kcm_default_cache **c;
1103 
1104     for (c = &default_caches; *c != NULL; c = &(*c)->next) {
1105 	if (!kcm_is_same_session(client, (*c)->uid, (*c)->session))
1106 	    continue;
1107 	if (strcmp((*c)->name, name) == 0) {
1108 	    struct kcm_default_cache *h = *c;
1109 	    *c = (*c)->next;
1110 	    free(h->name);
1111 	    free(h);
1112 	    break;
1113 	}
1114     }
1115 }
1116 
1117 static krb5_error_code
1118 kcm_op_set_default_cache(krb5_context context,
1119 			 kcm_client *client,
1120 			 kcm_operation opcode,
1121 			 krb5_storage *request,
1122 			 krb5_storage *response)
1123 {
1124     struct kcm_default_cache *c;
1125     krb5_error_code ret;
1126     char *name;
1127 
1128     ret = krb5_ret_stringz(request, &name);
1129     if (ret)
1130 	return ret;
1131 
1132     KCM_LOG_REQUEST_NAME(context, client, opcode, name);
1133 
1134     for (c = default_caches; c != NULL; c = c->next) {
1135 	if (kcm_is_same_session(client, c->uid, c->session))
1136 	    break;
1137     }
1138     if (c == NULL) {
1139 	c = malloc(sizeof(*c));
1140 	if (c == NULL)
1141 	    return ENOMEM;
1142 	c->session = client->session;
1143 	c->uid = client->uid;
1144 	c->name = strdup(name);
1145 
1146 	c->next = default_caches;
1147 	default_caches = c;
1148     } else {
1149 	free(c->name);
1150 	c->name = strdup(name);
1151     }
1152 
1153     return 0;
1154 }
1155 
1156 static krb5_error_code
1157 kcm_op_get_kdc_offset(krb5_context context,
1158 		      kcm_client *client,
1159 		      kcm_operation opcode,
1160 		      krb5_storage *request,
1161 		      krb5_storage *response)
1162 {
1163     krb5_error_code ret;
1164     kcm_ccache ccache;
1165     char *name;
1166 
1167     ret = krb5_ret_stringz(request, &name);
1168     if (ret)
1169 	return ret;
1170 
1171     KCM_LOG_REQUEST_NAME(context, client, opcode, name);
1172 
1173     ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache);
1174     free(name);
1175     if (ret)
1176 	return ret;
1177 
1178     HEIMDAL_MUTEX_lock(&ccache->mutex);
1179     ret = krb5_store_int32(response, ccache->kdc_offset);
1180     HEIMDAL_MUTEX_unlock(&ccache->mutex);
1181 
1182     kcm_release_ccache(context, ccache);
1183 
1184     return ret;
1185 }
1186 
1187 static krb5_error_code
1188 kcm_op_set_kdc_offset(krb5_context context,
1189 		      kcm_client *client,
1190 		      kcm_operation opcode,
1191 		      krb5_storage *request,
1192 		      krb5_storage *response)
1193 {
1194     krb5_error_code ret;
1195     kcm_ccache ccache;
1196     int32_t offset;
1197     char *name;
1198 
1199     ret = krb5_ret_stringz(request, &name);
1200     if (ret)
1201 	return ret;
1202 
1203     KCM_LOG_REQUEST_NAME(context, client, opcode, name);
1204 
1205     ret = krb5_ret_int32(request, &offset);
1206     if (ret) {
1207 	free(name);
1208 	return ret;
1209     }
1210 
1211     ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache);
1212     free(name);
1213     if (ret)
1214 	return ret;
1215 
1216     HEIMDAL_MUTEX_lock(&ccache->mutex);
1217     ccache->kdc_offset = offset;
1218     HEIMDAL_MUTEX_unlock(&ccache->mutex);
1219 
1220     kcm_release_ccache(context, ccache);
1221 
1222     return ret;
1223 }
1224 
1225 struct kcm_ntlm_cred {
1226     kcmuuid_t uuid;
1227     char *user;
1228     char *domain;
1229     krb5_data nthash;
1230     uid_t uid;
1231     pid_t session;
1232     struct kcm_ntlm_cred *next;
1233 };
1234 
1235 static struct kcm_ntlm_cred *ntlm_head;
1236 
1237 static void
1238 free_cred(struct kcm_ntlm_cred *cred)
1239 {
1240     free(cred->user);
1241     free(cred->domain);
1242     krb5_data_free(&cred->nthash);
1243     free(cred);
1244 }
1245 
1246 
1247 /*
1248  * name
1249  * domain
1250  * ntlm hash
1251  *
1252  * Reply:
1253  *   uuid
1254  */
1255 
1256 static struct kcm_ntlm_cred *
1257 find_ntlm_cred(const char *user, const char *domain, kcm_client *client)
1258 {
1259     struct kcm_ntlm_cred *c;
1260 
1261     for (c = ntlm_head; c != NULL; c = c->next)
1262 	if ((user[0] == '\0' || strcmp(user, c->user) == 0) &&
1263 	    (domain == NULL || strcmp(domain, c->domain) == 0) &&
1264 	    kcm_is_same_session(client, c->uid, c->session))
1265 	    return c;
1266 
1267     return NULL;
1268 }
1269 
1270 static krb5_error_code
1271 kcm_op_add_ntlm_cred(krb5_context context,
1272 		     kcm_client *client,
1273 		     kcm_operation opcode,
1274 		     krb5_storage *request,
1275 		     krb5_storage *response)
1276 {
1277     struct kcm_ntlm_cred *cred, *c;
1278     krb5_error_code ret;
1279 
1280     cred = calloc(1, sizeof(*cred));
1281     if (cred == NULL)
1282 	return ENOMEM;
1283 
1284     RAND_bytes(cred->uuid, sizeof(cred->uuid));
1285 
1286     ret = krb5_ret_stringz(request, &cred->user);
1287     if (ret)
1288 	goto error;
1289 
1290     ret = krb5_ret_stringz(request, &cred->domain);
1291     if (ret)
1292 	goto error;
1293 
1294     ret = krb5_ret_data(request, &cred->nthash);
1295     if (ret)
1296 	goto error;
1297 
1298     /* search for dups */
1299     c = find_ntlm_cred(cred->user, cred->domain, client);
1300     if (c) {
1301 	krb5_data hash = c->nthash;
1302 	c->nthash = cred->nthash;
1303 	cred->nthash = hash;
1304 	free_cred(cred);
1305 	cred = c;
1306     } else {
1307 	cred->next = ntlm_head;
1308 	ntlm_head = cred;
1309     }
1310 
1311     cred->uid = client->uid;
1312     cred->session = client->session;
1313 
1314     /* write response */
1315     (void)krb5_storage_write(response, &cred->uuid, sizeof(cred->uuid));
1316 
1317     return 0;
1318 
1319  error:
1320     free_cred(cred);
1321 
1322     return ret;
1323 }
1324 
1325 /*
1326  * { "HAVE_NTLM_CRED",		NULL },
1327  *
1328  * input:
1329  *  name
1330  *  domain
1331  */
1332 
1333 static krb5_error_code
1334 kcm_op_have_ntlm_cred(krb5_context context,
1335 		     kcm_client *client,
1336 		     kcm_operation opcode,
1337 		     krb5_storage *request,
1338 		     krb5_storage *response)
1339 {
1340     struct kcm_ntlm_cred *c;
1341     char *user = NULL, *domain = NULL;
1342     krb5_error_code ret;
1343 
1344     ret = krb5_ret_stringz(request, &user);
1345     if (ret)
1346 	goto error;
1347 
1348     ret = krb5_ret_stringz(request, &domain);
1349     if (ret)
1350 	goto error;
1351 
1352     if (domain[0] == '\0') {
1353 	free(domain);
1354 	domain = NULL;
1355     }
1356 
1357     c = find_ntlm_cred(user, domain, client);
1358     if (c == NULL)
1359 	ret = ENOENT;
1360 
1361  error:
1362     free(user);
1363     if (domain)
1364 	free(domain);
1365 
1366     return ret;
1367 }
1368 
1369 /*
1370  * { "DEL_NTLM_CRED",		NULL },
1371  *
1372  * input:
1373  *  name
1374  *  domain
1375  */
1376 
1377 static krb5_error_code
1378 kcm_op_del_ntlm_cred(krb5_context context,
1379 		     kcm_client *client,
1380 		     kcm_operation opcode,
1381 		     krb5_storage *request,
1382 		     krb5_storage *response)
1383 {
1384     struct kcm_ntlm_cred **cp, *c;
1385     char *user = NULL, *domain = NULL;
1386     krb5_error_code ret;
1387 
1388     ret = krb5_ret_stringz(request, &user);
1389     if (ret)
1390 	goto error;
1391 
1392     ret = krb5_ret_stringz(request, &domain);
1393     if (ret)
1394 	goto error;
1395 
1396     for (cp = &ntlm_head; *cp != NULL; cp = &(*cp)->next) {
1397 	if (strcmp(user, (*cp)->user) == 0 && strcmp(domain, (*cp)->domain) == 0 &&
1398 	    kcm_is_same_session(client, (*cp)->uid, (*cp)->session))
1399 	{
1400 	    c = *cp;
1401 	    *cp = c->next;
1402 
1403 	    free_cred(c);
1404 	    break;
1405 	}
1406     }
1407 
1408  error:
1409     free(user);
1410     free(domain);
1411 
1412     return ret;
1413 }
1414 
1415 /*
1416  * { "DO_NTLM_AUTH",		NULL },
1417  *
1418  * input:
1419  *  name:string
1420  *  domain:string
1421  *  type2:data
1422  *
1423  * reply:
1424  *  type3:data
1425  *  flags:int32
1426  *  session-key:data
1427  */
1428 
1429 #define NTLM_FLAG_SESSIONKEY 1
1430 #define NTLM_FLAG_NTLM2_SESSION 2
1431 #define NTLM_FLAG_KEYEX 4
1432 
1433 static krb5_error_code
1434 kcm_op_do_ntlm(krb5_context context,
1435 	       kcm_client *client,
1436 	       kcm_operation opcode,
1437 	       krb5_storage *request,
1438 	       krb5_storage *response)
1439 {
1440     struct kcm_ntlm_cred *c;
1441     struct ntlm_type2 type2;
1442     struct ntlm_type3 type3;
1443     char *user = NULL, *domain = NULL;
1444     struct ntlm_buf ndata, sessionkey;
1445     krb5_data data;
1446     krb5_error_code ret;
1447     uint32_t flags = 0;
1448 
1449     memset(&type2, 0, sizeof(type2));
1450     memset(&type3, 0, sizeof(type3));
1451     sessionkey.data = NULL;
1452     sessionkey.length = 0;
1453 
1454     ret = krb5_ret_stringz(request, &user);
1455     if (ret)
1456 	goto error;
1457 
1458     ret = krb5_ret_stringz(request, &domain);
1459     if (ret)
1460 	goto error;
1461 
1462     if (domain[0] == '\0') {
1463 	free(domain);
1464 	domain = NULL;
1465     }
1466 
1467     c = find_ntlm_cred(user, domain, client);
1468     if (c == NULL) {
1469 	ret = EINVAL;
1470 	goto error;
1471     }
1472 
1473     ret = krb5_ret_data(request, &data);
1474     if (ret)
1475 	goto error;
1476 
1477     ndata.data = data.data;
1478     ndata.length = data.length;
1479 
1480     ret = heim_ntlm_decode_type2(&ndata, &type2);
1481     krb5_data_free(&data);
1482     if (ret)
1483 	goto error;
1484 
1485     if (domain && strcmp(domain, type2.targetname) == 0) {
1486 	ret = EINVAL;
1487 	goto error;
1488     }
1489 
1490     type3.username = c->user;
1491     type3.flags = type2.flags;
1492     type3.targetname = type2.targetname;
1493     type3.ws = rk_UNCONST("workstation");
1494 
1495     /*
1496      * NTLM Version 1 if no targetinfo buffer.
1497      */
1498 
1499     if (1 || type2.targetinfo.length == 0) {
1500 	struct ntlm_buf sessionkey;
1501 
1502 	if (type2.flags & NTLM_NEG_NTLM2_SESSION) {
1503 	    unsigned char nonce[8];
1504 
1505 	    if (RAND_bytes(nonce, sizeof(nonce)) != 1) {
1506 		ret = EINVAL;
1507 		goto error;
1508 	    }
1509 
1510 	    ret = heim_ntlm_calculate_ntlm2_sess(nonce,
1511 						 type2.challenge,
1512 						 c->nthash.data,
1513 						 &type3.lm,
1514 						 &type3.ntlm);
1515 	} else {
1516 	    ret = heim_ntlm_calculate_ntlm1(c->nthash.data,
1517 					    c->nthash.length,
1518 					    type2.challenge,
1519 					    &type3.ntlm);
1520 
1521 	}
1522 	if (ret)
1523 	    goto error;
1524 
1525 	ret = heim_ntlm_build_ntlm1_master(c->nthash.data,
1526 					   c->nthash.length,
1527 					   &sessionkey,
1528 					   &type3.sessionkey);
1529 	if (ret) {
1530 	    if (type3.lm.data)
1531 		free(type3.lm.data);
1532 	    if (type3.ntlm.data)
1533 		free(type3.ntlm.data);
1534 	    goto error;
1535 	}
1536 
1537 	free(sessionkey.data);
1538 	if (ret) {
1539 	    if (type3.lm.data)
1540 		free(type3.lm.data);
1541 	    if (type3.ntlm.data)
1542 		free(type3.ntlm.data);
1543 	    goto error;
1544 	}
1545 	flags |= NTLM_FLAG_SESSIONKEY;
1546 #if 0
1547     } else {
1548 	struct ntlm_buf sessionkey;
1549 	unsigned char ntlmv2[16];
1550 	struct ntlm_targetinfo ti;
1551 
1552 	/* verify infotarget */
1553 
1554 	ret = heim_ntlm_decode_targetinfo(&type2.targetinfo, 1, &ti);
1555 	if(ret) {
1556 	    _gss_ntlm_delete_sec_context(minor_status,
1557 					 context_handle, NULL);
1558 	    *minor_status = ret;
1559 	    return GSS_S_FAILURE;
1560 	}
1561 
1562 	if (ti.domainname && strcmp(ti.domainname, name->domain) != 0) {
1563 	    _gss_ntlm_delete_sec_context(minor_status,
1564 					 context_handle, NULL);
1565 	    *minor_status = EINVAL;
1566 	    return GSS_S_FAILURE;
1567 	}
1568 
1569 	ret = heim_ntlm_calculate_ntlm2(ctx->client->key.data,
1570 					ctx->client->key.length,
1571 					type3.username,
1572 					name->domain,
1573 					type2.challenge,
1574 					&type2.targetinfo,
1575 					ntlmv2,
1576 					&type3.ntlm);
1577 	if (ret) {
1578 	    _gss_ntlm_delete_sec_context(minor_status,
1579 					 context_handle, NULL);
1580 	    *minor_status = ret;
1581 	    return GSS_S_FAILURE;
1582 	}
1583 
1584 	ret = heim_ntlm_build_ntlm1_master(ntlmv2, sizeof(ntlmv2),
1585 					   &sessionkey,
1586 					   &type3.sessionkey);
1587 	memset(ntlmv2, 0, sizeof(ntlmv2));
1588 	if (ret) {
1589 	    _gss_ntlm_delete_sec_context(minor_status,
1590 					 context_handle, NULL);
1591 	    *minor_status = ret;
1592 	    return GSS_S_FAILURE;
1593 	}
1594 
1595 	flags |= NTLM_FLAG_NTLM2_SESSION |
1596 	         NTLM_FLAG_SESSION;
1597 
1598 	if (type3.flags & NTLM_NEG_KEYEX)
1599 	    flags |= NTLM_FLAG_KEYEX;
1600 
1601 	ret = krb5_data_copy(&ctx->sessionkey,
1602 			     sessionkey.data, sessionkey.length);
1603 	free(sessionkey.data);
1604 	if (ret) {
1605 	    _gss_ntlm_delete_sec_context(minor_status,
1606 					 context_handle, NULL);
1607 	    *minor_status = ret;
1608 	    return GSS_S_FAILURE;
1609 	}
1610 #endif
1611     }
1612 
1613 #if 0
1614     if (flags & NTLM_FLAG_NTLM2_SESSION) {
1615 	_gss_ntlm_set_key(&ctx->u.v2.send, 0, (ctx->flags & NTLM_NEG_KEYEX),
1616 			  ctx->sessionkey.data,
1617 			  ctx->sessionkey.length);
1618 	_gss_ntlm_set_key(&ctx->u.v2.recv, 1, (ctx->flags & NTLM_NEG_KEYEX),
1619 			  ctx->sessionkey.data,
1620 			  ctx->sessionkey.length);
1621     } else {
1622 	flags |= NTLM_FLAG_SESSION;
1623 	RC4_set_key(&ctx->u.v1.crypto_recv.key,
1624 		    ctx->sessionkey.length,
1625 		    ctx->sessionkey.data);
1626 	RC4_set_key(&ctx->u.v1.crypto_send.key,
1627 		    ctx->sessionkey.length,
1628 		    ctx->sessionkey.data);
1629     }
1630 #endif
1631 
1632     ret = heim_ntlm_encode_type3(&type3, &ndata);
1633     if (ret)
1634 	goto error;
1635 
1636     data.data = ndata.data;
1637     data.length = ndata.length;
1638     ret = krb5_store_data(response, data);
1639     heim_ntlm_free_buf(&ndata);
1640     if (ret) goto error;
1641 
1642     ret = krb5_store_int32(response, flags);
1643     if (ret) goto error;
1644 
1645     data.data = sessionkey.data;
1646     data.length = sessionkey.length;
1647 
1648     ret = krb5_store_data(response, data);
1649     if (ret) goto error;
1650 
1651  error:
1652     free(type3.username);
1653     heim_ntlm_free_type2(&type2);
1654     free(user);
1655     if (domain)
1656 	free(domain);
1657 
1658     return ret;
1659 }
1660 
1661 
1662 /*
1663  * { "GET_NTLM_UUID_LIST",	NULL }
1664  *
1665  * reply:
1666  *   1 user domain
1667  *   0 [ end of list ]
1668  */
1669 
1670 static krb5_error_code
1671 kcm_op_get_ntlm_user_list(krb5_context context,
1672 			  kcm_client *client,
1673 			  kcm_operation opcode,
1674 			  krb5_storage *request,
1675 			  krb5_storage *response)
1676 {
1677     struct kcm_ntlm_cred *c;
1678     krb5_error_code ret;
1679 
1680     for (c = ntlm_head; c != NULL; c = c->next) {
1681 	if (!kcm_is_same_session(client, c->uid, c->session))
1682 	    continue;
1683 
1684 	ret = krb5_store_uint32(response, 1);
1685 	if (ret)
1686 	    return ret;
1687 	ret = krb5_store_stringz(response, c->user);
1688 	if (ret)
1689 	    return ret;
1690 	ret = krb5_store_stringz(response, c->domain);
1691 	if (ret)
1692 	    return ret;
1693     }
1694     return krb5_store_uint32(response, 0);
1695 }
1696 
1697 /*
1698  *
1699  */
1700 
1701 static struct kcm_op kcm_ops[] = {
1702     { "NOOP", 			kcm_op_noop },
1703     { "GET_NAME",		kcm_op_get_name },
1704     { "RESOLVE",		kcm_op_noop },
1705     { "GEN_NEW", 		kcm_op_gen_new },
1706     { "INITIALIZE",		kcm_op_initialize },
1707     { "DESTROY",		kcm_op_destroy },
1708     { "STORE",			kcm_op_store },
1709     { "RETRIEVE",		kcm_op_retrieve },
1710     { "GET_PRINCIPAL",		kcm_op_get_principal },
1711     { "GET_CRED_UUID_LIST",	kcm_op_get_cred_uuid_list },
1712     { "GET_CRED_BY_UUID",	kcm_op_get_cred_by_uuid },
1713     { "REMOVE_CRED",		kcm_op_remove_cred },
1714     { "SET_FLAGS",		kcm_op_set_flags },
1715     { "CHOWN",			kcm_op_chown },
1716     { "CHMOD",			kcm_op_chmod },
1717     { "GET_INITIAL_TICKET",	kcm_op_get_initial_ticket },
1718     { "GET_TICKET",		kcm_op_get_ticket },
1719     { "MOVE_CACHE",		kcm_op_move_cache },
1720     { "GET_CACHE_UUID_LIST",	kcm_op_get_cache_uuid_list },
1721     { "GET_CACHE_BY_UUID",	kcm_op_get_cache_by_uuid },
1722     { "GET_DEFAULT_CACHE",      kcm_op_get_default_cache },
1723     { "SET_DEFAULT_CACHE",      kcm_op_set_default_cache },
1724     { "GET_KDC_OFFSET",      	kcm_op_get_kdc_offset },
1725     { "SET_KDC_OFFSET",      	kcm_op_set_kdc_offset },
1726     { "ADD_NTLM_CRED",		kcm_op_add_ntlm_cred },
1727     { "HAVE_USER_CRED",		kcm_op_have_ntlm_cred },
1728     { "DEL_NTLM_CRED",		kcm_op_del_ntlm_cred },
1729     { "DO_NTLM_AUTH",		kcm_op_do_ntlm },
1730     { "GET_NTLM_USER_LIST",	kcm_op_get_ntlm_user_list }
1731 };
1732 
1733 
1734 const char *
1735 kcm_op2string(kcm_operation opcode)
1736 {
1737     if (opcode >= sizeof(kcm_ops)/sizeof(kcm_ops[0]))
1738 	return "Unknown operation";
1739 
1740     return kcm_ops[opcode].name;
1741 }
1742 
1743 krb5_error_code
1744 kcm_dispatch(krb5_context context,
1745 	     kcm_client *client,
1746 	     krb5_data *req_data,
1747 	     krb5_data *resp_data)
1748 {
1749     krb5_error_code ret;
1750     kcm_method method;
1751     krb5_storage *req_sp = NULL;
1752     krb5_storage *resp_sp = NULL;
1753     uint16_t opcode;
1754 
1755     resp_sp = krb5_storage_emem();
1756     if (resp_sp == NULL) {
1757 	return ENOMEM;
1758     }
1759 
1760     if (client->pid == -1) {
1761 	kcm_log(0, "Client had invalid process number");
1762 	ret = KRB5_FCC_INTERNAL;
1763 	goto out;
1764     }
1765 
1766     req_sp = krb5_storage_from_data(req_data);
1767     if (req_sp == NULL) {
1768 	kcm_log(0, "Process %d: failed to initialize storage from data",
1769 		client->pid);
1770 	ret = KRB5_CC_IO;
1771 	goto out;
1772     }
1773 
1774     ret = krb5_ret_uint16(req_sp, &opcode);
1775     if (ret) {
1776 	kcm_log(0, "Process %d: didn't send a message", client->pid);
1777 	goto out;
1778     }
1779 
1780     if (opcode >= sizeof(kcm_ops)/sizeof(kcm_ops[0])) {
1781 	kcm_log(0, "Process %d: invalid operation code %d",
1782 		client->pid, opcode);
1783 	ret = KRB5_FCC_INTERNAL;
1784 	goto out;
1785     }
1786     method = kcm_ops[opcode].method;
1787     if (method == NULL) {
1788 	kcm_log(0, "Process %d: operation code %s not implemented",
1789 		client->pid, kcm_op2string(opcode));
1790 	ret = KRB5_FCC_INTERNAL;
1791 	goto out;
1792     }
1793 
1794     /* seek past place for status code */
1795     krb5_storage_seek(resp_sp, 4, SEEK_SET);
1796 
1797     ret = (*method)(context, client, opcode, req_sp, resp_sp);
1798 
1799 out:
1800     if (req_sp != NULL) {
1801 	krb5_storage_free(req_sp);
1802     }
1803 
1804     krb5_storage_seek(resp_sp, 0, SEEK_SET);
1805     krb5_store_int32(resp_sp, ret);
1806 
1807     ret = krb5_storage_to_data(resp_sp, resp_data);
1808     krb5_storage_free(resp_sp);
1809 
1810     return ret;
1811 }
1812 
1813