xref: /freebsd/crypto/heimdal/kcm/protocol.c (revision 10b59a9b4add0320d52c15ce057dd697261e7dfc)
1 /*
2  * Copyright (c) 2005, PADL Software Pty Ltd.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * 3. Neither the name of PADL Software nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include "kcm_locl.h"
34 
35 RCSID("$Id: protocol.c 22112 2007-12-03 19:34:33Z lha $");
36 
37 static krb5_error_code
38 kcm_op_noop(krb5_context context,
39 	    kcm_client *client,
40 	    kcm_operation opcode,
41 	    krb5_storage *request,
42 	    krb5_storage *response)
43 {
44     KCM_LOG_REQUEST(context, client, opcode);
45 
46     return 0;
47 }
48 
49 /*
50  * Request:
51  *	NameZ
52  * Response:
53  *	NameZ
54  *
55  */
56 static krb5_error_code
57 kcm_op_get_name(krb5_context context,
58 		kcm_client *client,
59 		kcm_operation opcode,
60 		krb5_storage *request,
61 		krb5_storage *response)
62 
63 {
64     krb5_error_code ret;
65     char *name = NULL;
66     kcm_ccache ccache;
67 
68     ret = krb5_ret_stringz(request, &name);
69     if (ret)
70 	return ret;
71 
72     KCM_LOG_REQUEST_NAME(context, client, opcode, name);
73 
74     ret = kcm_ccache_resolve_client(context, client, opcode,
75 				    name, &ccache);
76     if (ret) {
77 	free(name);
78 	return ret;
79     }
80 
81     ret = krb5_store_stringz(response, ccache->name);
82     if (ret) {
83 	kcm_release_ccache(context, &ccache);
84 	free(name);
85 	return ret;
86     }
87 
88     free(name);
89     kcm_release_ccache(context, &ccache);
90     return 0;
91 }
92 
93 /*
94  * Request:
95  *
96  * Response:
97  *	NameZ
98  */
99 static krb5_error_code
100 kcm_op_gen_new(krb5_context context,
101 	       kcm_client *client,
102 	       kcm_operation opcode,
103 	       krb5_storage *request,
104 	       krb5_storage *response)
105 {
106     krb5_error_code ret;
107     char *name;
108 
109     KCM_LOG_REQUEST(context, client, opcode);
110 
111     name = kcm_ccache_nextid(client->pid, client->uid, client->gid);
112     if (name == NULL) {
113 	return KRB5_CC_NOMEM;
114     }
115 
116     ret = krb5_store_stringz(response, name);
117     free(name);
118 
119     return ret;
120 }
121 
122 /*
123  * Request:
124  *	NameZ
125  *	Principal
126  *
127  * Response:
128  *
129  */
130 static krb5_error_code
131 kcm_op_initialize(krb5_context context,
132 		  kcm_client *client,
133 		  kcm_operation opcode,
134 		  krb5_storage *request,
135 		  krb5_storage *response)
136 {
137     kcm_ccache ccache;
138     krb5_principal principal;
139     krb5_error_code ret;
140     char *name;
141 #if 0
142     kcm_event event;
143 #endif
144 
145     KCM_LOG_REQUEST(context, client, opcode);
146 
147     ret = krb5_ret_stringz(request, &name);
148     if (ret)
149 	return ret;
150 
151     ret = krb5_ret_principal(request, &principal);
152     if (ret) {
153 	free(name);
154 	return ret;
155     }
156 
157     ret = kcm_ccache_new_client(context, client, name, &ccache);
158     if (ret) {
159 	free(name);
160 	krb5_free_principal(context, principal);
161 	return ret;
162     }
163 
164     ccache->client = principal;
165 
166     free(name);
167 
168 #if 0
169     /*
170      * Create a new credentials cache. To mitigate DoS attacks we will
171      * expire it in 30 minutes unless it has some credentials added
172      * to it
173      */
174 
175     event.fire_time = 30 * 60;
176     event.expire_time = 0;
177     event.backoff_time = 0;
178     event.action = KCM_EVENT_DESTROY_EMPTY_CACHE;
179     event.ccache = ccache;
180 
181     ret = kcm_enqueue_event_relative(context, &event);
182 #endif
183 
184     kcm_release_ccache(context, &ccache);
185 
186     return ret;
187 }
188 
189 /*
190  * Request:
191  *	NameZ
192  *
193  * Response:
194  *
195  */
196 static krb5_error_code
197 kcm_op_destroy(krb5_context context,
198 	       kcm_client *client,
199 	       kcm_operation opcode,
200 	       krb5_storage *request,
201 	       krb5_storage *response)
202 {
203     krb5_error_code ret;
204     char *name;
205 
206     ret = krb5_ret_stringz(request, &name);
207     if (ret)
208 	return ret;
209 
210     KCM_LOG_REQUEST_NAME(context, client, opcode, name);
211 
212     ret = kcm_ccache_destroy_client(context, client, name);
213 
214     free(name);
215 
216     return ret;
217 }
218 
219 /*
220  * Request:
221  *	NameZ
222  *	Creds
223  *
224  * Response:
225  *
226  */
227 static krb5_error_code
228 kcm_op_store(krb5_context context,
229 	     kcm_client *client,
230 	     kcm_operation opcode,
231 	     krb5_storage *request,
232 	     krb5_storage *response)
233 {
234     krb5_creds creds;
235     krb5_error_code ret;
236     kcm_ccache ccache;
237     char *name;
238 
239     ret = krb5_ret_stringz(request, &name);
240     if (ret)
241 	return ret;
242 
243     KCM_LOG_REQUEST_NAME(context, client, opcode, name);
244 
245     ret = krb5_ret_creds(request, &creds);
246     if (ret) {
247 	free(name);
248 	return ret;
249     }
250 
251     ret = kcm_ccache_resolve_client(context, client, opcode,
252 				    name, &ccache);
253     if (ret) {
254 	free(name);
255 	krb5_free_cred_contents(context, &creds);
256 	return ret;
257     }
258 
259     ret = kcm_ccache_store_cred(context, ccache, &creds, 0);
260     if (ret) {
261 	free(name);
262 	krb5_free_cred_contents(context, &creds);
263 	kcm_release_ccache(context, &ccache);
264 	return ret;
265     }
266 
267     kcm_ccache_enqueue_default(context, ccache, &creds);
268 
269     free(name);
270     kcm_release_ccache(context, &ccache);
271 
272     return 0;
273 }
274 
275 /*
276  * Request:
277  *	NameZ
278  *	WhichFields
279  *	MatchCreds
280  *
281  * Response:
282  *	Creds
283  *
284  */
285 static krb5_error_code
286 kcm_op_retrieve(krb5_context context,
287 		kcm_client *client,
288 		kcm_operation opcode,
289 		krb5_storage *request,
290 		krb5_storage *response)
291 {
292     uint32_t flags;
293     krb5_creds mcreds;
294     krb5_error_code ret;
295     kcm_ccache ccache;
296     char *name;
297     krb5_creds *credp;
298     int free_creds = 0;
299 
300     ret = krb5_ret_stringz(request, &name);
301     if (ret)
302 	return ret;
303 
304     KCM_LOG_REQUEST_NAME(context, client, opcode, name);
305 
306     ret = krb5_ret_uint32(request, &flags);
307     if (ret) {
308 	free(name);
309 	return ret;
310     }
311 
312     ret = krb5_ret_creds_tag(request, &mcreds);
313     if (ret) {
314 	free(name);
315 	return ret;
316     }
317 
318     if (disallow_getting_krbtgt &&
319 	mcreds.server->name.name_string.len == 2 &&
320 	strcmp(mcreds.server->name.name_string.val[0], KRB5_TGS_NAME) == 0)
321     {
322 	free(name);
323 	krb5_free_cred_contents(context, &mcreds);
324 	return KRB5_FCC_PERM;
325     }
326 
327     ret = kcm_ccache_resolve_client(context, client, opcode,
328 				    name, &ccache);
329     if (ret) {
330 	free(name);
331 	krb5_free_cred_contents(context, &mcreds);
332 	return ret;
333     }
334 
335     ret = kcm_ccache_retrieve_cred(context, ccache, flags,
336 				   &mcreds, &credp);
337     if (ret && ((flags & KRB5_GC_CACHED) == 0)) {
338 	krb5_ccache_data ccdata;
339 
340 	/* try and acquire */
341 	HEIMDAL_MUTEX_lock(&ccache->mutex);
342 
343 	/* Fake up an internal ccache */
344 	kcm_internal_ccache(context, ccache, &ccdata);
345 
346 	/* glue cc layer will store creds */
347 	ret = krb5_get_credentials(context, 0, &ccdata, &mcreds, &credp);
348 	if (ret == 0)
349 	    free_creds = 1;
350 
351 	HEIMDAL_MUTEX_unlock(&ccache->mutex);
352     }
353 
354     if (ret == 0) {
355 	ret = krb5_store_creds(response, credp);
356     }
357 
358     free(name);
359     krb5_free_cred_contents(context, &mcreds);
360     kcm_release_ccache(context, &ccache);
361 
362     if (free_creds)
363 	krb5_free_cred_contents(context, credp);
364 
365     return ret;
366 }
367 
368 /*
369  * Request:
370  *	NameZ
371  *
372  * Response:
373  *	Principal
374  */
375 static krb5_error_code
376 kcm_op_get_principal(krb5_context context,
377 		     kcm_client *client,
378 		     kcm_operation opcode,
379 		     krb5_storage *request,
380 		     krb5_storage *response)
381 {
382     krb5_error_code ret;
383     kcm_ccache ccache;
384     char *name;
385 
386     ret = krb5_ret_stringz(request, &name);
387     if (ret)
388 	return ret;
389 
390     KCM_LOG_REQUEST_NAME(context, client, opcode, name);
391 
392     ret = kcm_ccache_resolve_client(context, client, opcode,
393 				    name, &ccache);
394     if (ret) {
395 	free(name);
396 	return ret;
397     }
398 
399     if (ccache->client == NULL)
400 	ret = KRB5_CC_NOTFOUND;
401     else
402 	ret = krb5_store_principal(response, ccache->client);
403 
404     free(name);
405     kcm_release_ccache(context, &ccache);
406 
407     return 0;
408 }
409 
410 /*
411  * Request:
412  *	NameZ
413  *
414  * Response:
415  *	Cursor
416  *
417  */
418 static krb5_error_code
419 kcm_op_get_first(krb5_context context,
420 		 kcm_client *client,
421 		 kcm_operation opcode,
422 		 krb5_storage *request,
423 		 krb5_storage *response)
424 {
425     krb5_error_code ret;
426     kcm_ccache ccache;
427     uint32_t cursor;
428     char *name;
429 
430     ret = krb5_ret_stringz(request, &name);
431     if (ret)
432 	return ret;
433 
434     KCM_LOG_REQUEST_NAME(context, client, opcode, name);
435 
436     ret = kcm_ccache_resolve_client(context, client, opcode,
437 				    name, &ccache);
438     if (ret) {
439 	free(name);
440 	return ret;
441     }
442 
443     ret = kcm_cursor_new(context, client->pid, ccache, &cursor);
444     if (ret) {
445 	kcm_release_ccache(context, &ccache);
446 	free(name);
447 	return ret;
448     }
449 
450     ret = krb5_store_int32(response, cursor);
451 
452     free(name);
453     kcm_release_ccache(context, &ccache);
454 
455     return ret;
456 }
457 
458 /*
459  * Request:
460  *	NameZ
461  *	Cursor
462  *
463  * Response:
464  *	Creds
465  */
466 static krb5_error_code
467 kcm_op_get_next(krb5_context context,
468 		kcm_client *client,
469 		kcm_operation opcode,
470 		krb5_storage *request,
471 		krb5_storage *response)
472 {
473     krb5_error_code ret;
474     kcm_ccache ccache;
475     char *name;
476     uint32_t cursor;
477     kcm_cursor *c;
478 
479     ret = krb5_ret_stringz(request, &name);
480     if (ret)
481 	return ret;
482 
483     KCM_LOG_REQUEST_NAME(context, client, opcode, name);
484 
485     ret = krb5_ret_uint32(request, &cursor);
486     if (ret) {
487 	free(name);
488 	return ret;
489     }
490 
491     ret = kcm_ccache_resolve_client(context, client, opcode,
492 				    name, &ccache);
493     if (ret) {
494 	free(name);
495 	return ret;
496     }
497 
498     ret = kcm_cursor_find(context, client->pid, ccache, cursor, &c);
499     if (ret) {
500 	kcm_release_ccache(context, &ccache);
501 	free(name);
502 	return ret;
503     }
504 
505     HEIMDAL_MUTEX_lock(&ccache->mutex);
506     if (c->credp == NULL) {
507 	ret = KRB5_CC_END;
508     } else {
509 	ret = krb5_store_creds(response, &c->credp->cred);
510 	c->credp = c->credp->next;
511     }
512     HEIMDAL_MUTEX_unlock(&ccache->mutex);
513 
514     free(name);
515     kcm_release_ccache(context, &ccache);
516 
517     return ret;
518 }
519 
520 /*
521  * Request:
522  *	NameZ
523  *	Cursor
524  *
525  * Response:
526  *
527  */
528 static krb5_error_code
529 kcm_op_end_get(krb5_context context,
530 	       kcm_client *client,
531 	       kcm_operation opcode,
532 	       krb5_storage *request,
533 	       krb5_storage *response)
534 {
535     krb5_error_code ret;
536     kcm_ccache ccache;
537     uint32_t cursor;
538     char *name;
539 
540     ret = krb5_ret_stringz(request, &name);
541     if (ret)
542 	return ret;
543 
544     KCM_LOG_REQUEST_NAME(context, client, opcode, name);
545 
546     ret = krb5_ret_uint32(request, &cursor);
547     if (ret) {
548 	free(name);
549 	return ret;
550     }
551 
552     ret = kcm_ccache_resolve_client(context, client, opcode,
553 				    name, &ccache);
554     if (ret) {
555 	free(name);
556 	return ret;
557     }
558 
559     ret = kcm_cursor_delete(context, client->pid, ccache, cursor);
560 
561     free(name);
562     kcm_release_ccache(context, &ccache);
563 
564     return ret;
565 }
566 
567 /*
568  * Request:
569  *	NameZ
570  *	WhichFields
571  *	MatchCreds
572  *
573  * Response:
574  *
575  */
576 static krb5_error_code
577 kcm_op_remove_cred(krb5_context context,
578 		   kcm_client *client,
579 		   kcm_operation opcode,
580 		   krb5_storage *request,
581 		   krb5_storage *response)
582 {
583     uint32_t whichfields;
584     krb5_creds mcreds;
585     krb5_error_code ret;
586     kcm_ccache ccache;
587     char *name;
588 
589     ret = krb5_ret_stringz(request, &name);
590     if (ret)
591 	return ret;
592 
593     KCM_LOG_REQUEST_NAME(context, client, opcode, name);
594 
595     ret = krb5_ret_uint32(request, &whichfields);
596     if (ret) {
597 	free(name);
598 	return ret;
599     }
600 
601     ret = krb5_ret_creds_tag(request, &mcreds);
602     if (ret) {
603 	free(name);
604 	return ret;
605     }
606 
607     ret = kcm_ccache_resolve_client(context, client, opcode,
608 				    name, &ccache);
609     if (ret) {
610 	free(name);
611 	krb5_free_cred_contents(context, &mcreds);
612 	return ret;
613     }
614 
615     ret = kcm_ccache_remove_cred(context, ccache, whichfields, &mcreds);
616 
617     /* XXX need to remove any events that match */
618 
619     free(name);
620     krb5_free_cred_contents(context, &mcreds);
621     kcm_release_ccache(context, &ccache);
622 
623     return ret;
624 }
625 
626 /*
627  * Request:
628  *	NameZ
629  *	Flags
630  *
631  * Response:
632  *
633  */
634 static krb5_error_code
635 kcm_op_set_flags(krb5_context context,
636 		 kcm_client *client,
637 		 kcm_operation opcode,
638 		 krb5_storage *request,
639 		 krb5_storage *response)
640 {
641     uint32_t flags;
642     krb5_error_code ret;
643     kcm_ccache ccache;
644     char *name;
645 
646     ret = krb5_ret_stringz(request, &name);
647     if (ret)
648 	return ret;
649 
650     KCM_LOG_REQUEST_NAME(context, client, opcode, name);
651 
652     ret = krb5_ret_uint32(request, &flags);
653     if (ret) {
654 	free(name);
655 	return ret;
656     }
657 
658     ret = kcm_ccache_resolve_client(context, client, opcode,
659 				    name, &ccache);
660     if (ret) {
661 	free(name);
662 	return ret;
663     }
664 
665     /* we don't really support any flags yet */
666     free(name);
667     kcm_release_ccache(context, &ccache);
668 
669     return 0;
670 }
671 
672 /*
673  * Request:
674  *	NameZ
675  *	UID
676  *	GID
677  *
678  * Response:
679  *
680  */
681 static krb5_error_code
682 kcm_op_chown(krb5_context context,
683 	     kcm_client *client,
684 	     kcm_operation opcode,
685 	     krb5_storage *request,
686 	     krb5_storage *response)
687 {
688     uint32_t uid;
689     uint32_t gid;
690     krb5_error_code ret;
691     kcm_ccache ccache;
692     char *name;
693 
694     ret = krb5_ret_stringz(request, &name);
695     if (ret)
696 	return ret;
697 
698     KCM_LOG_REQUEST_NAME(context, client, opcode, name);
699 
700     ret = krb5_ret_uint32(request, &uid);
701     if (ret) {
702 	free(name);
703 	return ret;
704     }
705 
706     ret = krb5_ret_uint32(request, &gid);
707     if (ret) {
708 	free(name);
709 	return ret;
710     }
711 
712     ret = kcm_ccache_resolve_client(context, client, opcode,
713 				    name, &ccache);
714     if (ret) {
715 	free(name);
716 	return ret;
717     }
718 
719     ret = kcm_chown(context, client, ccache, uid, gid);
720 
721     free(name);
722     kcm_release_ccache(context, &ccache);
723 
724     return ret;
725 }
726 
727 /*
728  * Request:
729  *	NameZ
730  *	Mode
731  *
732  * Response:
733  *
734  */
735 static krb5_error_code
736 kcm_op_chmod(krb5_context context,
737 	     kcm_client *client,
738 	     kcm_operation opcode,
739 	     krb5_storage *request,
740 	     krb5_storage *response)
741 {
742     uint16_t mode;
743     krb5_error_code ret;
744     kcm_ccache ccache;
745     char *name;
746 
747     ret = krb5_ret_stringz(request, &name);
748     if (ret)
749 	return ret;
750 
751     KCM_LOG_REQUEST_NAME(context, client, opcode, name);
752 
753     ret = krb5_ret_uint16(request, &mode);
754     if (ret) {
755 	free(name);
756 	return ret;
757     }
758 
759     ret = kcm_ccache_resolve_client(context, client, opcode,
760 				    name, &ccache);
761     if (ret) {
762 	free(name);
763 	return ret;
764     }
765 
766     ret = kcm_chmod(context, client, ccache, mode);
767 
768     free(name);
769     kcm_release_ccache(context, &ccache);
770 
771     return ret;
772 }
773 
774 /*
775  * Protocol extensions for moving ticket acquisition responsibility
776  * from client to KCM follow.
777  */
778 
779 /*
780  * Request:
781  *	NameZ
782  *	ServerPrincipalPresent
783  *	ServerPrincipal OPTIONAL
784  *	Key
785  *
786  * Repsonse:
787  *
788  */
789 static krb5_error_code
790 kcm_op_get_initial_ticket(krb5_context context,
791 			  kcm_client *client,
792 			  kcm_operation opcode,
793 			  krb5_storage *request,
794 			  krb5_storage *response)
795 {
796     krb5_error_code ret;
797     kcm_ccache ccache;
798     char *name;
799     int8_t not_tgt = 0;
800     krb5_principal server = NULL;
801     krb5_keyblock key;
802 
803     krb5_keyblock_zero(&key);
804 
805     ret = krb5_ret_stringz(request, &name);
806     if (ret)
807 	return ret;
808 
809     KCM_LOG_REQUEST_NAME(context, client, opcode, name);
810 
811     ret = krb5_ret_int8(request, &not_tgt);
812     if (ret) {
813 	free(name);
814 	return ret;
815     }
816 
817     if (not_tgt) {
818 	ret = krb5_ret_principal(request, &server);
819 	if (ret) {
820 	    free(name);
821 	    return ret;
822 	}
823     }
824 
825     ret = krb5_ret_keyblock(request, &key);
826     if (ret) {
827 	free(name);
828 	if (server != NULL)
829 	    krb5_free_principal(context, server);
830 	return ret;
831     }
832 
833     ret = kcm_ccache_resolve_client(context, client, opcode,
834 				    name, &ccache);
835     if (ret == 0) {
836 	HEIMDAL_MUTEX_lock(&ccache->mutex);
837 
838 	if (ccache->server != NULL) {
839 	    krb5_free_principal(context, ccache->server);
840 	    ccache->server = NULL;
841 	}
842 
843 	krb5_free_keyblock(context, &ccache->key.keyblock);
844 
845 	ccache->server = server;
846 	ccache->key.keyblock = key;
847     	ccache->flags |= KCM_FLAGS_USE_CACHED_KEY;
848 
849 	ret = kcm_ccache_enqueue_default(context, ccache, NULL);
850 	if (ret) {
851 	    ccache->server = NULL;
852 	    krb5_keyblock_zero(&ccache->key.keyblock);
853 	    ccache->flags &= ~(KCM_FLAGS_USE_CACHED_KEY);
854 	}
855 
856 	HEIMDAL_MUTEX_unlock(&ccache->mutex);
857     }
858 
859     free(name);
860 
861     if (ret != 0) {
862 	krb5_free_principal(context, server);
863 	krb5_free_keyblock(context, &key);
864     }
865 
866     kcm_release_ccache(context, &ccache);
867 
868     return ret;
869 }
870 
871 /*
872  * Request:
873  *	NameZ
874  *	ServerPrincipal
875  *	KDCFlags
876  *	EncryptionType
877  *
878  * Repsonse:
879  *
880  */
881 static krb5_error_code
882 kcm_op_get_ticket(krb5_context context,
883 		  kcm_client *client,
884 		  kcm_operation opcode,
885 		  krb5_storage *request,
886 		  krb5_storage *response)
887 {
888     krb5_error_code ret;
889     kcm_ccache ccache;
890     char *name;
891     krb5_principal server = NULL;
892     krb5_ccache_data ccdata;
893     krb5_creds in, *out;
894     krb5_kdc_flags flags;
895 
896     memset(&in, 0, sizeof(in));
897 
898     ret = krb5_ret_stringz(request, &name);
899     if (ret)
900 	return ret;
901 
902     KCM_LOG_REQUEST_NAME(context, client, opcode, name);
903 
904     ret = krb5_ret_uint32(request, &flags.i);
905     if (ret) {
906 	free(name);
907 	return ret;
908     }
909 
910     ret = krb5_ret_int32(request, &in.session.keytype);
911     if (ret) {
912 	free(name);
913 	return ret;
914     }
915 
916     ret = krb5_ret_principal(request, &server);
917     if (ret) {
918 	free(name);
919 	return ret;
920     }
921 
922     ret = kcm_ccache_resolve_client(context, client, opcode,
923 				    name, &ccache);
924     if (ret) {
925 	krb5_free_principal(context, server);
926 	free(name);
927 	return ret;
928     }
929 
930     HEIMDAL_MUTEX_lock(&ccache->mutex);
931 
932     /* Fake up an internal ccache */
933     kcm_internal_ccache(context, ccache, &ccdata);
934 
935     in.client = ccache->client;
936     in.server = server;
937     in.times.endtime = 0;
938 
939     /* glue cc layer will store creds */
940     ret = krb5_get_credentials_with_flags(context, 0, flags,
941 					  &ccdata, &in, &out);
942 
943     HEIMDAL_MUTEX_unlock(&ccache->mutex);
944 
945     if (ret == 0)
946 	krb5_free_cred_contents(context, out);
947 
948     free(name);
949 
950     return ret;
951 }
952 
953 static struct kcm_op kcm_ops[] = {
954     { "NOOP", 			kcm_op_noop },
955     { "GET_NAME",		kcm_op_get_name },
956     { "RESOLVE",		kcm_op_noop },
957     { "GEN_NEW", 		kcm_op_gen_new },
958     { "INITIALIZE",		kcm_op_initialize },
959     { "DESTROY",		kcm_op_destroy },
960     { "STORE",			kcm_op_store },
961     { "RETRIEVE",		kcm_op_retrieve },
962     { "GET_PRINCIPAL",		kcm_op_get_principal },
963     { "GET_FIRST",		kcm_op_get_first },
964     { "GET_NEXT",		kcm_op_get_next },
965     { "END_GET",		kcm_op_end_get },
966     { "REMOVE_CRED",		kcm_op_remove_cred },
967     { "SET_FLAGS",		kcm_op_set_flags },
968     { "CHOWN",			kcm_op_chown },
969     { "CHMOD",			kcm_op_chmod },
970     { "GET_INITIAL_TICKET",	kcm_op_get_initial_ticket },
971     { "GET_TICKET",		kcm_op_get_ticket }
972 };
973 
974 
975 const char *kcm_op2string(kcm_operation opcode)
976 {
977     if (opcode >= sizeof(kcm_ops)/sizeof(kcm_ops[0]))
978 	return "Unknown operation";
979 
980     return kcm_ops[opcode].name;
981 }
982 
983 krb5_error_code
984 kcm_dispatch(krb5_context context,
985 	     kcm_client *client,
986 	     krb5_data *req_data,
987 	     krb5_data *resp_data)
988 {
989     krb5_error_code ret;
990     kcm_method method;
991     krb5_storage *req_sp = NULL;
992     krb5_storage *resp_sp = NULL;
993     uint16_t opcode;
994 
995     resp_sp = krb5_storage_emem();
996     if (resp_sp == NULL) {
997 	return ENOMEM;
998     }
999 
1000     if (client->pid == -1) {
1001 	kcm_log(0, "Client had invalid process number");
1002 	ret = KRB5_FCC_INTERNAL;
1003 	goto out;
1004     }
1005 
1006     req_sp = krb5_storage_from_data(req_data);
1007     if (req_sp == NULL) {
1008 	kcm_log(0, "Process %d: failed to initialize storage from data",
1009 		client->pid);
1010 	ret = KRB5_CC_IO;
1011 	goto out;
1012     }
1013 
1014     ret = krb5_ret_uint16(req_sp, &opcode);
1015     if (ret) {
1016 	kcm_log(0, "Process %d: didn't send a message", client->pid);
1017 	goto out;
1018     }
1019 
1020     if (opcode >= sizeof(kcm_ops)/sizeof(kcm_ops[0])) {
1021 	kcm_log(0, "Process %d: invalid operation code %d",
1022 		client->pid, opcode);
1023 	ret = KRB5_FCC_INTERNAL;
1024 	goto out;
1025     }
1026     method = kcm_ops[opcode].method;
1027 
1028     /* seek past place for status code */
1029     krb5_storage_seek(resp_sp, 4, SEEK_SET);
1030 
1031     ret = (*method)(context, client, opcode, req_sp, resp_sp);
1032 
1033 out:
1034     if (req_sp != NULL) {
1035 	krb5_storage_free(req_sp);
1036     }
1037 
1038     krb5_storage_seek(resp_sp, 0, SEEK_SET);
1039     krb5_store_int32(resp_sp, ret);
1040 
1041     ret = krb5_storage_to_data(resp_sp, resp_data);
1042     krb5_storage_free(resp_sp);
1043 
1044     return ret;
1045 }
1046 
1047