add KRB5_KU_PA_PKINIT_KX
[metze/heimdal/wip.git] / kcm / protocol.c
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$");
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     struct kcm_creds *creds;
426     krb5_error_code ret;
427     kcm_ccache ccache;
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     free(name);
439     if (ret)
440         return ret;
441
442     for (creds = ccache->creds ; creds ; creds = creds->next) {
443         ssize_t sret;
444         sret = krb5_storage_write(response, &creds->uuid, sizeof(creds->uuid));
445         if (sret != sizeof(creds->uuid)) {
446             ret = ENOMEM;
447             break;
448         }
449     }
450
451     kcm_release_ccache(context, &ccache);
452
453     return ret;
454 }
455
456 /*
457  * Request:
458  *      NameZ
459  *      Cursor
460  *
461  * Response:
462  *      Creds
463  */
464 static krb5_error_code
465 kcm_op_get_next(krb5_context context,
466                 kcm_client *client,
467                 kcm_operation opcode,
468                 krb5_storage *request,
469                 krb5_storage *response)
470 {
471     krb5_error_code ret;
472     kcm_ccache ccache;
473     char *name;
474     struct kcm_creds *c;
475     kcmuuid_t uuid;
476     ssize_t sret;
477
478     ret = krb5_ret_stringz(request, &name);
479     if (ret)
480         return ret;
481
482     KCM_LOG_REQUEST_NAME(context, client, opcode, name);
483
484     ret = kcm_ccache_resolve_client(context, client, opcode,
485                                     name, &ccache);
486     free(name);
487     if (ret)
488         return ret;
489
490     sret = krb5_storage_read(request, &uuid, sizeof(uuid));
491     if (sret != sizeof(uuid)) {
492         kcm_release_ccache(context, &ccache);
493         krb5_clear_error_message(context);
494         return KRB5_CC_IO;
495     }
496
497     c = kcm_ccache_find_cred_uuid(context, ccache, uuid);
498     if (c == NULL) {
499         kcm_release_ccache(context, &ccache);
500         return KRB5_CC_END;
501     }
502
503     HEIMDAL_MUTEX_lock(&ccache->mutex);
504     ret = krb5_store_creds(response, &c->cred);
505     HEIMDAL_MUTEX_unlock(&ccache->mutex);
506
507     kcm_release_ccache(context, &ccache);
508
509     return ret;
510 }
511
512 /*
513  * Request:
514  *      NameZ
515  *      Cursor
516  *
517  * Response:
518  *      
519  */
520 static krb5_error_code
521 kcm_op_end_get(krb5_context context,
522                kcm_client *client,
523                kcm_operation opcode,
524                krb5_storage *request,
525                krb5_storage *response)
526 {
527     krb5_error_code ret;
528     char *name;
529
530     ret = krb5_ret_stringz(request, &name);
531     if (ret)
532         return ret;
533
534     KCM_LOG_REQUEST_NAME(context, client, opcode, name);
535     free(name);
536
537     return ret;
538 }
539
540 /*
541  * Request:
542  *      NameZ
543  *      WhichFields
544  *      MatchCreds
545  *
546  * Response:
547  *      
548  */
549 static krb5_error_code
550 kcm_op_remove_cred(krb5_context context,
551                    kcm_client *client,
552                    kcm_operation opcode,
553                    krb5_storage *request,
554                    krb5_storage *response)
555 {
556     uint32_t whichfields;
557     krb5_creds mcreds;
558     krb5_error_code ret;
559     kcm_ccache ccache;
560     char *name;
561
562     ret = krb5_ret_stringz(request, &name);
563     if (ret)
564         return ret;
565
566     KCM_LOG_REQUEST_NAME(context, client, opcode, name);
567
568     ret = krb5_ret_uint32(request, &whichfields);
569     if (ret) {
570         free(name);
571         return ret;
572     }
573
574     ret = krb5_ret_creds_tag(request, &mcreds);
575     if (ret) {
576         free(name);
577         return ret;
578     }
579
580     ret = kcm_ccache_resolve_client(context, client, opcode,
581                                     name, &ccache);
582     if (ret) {
583         free(name);
584         krb5_free_cred_contents(context, &mcreds);
585         return ret;
586     }
587
588     ret = kcm_ccache_remove_cred(context, ccache, whichfields, &mcreds);
589
590     /* XXX need to remove any events that match */
591
592     free(name);
593     krb5_free_cred_contents(context, &mcreds);
594     kcm_release_ccache(context, &ccache);
595
596     return ret;
597 }
598
599 /*
600  * Request:
601  *      NameZ
602  *      Flags
603  *
604  * Response:
605  *      
606  */
607 static krb5_error_code
608 kcm_op_set_flags(krb5_context context,
609                  kcm_client *client,
610                  kcm_operation opcode,
611                  krb5_storage *request,
612                  krb5_storage *response)
613 {
614     uint32_t flags;
615     krb5_error_code ret;
616     kcm_ccache ccache;
617     char *name;
618
619     ret = krb5_ret_stringz(request, &name);
620     if (ret)
621         return ret;
622
623     KCM_LOG_REQUEST_NAME(context, client, opcode, name);
624
625     ret = krb5_ret_uint32(request, &flags);
626     if (ret) {
627         free(name);
628         return ret;
629     }
630
631     ret = kcm_ccache_resolve_client(context, client, opcode,
632                                     name, &ccache);
633     if (ret) {
634         free(name);
635         return ret;
636     }
637
638     /* we don't really support any flags yet */
639     free(name);
640     kcm_release_ccache(context, &ccache);
641
642     return 0;
643 }
644
645 /*
646  * Request:
647  *      NameZ
648  *      UID
649  *      GID
650  *
651  * Response:
652  *      
653  */
654 static krb5_error_code
655 kcm_op_chown(krb5_context context,
656              kcm_client *client,
657              kcm_operation opcode,
658              krb5_storage *request,
659              krb5_storage *response)
660 {
661     uint32_t uid;
662     uint32_t gid;
663     krb5_error_code ret;
664     kcm_ccache ccache;
665     char *name;
666
667     ret = krb5_ret_stringz(request, &name);
668     if (ret)
669         return ret;
670
671     KCM_LOG_REQUEST_NAME(context, client, opcode, name);
672
673     ret = krb5_ret_uint32(request, &uid);
674     if (ret) {
675         free(name);
676         return ret;
677     }
678
679     ret = krb5_ret_uint32(request, &gid);
680     if (ret) {
681         free(name);
682         return ret;
683     }
684
685     ret = kcm_ccache_resolve_client(context, client, opcode,
686                                     name, &ccache);
687     if (ret) {
688         free(name);
689         return ret;
690     }
691
692     ret = kcm_chown(context, client, ccache, uid, gid);
693
694     free(name);
695     kcm_release_ccache(context, &ccache);
696
697     return ret;
698 }
699
700 /*
701  * Request:
702  *      NameZ
703  *      Mode
704  *
705  * Response:
706  *      
707  */
708 static krb5_error_code
709 kcm_op_chmod(krb5_context context,
710              kcm_client *client,
711              kcm_operation opcode,
712              krb5_storage *request,
713              krb5_storage *response)
714 {
715     uint16_t mode;
716     krb5_error_code ret;
717     kcm_ccache ccache;
718     char *name;
719
720     ret = krb5_ret_stringz(request, &name);
721     if (ret)
722         return ret;
723
724     KCM_LOG_REQUEST_NAME(context, client, opcode, name);
725
726     ret = krb5_ret_uint16(request, &mode);
727     if (ret) {
728         free(name);
729         return ret;
730     }
731
732     ret = kcm_ccache_resolve_client(context, client, opcode,
733                                     name, &ccache);
734     if (ret) {
735         free(name);
736         return ret;
737     }
738
739     ret = kcm_chmod(context, client, ccache, mode);
740
741     free(name);
742     kcm_release_ccache(context, &ccache);
743
744     return ret;
745 }
746
747 /*
748  * Protocol extensions for moving ticket acquisition responsibility
749  * from client to KCM follow.
750  */
751
752 /*
753  * Request:
754  *      NameZ
755  *      ServerPrincipalPresent
756  *      ServerPrincipal OPTIONAL
757  *      Key
758  *
759  * Repsonse:
760  *
761  */
762 static krb5_error_code
763 kcm_op_get_initial_ticket(krb5_context context,
764                           kcm_client *client,
765                           kcm_operation opcode,
766                           krb5_storage *request,
767                           krb5_storage *response)
768 {
769     krb5_error_code ret;
770     kcm_ccache ccache;
771     char *name;
772     int8_t not_tgt = 0;
773     krb5_principal server = NULL;
774     krb5_keyblock key;
775
776     krb5_keyblock_zero(&key);
777
778     ret = krb5_ret_stringz(request, &name);
779     if (ret)
780         return ret;
781
782     KCM_LOG_REQUEST_NAME(context, client, opcode, name);
783
784     ret = krb5_ret_int8(request, &not_tgt);
785     if (ret) {
786         free(name);
787         return ret;
788     }
789
790     if (not_tgt) {
791         ret = krb5_ret_principal(request, &server);
792         if (ret) {
793             free(name);
794             return ret;
795         }
796     }
797
798     ret = krb5_ret_keyblock(request, &key);
799     if (ret) {
800         free(name);
801         if (server != NULL)
802             krb5_free_principal(context, server);
803         return ret;
804     }
805
806     ret = kcm_ccache_resolve_client(context, client, opcode,
807                                     name, &ccache);
808     if (ret == 0) {
809         HEIMDAL_MUTEX_lock(&ccache->mutex);
810
811         if (ccache->server != NULL) {
812             krb5_free_principal(context, ccache->server);
813             ccache->server = NULL;
814         }
815
816         krb5_free_keyblock(context, &ccache->key.keyblock);
817
818         ccache->server = server;
819         ccache->key.keyblock = key;
820         ccache->flags |= KCM_FLAGS_USE_CACHED_KEY;
821
822         ret = kcm_ccache_enqueue_default(context, ccache, NULL);
823         if (ret) {
824             ccache->server = NULL;
825             krb5_keyblock_zero(&ccache->key.keyblock);
826             ccache->flags &= ~(KCM_FLAGS_USE_CACHED_KEY);
827         }
828
829         HEIMDAL_MUTEX_unlock(&ccache->mutex);
830     }
831
832     free(name);
833
834     if (ret != 0) {
835         krb5_free_principal(context, server);
836         krb5_free_keyblock(context, &key);
837     }
838
839     kcm_release_ccache(context, &ccache);
840
841     return ret;
842 }
843
844 /*
845  * Request:
846  *      NameZ
847  *      ServerPrincipal
848  *      KDCFlags
849  *      EncryptionType
850  *
851  * Repsonse:
852  *
853  */
854 static krb5_error_code
855 kcm_op_get_ticket(krb5_context context,
856                   kcm_client *client,
857                   kcm_operation opcode,
858                   krb5_storage *request,
859                   krb5_storage *response)
860 {
861     krb5_error_code ret;
862     kcm_ccache ccache;
863     char *name;
864     krb5_principal server = NULL;
865     krb5_ccache_data ccdata;
866     krb5_creds in, *out;
867     krb5_kdc_flags flags;
868
869     memset(&in, 0, sizeof(in));
870
871     ret = krb5_ret_stringz(request, &name);
872     if (ret)
873         return ret;
874
875     KCM_LOG_REQUEST_NAME(context, client, opcode, name);
876
877     ret = krb5_ret_uint32(request, &flags.i);
878     if (ret) {
879         free(name);
880         return ret;
881     }
882
883     ret = krb5_ret_int32(request, &in.session.keytype);
884     if (ret) {
885         free(name);
886         return ret;
887     }
888
889     ret = krb5_ret_principal(request, &server);
890     if (ret) {
891         free(name);
892         return ret;
893     }
894
895     ret = kcm_ccache_resolve_client(context, client, opcode,
896                                     name, &ccache);
897     if (ret) {
898         krb5_free_principal(context, server);
899         free(name);
900         return ret;
901     }
902
903     HEIMDAL_MUTEX_lock(&ccache->mutex);
904
905     /* Fake up an internal ccache */
906     kcm_internal_ccache(context, ccache, &ccdata);
907
908     in.client = ccache->client;
909     in.server = server;
910     in.times.endtime = 0;
911
912     /* glue cc layer will store creds */
913     ret = krb5_get_credentials_with_flags(context, 0, flags,
914                                           &ccdata, &in, &out);
915
916     HEIMDAL_MUTEX_unlock(&ccache->mutex);
917
918     if (ret == 0)
919         krb5_free_cred_contents(context, out);
920
921     free(name);
922
923     return ret;
924 }
925
926 /*
927  * Request:
928  *      OldNameZ
929  *      NewNameZ
930  *
931  * Repsonse:
932  *
933  */
934 static krb5_error_code
935 kcm_op_move_cache(krb5_context context,
936                   kcm_client *client,
937                   kcm_operation opcode,
938                   krb5_storage *request,
939                   krb5_storage *response)
940 {
941     krb5_error_code ret;
942     kcm_ccache oldid, newid;
943     char *oldname, *newname;
944
945     ret = krb5_ret_stringz(request, &oldname);
946     if (ret)
947         return ret;
948
949     KCM_LOG_REQUEST_NAME(context, client, opcode, oldname);
950
951     ret = krb5_ret_stringz(request, &newname);
952     if (ret) {
953         free(oldname);
954         return ret;
955     }
956
957     ret = kcm_ccache_resolve_client(context, client, opcode, oldname, &oldid);
958     if (ret) {
959         free(oldname);
960         free(newname);
961         return ret;
962     }
963
964     /* Check if new credential cache exists, if not create one. */
965     ret = kcm_ccache_resolve_client(context, client, opcode, newname, &newid);
966     if (ret == KRB5_FCC_NOFILE)
967         ret = kcm_ccache_new_client(context, client, newname, &newid);
968     free(newname);
969
970     if (ret) {
971         free(oldname);
972         kcm_release_ccache(context, &oldid);
973         return ret;
974     }
975
976     HEIMDAL_MUTEX_lock(&oldid->mutex);
977     HEIMDAL_MUTEX_lock(&newid->mutex);
978
979     /* move content */
980     {
981         kcm_ccache_data tmp;
982
983 #define MOVE(n,o,f) { tmp.f = n->f ; n->f = o->f; o->f = tmp.f; }
984
985         MOVE(newid, oldid, flags);
986         MOVE(newid, oldid, client);
987         MOVE(newid, oldid, server);
988         MOVE(newid, oldid, creds);
989         MOVE(newid, oldid, tkt_life);
990         MOVE(newid, oldid, renew_life);
991         MOVE(newid, oldid, key);
992         MOVE(newid, oldid, key);
993 #undef MOVE
994     }
995
996     HEIMDAL_MUTEX_unlock(&oldid->mutex);
997     HEIMDAL_MUTEX_unlock(&newid->mutex);
998
999     kcm_release_ccache(context, &oldid);
1000     kcm_release_ccache(context, &newid);
1001
1002     ret = kcm_ccache_destroy_client(context, client, oldname);
1003
1004     free(oldname);
1005
1006     return ret;
1007 }
1008
1009
1010 static struct kcm_op kcm_ops[] = {
1011     { "NOOP",                   kcm_op_noop },
1012     { "GET_NAME",               kcm_op_get_name },
1013     { "RESOLVE",                kcm_op_noop },
1014     { "GEN_NEW",                kcm_op_gen_new },
1015     { "INITIALIZE",             kcm_op_initialize },
1016     { "DESTROY",                kcm_op_destroy },
1017     { "STORE",                  kcm_op_store },
1018     { "RETRIEVE",               kcm_op_retrieve },
1019     { "GET_PRINCIPAL",          kcm_op_get_principal },
1020     { "GET_FIRST",              kcm_op_get_first },
1021     { "GET_NEXT",               kcm_op_get_next },
1022     { "END_GET",                kcm_op_end_get },
1023     { "REMOVE_CRED",            kcm_op_remove_cred },
1024     { "SET_FLAGS",              kcm_op_set_flags },
1025     { "CHOWN",                  kcm_op_chown },
1026     { "CHMOD",                  kcm_op_chmod },
1027     { "GET_INITIAL_TICKET",     kcm_op_get_initial_ticket },
1028     { "GET_TICKET",             kcm_op_get_ticket },
1029     { "MOVE_CACHE",             kcm_op_move_cache }
1030 };
1031
1032
1033 const char *kcm_op2string(kcm_operation opcode)
1034 {
1035     if (opcode >= sizeof(kcm_ops)/sizeof(kcm_ops[0]))
1036         return "Unknown operation";
1037
1038     return kcm_ops[opcode].name;
1039 }
1040
1041 krb5_error_code
1042 kcm_dispatch(krb5_context context,
1043              kcm_client *client,
1044              krb5_data *req_data,
1045              krb5_data *resp_data)
1046 {
1047     krb5_error_code ret;
1048     kcm_method method;
1049     krb5_storage *req_sp = NULL;
1050     krb5_storage *resp_sp = NULL;
1051     uint16_t opcode;
1052
1053     resp_sp = krb5_storage_emem();
1054     if (resp_sp == NULL) {
1055         return ENOMEM;
1056     }
1057
1058     if (client->pid == -1) {
1059         kcm_log(0, "Client had invalid process number");
1060         ret = KRB5_FCC_INTERNAL;
1061         goto out;
1062     }
1063
1064     req_sp = krb5_storage_from_data(req_data);
1065     if (req_sp == NULL) {
1066         kcm_log(0, "Process %d: failed to initialize storage from data",
1067                 client->pid);
1068         ret = KRB5_CC_IO;
1069         goto out;
1070     }
1071
1072     ret = krb5_ret_uint16(req_sp, &opcode);
1073     if (ret) {
1074         kcm_log(0, "Process %d: didn't send a message", client->pid);
1075         goto out;
1076     }
1077
1078     if (opcode >= sizeof(kcm_ops)/sizeof(kcm_ops[0])) {
1079         kcm_log(0, "Process %d: invalid operation code %d",
1080                 client->pid, opcode);
1081         ret = KRB5_FCC_INTERNAL;
1082         goto out;
1083     }
1084     method = kcm_ops[opcode].method;
1085
1086     /* seek past place for status code */
1087     krb5_storage_seek(resp_sp, 4, SEEK_SET);
1088
1089     ret = (*method)(context, client, opcode, req_sp, resp_sp);
1090
1091 out:
1092     if (req_sp != NULL) {
1093         krb5_storage_free(req_sp);
1094     }
1095
1096     krb5_storage_seek(resp_sp, 0, SEEK_SET);
1097     krb5_store_int32(resp_sp, ret);
1098
1099     ret = krb5_storage_to_data(resp_sp, resp_data);
1100     krb5_storage_free(resp_sp);
1101
1102     return ret;
1103 }
1104