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