]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - crypto/heimdal/kcm/protocol.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / crypto / heimdal / 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: 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