rename SoftToken to Heimdal hx509 SoftToken.
[heimdal.git] / kcm / protocol.c
blobe35ea6ebeffafaff8d2549cf2be1d8f4aa85b03d
1 /*
2 * Copyright (c) 2005, PADL Software Pty Ltd.
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
33 #include "kcm_locl.h"
35 RCSID("$Id$");
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)
44 KCM_LOG_REQUEST(context, client, opcode);
46 return 0;
50 * Request:
51 * NameZ
52 * Response:
53 * NameZ
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)
64 krb5_error_code ret;
65 char *name = NULL;
66 kcm_ccache ccache;
68 ret = krb5_ret_stringz(request, &name);
69 if (ret)
70 return ret;
72 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
74 ret = kcm_ccache_resolve_client(context, client, opcode,
75 name, &ccache);
76 if (ret) {
77 free(name);
78 return ret;
81 ret = krb5_store_stringz(response, ccache->name);
82 if (ret) {
83 kcm_release_ccache(context, &ccache);
84 free(name);
85 return ret;
88 free(name);
89 kcm_release_ccache(context, &ccache);
90 return 0;
94 * Request:
96 * Response:
97 * NameZ
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)
106 krb5_error_code ret;
107 char *name;
109 KCM_LOG_REQUEST(context, client, opcode);
111 name = kcm_ccache_nextid(client->pid, client->uid, client->gid);
112 if (name == NULL) {
113 return KRB5_CC_NOMEM;
116 ret = krb5_store_stringz(response, name);
117 free(name);
119 return ret;
123 * Request:
124 * NameZ
125 * Principal
127 * Response:
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)
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
145 KCM_LOG_REQUEST(context, client, opcode);
147 ret = krb5_ret_stringz(request, &name);
148 if (ret)
149 return ret;
151 ret = krb5_ret_principal(request, &principal);
152 if (ret) {
153 free(name);
154 return ret;
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;
164 ccache->client = principal;
166 free(name);
168 #if 0
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
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;
181 ret = kcm_enqueue_event_relative(context, &event);
182 #endif
184 kcm_release_ccache(context, &ccache);
186 return ret;
190 * Request:
191 * NameZ
193 * Response:
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)
203 krb5_error_code ret;
204 char *name;
206 ret = krb5_ret_stringz(request, &name);
207 if (ret)
208 return ret;
210 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
212 ret = kcm_ccache_destroy_client(context, client, name);
214 free(name);
216 return ret;
220 * Request:
221 * NameZ
222 * Creds
224 * Response:
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)
234 krb5_creds creds;
235 krb5_error_code ret;
236 kcm_ccache ccache;
237 char *name;
239 ret = krb5_ret_stringz(request, &name);
240 if (ret)
241 return ret;
243 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
245 ret = krb5_ret_creds(request, &creds);
246 if (ret) {
247 free(name);
248 return ret;
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;
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;
267 kcm_ccache_enqueue_default(context, ccache, &creds);
269 free(name);
270 kcm_release_ccache(context, &ccache);
272 return 0;
276 * Request:
277 * NameZ
278 * WhichFields
279 * MatchCreds
281 * Response:
282 * Creds
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)
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;
300 ret = krb5_ret_stringz(request, &name);
301 if (ret)
302 return ret;
304 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
306 ret = krb5_ret_uint32(request, &flags);
307 if (ret) {
308 free(name);
309 return ret;
312 ret = krb5_ret_creds_tag(request, &mcreds);
313 if (ret) {
314 free(name);
315 return ret;
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)
322 free(name);
323 krb5_free_cred_contents(context, &mcreds);
324 return KRB5_FCC_PERM;
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;
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;
340 /* try and acquire */
341 HEIMDAL_MUTEX_lock(&ccache->mutex);
343 /* Fake up an internal ccache */
344 kcm_internal_ccache(context, ccache, &ccdata);
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;
351 HEIMDAL_MUTEX_unlock(&ccache->mutex);
354 if (ret == 0) {
355 ret = krb5_store_creds(response, credp);
358 free(name);
359 krb5_free_cred_contents(context, &mcreds);
360 kcm_release_ccache(context, &ccache);
362 if (free_creds)
363 krb5_free_cred_contents(context, credp);
365 return ret;
369 * Request:
370 * NameZ
372 * Response:
373 * Principal
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)
382 krb5_error_code ret;
383 kcm_ccache ccache;
384 char *name;
386 ret = krb5_ret_stringz(request, &name);
387 if (ret)
388 return ret;
390 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
392 ret = kcm_ccache_resolve_client(context, client, opcode,
393 name, &ccache);
394 if (ret) {
395 free(name);
396 return ret;
399 if (ccache->client == NULL)
400 ret = KRB5_CC_NOTFOUND;
401 else
402 ret = krb5_store_principal(response, ccache->client);
404 free(name);
405 kcm_release_ccache(context, &ccache);
407 return 0;
411 * Request:
412 * NameZ
414 * Response:
415 * Cursor
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)
425 krb5_error_code ret;
426 kcm_ccache ccache;
427 uint32_t cursor;
428 char *name;
430 ret = krb5_ret_stringz(request, &name);
431 if (ret)
432 return ret;
434 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
436 ret = kcm_ccache_resolve_client(context, client, opcode,
437 name, &ccache);
438 if (ret) {
439 free(name);
440 return ret;
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;
450 ret = krb5_store_int32(response, cursor);
452 free(name);
453 kcm_release_ccache(context, &ccache);
455 return ret;
459 * Request:
460 * NameZ
461 * Cursor
463 * Response:
464 * Creds
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)
473 krb5_error_code ret;
474 kcm_ccache ccache;
475 char *name;
476 uint32_t cursor;
477 kcm_cursor *c;
479 ret = krb5_ret_stringz(request, &name);
480 if (ret)
481 return ret;
483 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
485 ret = krb5_ret_uint32(request, &cursor);
486 if (ret) {
487 free(name);
488 return ret;
491 ret = kcm_ccache_resolve_client(context, client, opcode,
492 name, &ccache);
493 if (ret) {
494 free(name);
495 return ret;
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;
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;
512 HEIMDAL_MUTEX_unlock(&ccache->mutex);
514 free(name);
515 kcm_release_ccache(context, &ccache);
517 return ret;
521 * Request:
522 * NameZ
523 * Cursor
525 * Response:
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)
535 krb5_error_code ret;
536 kcm_ccache ccache;
537 uint32_t cursor;
538 char *name;
540 ret = krb5_ret_stringz(request, &name);
541 if (ret)
542 return ret;
544 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
546 ret = krb5_ret_uint32(request, &cursor);
547 if (ret) {
548 free(name);
549 return ret;
552 ret = kcm_ccache_resolve_client(context, client, opcode,
553 name, &ccache);
554 if (ret) {
555 free(name);
556 return ret;
559 ret = kcm_cursor_delete(context, client->pid, ccache, cursor);
561 free(name);
562 kcm_release_ccache(context, &ccache);
564 return ret;
568 * Request:
569 * NameZ
570 * WhichFields
571 * MatchCreds
573 * Response:
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)
583 uint32_t whichfields;
584 krb5_creds mcreds;
585 krb5_error_code ret;
586 kcm_ccache ccache;
587 char *name;
589 ret = krb5_ret_stringz(request, &name);
590 if (ret)
591 return ret;
593 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
595 ret = krb5_ret_uint32(request, &whichfields);
596 if (ret) {
597 free(name);
598 return ret;
601 ret = krb5_ret_creds_tag(request, &mcreds);
602 if (ret) {
603 free(name);
604 return ret;
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;
615 ret = kcm_ccache_remove_cred(context, ccache, whichfields, &mcreds);
617 /* XXX need to remove any events that match */
619 free(name);
620 krb5_free_cred_contents(context, &mcreds);
621 kcm_release_ccache(context, &ccache);
623 return ret;
627 * Request:
628 * NameZ
629 * Flags
631 * Response:
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)
641 uint32_t flags;
642 krb5_error_code ret;
643 kcm_ccache ccache;
644 char *name;
646 ret = krb5_ret_stringz(request, &name);
647 if (ret)
648 return ret;
650 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
652 ret = krb5_ret_uint32(request, &flags);
653 if (ret) {
654 free(name);
655 return ret;
658 ret = kcm_ccache_resolve_client(context, client, opcode,
659 name, &ccache);
660 if (ret) {
661 free(name);
662 return ret;
665 /* we don't really support any flags yet */
666 free(name);
667 kcm_release_ccache(context, &ccache);
669 return 0;
673 * Request:
674 * NameZ
675 * UID
676 * GID
678 * Response:
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)
688 uint32_t uid;
689 uint32_t gid;
690 krb5_error_code ret;
691 kcm_ccache ccache;
692 char *name;
694 ret = krb5_ret_stringz(request, &name);
695 if (ret)
696 return ret;
698 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
700 ret = krb5_ret_uint32(request, &uid);
701 if (ret) {
702 free(name);
703 return ret;
706 ret = krb5_ret_uint32(request, &gid);
707 if (ret) {
708 free(name);
709 return ret;
712 ret = kcm_ccache_resolve_client(context, client, opcode,
713 name, &ccache);
714 if (ret) {
715 free(name);
716 return ret;
719 ret = kcm_chown(context, client, ccache, uid, gid);
721 free(name);
722 kcm_release_ccache(context, &ccache);
724 return ret;
728 * Request:
729 * NameZ
730 * Mode
732 * Response:
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)
742 uint16_t mode;
743 krb5_error_code ret;
744 kcm_ccache ccache;
745 char *name;
747 ret = krb5_ret_stringz(request, &name);
748 if (ret)
749 return ret;
751 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
753 ret = krb5_ret_uint16(request, &mode);
754 if (ret) {
755 free(name);
756 return ret;
759 ret = kcm_ccache_resolve_client(context, client, opcode,
760 name, &ccache);
761 if (ret) {
762 free(name);
763 return ret;
766 ret = kcm_chmod(context, client, ccache, mode);
768 free(name);
769 kcm_release_ccache(context, &ccache);
771 return ret;
775 * Protocol extensions for moving ticket acquisition responsibility
776 * from client to KCM follow.
780 * Request:
781 * NameZ
782 * ServerPrincipalPresent
783 * ServerPrincipal OPTIONAL
784 * Key
786 * Repsonse:
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)
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;
803 krb5_keyblock_zero(&key);
805 ret = krb5_ret_stringz(request, &name);
806 if (ret)
807 return ret;
809 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
811 ret = krb5_ret_int8(request, &not_tgt);
812 if (ret) {
813 free(name);
814 return ret;
817 if (not_tgt) {
818 ret = krb5_ret_principal(request, &server);
819 if (ret) {
820 free(name);
821 return ret;
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;
833 ret = kcm_ccache_resolve_client(context, client, opcode,
834 name, &ccache);
835 if (ret == 0) {
836 HEIMDAL_MUTEX_lock(&ccache->mutex);
838 if (ccache->server != NULL) {
839 krb5_free_principal(context, ccache->server);
840 ccache->server = NULL;
843 krb5_free_keyblock(context, &ccache->key.keyblock);
845 ccache->server = server;
846 ccache->key.keyblock = key;
847 ccache->flags |= KCM_FLAGS_USE_CACHED_KEY;
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);
856 HEIMDAL_MUTEX_unlock(&ccache->mutex);
859 free(name);
861 if (ret != 0) {
862 krb5_free_principal(context, server);
863 krb5_free_keyblock(context, &key);
866 kcm_release_ccache(context, &ccache);
868 return ret;
872 * Request:
873 * NameZ
874 * ServerPrincipal
875 * KDCFlags
876 * EncryptionType
878 * Repsonse:
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)
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;
896 memset(&in, 0, sizeof(in));
898 ret = krb5_ret_stringz(request, &name);
899 if (ret)
900 return ret;
902 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
904 ret = krb5_ret_uint32(request, &flags.i);
905 if (ret) {
906 free(name);
907 return ret;
910 ret = krb5_ret_int32(request, &in.session.keytype);
911 if (ret) {
912 free(name);
913 return ret;
916 ret = krb5_ret_principal(request, &server);
917 if (ret) {
918 free(name);
919 return ret;
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;
930 HEIMDAL_MUTEX_lock(&ccache->mutex);
932 /* Fake up an internal ccache */
933 kcm_internal_ccache(context, ccache, &ccdata);
935 in.client = ccache->client;
936 in.server = server;
937 in.times.endtime = 0;
939 /* glue cc layer will store creds */
940 ret = krb5_get_credentials_with_flags(context, 0, flags,
941 &ccdata, &in, &out);
943 HEIMDAL_MUTEX_unlock(&ccache->mutex);
945 if (ret == 0)
946 krb5_free_cred_contents(context, out);
948 free(name);
950 return ret;
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 }
975 const char *kcm_op2string(kcm_operation opcode)
977 if (opcode >= sizeof(kcm_ops)/sizeof(kcm_ops[0]))
978 return "Unknown operation";
980 return kcm_ops[opcode].name;
983 krb5_error_code
984 kcm_dispatch(krb5_context context,
985 kcm_client *client,
986 krb5_data *req_data,
987 krb5_data *resp_data)
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;
995 resp_sp = krb5_storage_emem();
996 if (resp_sp == NULL) {
997 return ENOMEM;
1000 if (client->pid == -1) {
1001 kcm_log(0, "Client had invalid process number");
1002 ret = KRB5_FCC_INTERNAL;
1003 goto out;
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;
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;
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;
1026 method = kcm_ops[opcode].method;
1028 /* seek past place for status code */
1029 krb5_storage_seek(resp_sp, 4, SEEK_SET);
1031 ret = (*method)(context, client, opcode, req_sp, resp_sp);
1033 out:
1034 if (req_sp != NULL) {
1035 krb5_storage_free(req_sp);
1038 krb5_storage_seek(resp_sp, 0, SEEK_SET);
1039 krb5_store_int32(resp_sp, ret);
1041 ret = krb5_storage_to_data(resp_sp, resp_data);
1042 krb5_storage_free(resp_sp);
1044 return ret;