2 * Copyright (c) 2005, PADL Software Pty Ltd.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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
35 RCSID("$Id: protocol.c 22112 2007-12-03 19:34:33Z lha $");
37 static krb5_error_code
38 kcm_op_noop(krb5_context context,
41 krb5_storage *request,
42 krb5_storage *response)
44 KCM_LOG_REQUEST(context, client, opcode);
56 static krb5_error_code
57 kcm_op_get_name(krb5_context context,
60 krb5_storage *request,
61 krb5_storage *response)
68 ret = krb5_ret_stringz(request, &name);
72 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
74 ret = kcm_ccache_resolve_client(context, client, opcode,
81 ret = krb5_store_stringz(response, ccache->name);
83 kcm_release_ccache(context, &ccache);
89 kcm_release_ccache(context, &ccache);
99 static krb5_error_code
100 kcm_op_gen_new(krb5_context context,
102 kcm_operation opcode,
103 krb5_storage *request,
104 krb5_storage *response)
109 KCM_LOG_REQUEST(context, client, opcode);
111 name = kcm_ccache_nextid(client->pid, client->uid, client->gid);
113 return KRB5_CC_NOMEM;
116 ret = krb5_store_stringz(response, name);
130 static krb5_error_code
131 kcm_op_initialize(krb5_context context,
133 kcm_operation opcode,
134 krb5_storage *request,
135 krb5_storage *response)
138 krb5_principal principal;
145 KCM_LOG_REQUEST(context, client, opcode);
147 ret = krb5_ret_stringz(request, &name);
151 ret = krb5_ret_principal(request, &principal);
157 ret = kcm_ccache_new_client(context, client, name, &ccache);
160 krb5_free_principal(context, principal);
164 ccache->client = principal;
170 * Create a new credentials cache. To mitigate DoS attacks we will
171 * expire it in 30 minutes unless it has some credentials added
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);
184 kcm_release_ccache(context, &ccache);
196 static krb5_error_code
197 kcm_op_destroy(krb5_context context,
199 kcm_operation opcode,
200 krb5_storage *request,
201 krb5_storage *response)
206 ret = krb5_ret_stringz(request, &name);
210 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
212 ret = kcm_ccache_destroy_client(context, client, name);
227 static krb5_error_code
228 kcm_op_store(krb5_context context,
230 kcm_operation opcode,
231 krb5_storage *request,
232 krb5_storage *response)
239 ret = krb5_ret_stringz(request, &name);
243 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
245 ret = krb5_ret_creds(request, &creds);
251 ret = kcm_ccache_resolve_client(context, client, opcode,
255 krb5_free_cred_contents(context, &creds);
259 ret = kcm_ccache_store_cred(context, ccache, &creds, 0);
262 krb5_free_cred_contents(context, &creds);
263 kcm_release_ccache(context, &ccache);
267 kcm_ccache_enqueue_default(context, ccache, &creds);
270 kcm_release_ccache(context, &ccache);
285 static krb5_error_code
286 kcm_op_retrieve(krb5_context context,
288 kcm_operation opcode,
289 krb5_storage *request,
290 krb5_storage *response)
300 ret = krb5_ret_stringz(request, &name);
304 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
306 ret = krb5_ret_uint32(request, &flags);
312 ret = krb5_ret_creds_tag(request, &mcreds);
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)
323 krb5_free_cred_contents(context, &mcreds);
324 return KRB5_FCC_PERM;
327 ret = kcm_ccache_resolve_client(context, client, opcode,
331 krb5_free_cred_contents(context, &mcreds);
335 ret = kcm_ccache_retrieve_cred(context, ccache, flags,
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);
351 HEIMDAL_MUTEX_unlock(&ccache->mutex);
355 ret = krb5_store_creds(response, credp);
359 krb5_free_cred_contents(context, &mcreds);
360 kcm_release_ccache(context, &ccache);
363 krb5_free_cred_contents(context, credp);
375 static krb5_error_code
376 kcm_op_get_principal(krb5_context context,
378 kcm_operation opcode,
379 krb5_storage *request,
380 krb5_storage *response)
386 ret = krb5_ret_stringz(request, &name);
390 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
392 ret = kcm_ccache_resolve_client(context, client, opcode,
399 if (ccache->client == NULL)
400 ret = KRB5_CC_NOTFOUND;
402 ret = krb5_store_principal(response, ccache->client);
405 kcm_release_ccache(context, &ccache);
418 static krb5_error_code
419 kcm_op_get_first(krb5_context context,
421 kcm_operation opcode,
422 krb5_storage *request,
423 krb5_storage *response)
430 ret = krb5_ret_stringz(request, &name);
434 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
436 ret = kcm_ccache_resolve_client(context, client, opcode,
443 ret = kcm_cursor_new(context, client->pid, ccache, &cursor);
445 kcm_release_ccache(context, &ccache);
450 ret = krb5_store_int32(response, cursor);
453 kcm_release_ccache(context, &ccache);
466 static krb5_error_code
467 kcm_op_get_next(krb5_context context,
469 kcm_operation opcode,
470 krb5_storage *request,
471 krb5_storage *response)
479 ret = krb5_ret_stringz(request, &name);
483 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
485 ret = krb5_ret_uint32(request, &cursor);
491 ret = kcm_ccache_resolve_client(context, client, opcode,
498 ret = kcm_cursor_find(context, client->pid, ccache, cursor, &c);
500 kcm_release_ccache(context, &ccache);
505 HEIMDAL_MUTEX_lock(&ccache->mutex);
506 if (c->credp == NULL) {
509 ret = krb5_store_creds(response, &c->credp->cred);
510 c->credp = c->credp->next;
512 HEIMDAL_MUTEX_unlock(&ccache->mutex);
515 kcm_release_ccache(context, &ccache);
528 static krb5_error_code
529 kcm_op_end_get(krb5_context context,
531 kcm_operation opcode,
532 krb5_storage *request,
533 krb5_storage *response)
540 ret = krb5_ret_stringz(request, &name);
544 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
546 ret = krb5_ret_uint32(request, &cursor);
552 ret = kcm_ccache_resolve_client(context, client, opcode,
559 ret = kcm_cursor_delete(context, client->pid, ccache, cursor);
562 kcm_release_ccache(context, &ccache);
576 static krb5_error_code
577 kcm_op_remove_cred(krb5_context context,
579 kcm_operation opcode,
580 krb5_storage *request,
581 krb5_storage *response)
583 uint32_t whichfields;
589 ret = krb5_ret_stringz(request, &name);
593 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
595 ret = krb5_ret_uint32(request, &whichfields);
601 ret = krb5_ret_creds_tag(request, &mcreds);
607 ret = kcm_ccache_resolve_client(context, client, opcode,
611 krb5_free_cred_contents(context, &mcreds);
615 ret = kcm_ccache_remove_cred(context, ccache, whichfields, &mcreds);
617 /* XXX need to remove any events that match */
620 krb5_free_cred_contents(context, &mcreds);
621 kcm_release_ccache(context, &ccache);
634 static krb5_error_code
635 kcm_op_set_flags(krb5_context context,
637 kcm_operation opcode,
638 krb5_storage *request,
639 krb5_storage *response)
646 ret = krb5_ret_stringz(request, &name);
650 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
652 ret = krb5_ret_uint32(request, &flags);
658 ret = kcm_ccache_resolve_client(context, client, opcode,
665 /* we don't really support any flags yet */
667 kcm_release_ccache(context, &ccache);
681 static krb5_error_code
682 kcm_op_chown(krb5_context context,
684 kcm_operation opcode,
685 krb5_storage *request,
686 krb5_storage *response)
694 ret = krb5_ret_stringz(request, &name);
698 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
700 ret = krb5_ret_uint32(request, &uid);
706 ret = krb5_ret_uint32(request, &gid);
712 ret = kcm_ccache_resolve_client(context, client, opcode,
719 ret = kcm_chown(context, client, ccache, uid, gid);
722 kcm_release_ccache(context, &ccache);
735 static krb5_error_code
736 kcm_op_chmod(krb5_context context,
738 kcm_operation opcode,
739 krb5_storage *request,
740 krb5_storage *response)
747 ret = krb5_ret_stringz(request, &name);
751 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
753 ret = krb5_ret_uint16(request, &mode);
759 ret = kcm_ccache_resolve_client(context, client, opcode,
766 ret = kcm_chmod(context, client, ccache, mode);
769 kcm_release_ccache(context, &ccache);
775 * Protocol extensions for moving ticket acquisition responsibility
776 * from client to KCM follow.
782 * ServerPrincipalPresent
783 * ServerPrincipal OPTIONAL
789 static krb5_error_code
790 kcm_op_get_initial_ticket(krb5_context context,
792 kcm_operation opcode,
793 krb5_storage *request,
794 krb5_storage *response)
800 krb5_principal server = NULL;
803 krb5_keyblock_zero(&key);
805 ret = krb5_ret_stringz(request, &name);
809 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
811 ret = krb5_ret_int8(request, ¬_tgt);
818 ret = krb5_ret_principal(request, &server);
825 ret = krb5_ret_keyblock(request, &key);
829 krb5_free_principal(context, server);
833 ret = kcm_ccache_resolve_client(context, client, opcode,
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);
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);
862 krb5_free_principal(context, server);
863 krb5_free_keyblock(context, &key);
866 kcm_release_ccache(context, &ccache);
881 static krb5_error_code
882 kcm_op_get_ticket(krb5_context context,
884 kcm_operation opcode,
885 krb5_storage *request,
886 krb5_storage *response)
891 krb5_principal server = NULL;
892 krb5_ccache_data ccdata;
894 krb5_kdc_flags flags;
896 memset(&in, 0, sizeof(in));
898 ret = krb5_ret_stringz(request, &name);
902 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
904 ret = krb5_ret_uint32(request, &flags.i);
910 ret = krb5_ret_int32(request, &in.session.keytype);
916 ret = krb5_ret_principal(request, &server);
922 ret = kcm_ccache_resolve_client(context, client, opcode,
925 krb5_free_principal(context, server);
930 HEIMDAL_MUTEX_lock(&ccache->mutex);
932 /* Fake up an internal ccache */
933 kcm_internal_ccache(context, ccache, &ccdata);
935 in.client = ccache->client;
937 in.times.endtime = 0;
939 /* glue cc layer will store creds */
940 ret = krb5_get_credentials_with_flags(context, 0, flags,
943 HEIMDAL_MUTEX_unlock(&ccache->mutex);
946 krb5_free_cred_contents(context, out);
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;
984 kcm_dispatch(krb5_context context,
987 krb5_data *resp_data)
991 krb5_storage *req_sp = NULL;
992 krb5_storage *resp_sp = NULL;
995 resp_sp = krb5_storage_emem();
996 if (resp_sp == NULL) {
1000 if (client->pid == -1) {
1001 kcm_log(0, "Client had invalid process number");
1002 ret = KRB5_FCC_INTERNAL;
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",
1014 ret = krb5_ret_uint16(req_sp, &opcode);
1016 kcm_log(0, "Process %d: didn't send a message", client->pid);
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;
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);
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);