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
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)
425 struct kcm_creds *creds;
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,
442 for (creds = ccache->creds ; creds ; creds = creds->next) {
444 sret = krb5_storage_write(response, &creds->uuid, sizeof(creds->uuid));
445 if (sret != sizeof(creds->uuid)) {
451 kcm_release_ccache(context, &ccache);
464 static krb5_error_code
465 kcm_op_get_next(krb5_context context,
467 kcm_operation opcode,
468 krb5_storage *request,
469 krb5_storage *response)
478 ret = krb5_ret_stringz(request, &name);
482 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
484 ret = kcm_ccache_resolve_client(context, client, opcode,
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);
497 c = kcm_ccache_find_cred_uuid(context, ccache, uuid);
499 kcm_release_ccache(context, &ccache);
503 HEIMDAL_MUTEX_lock(&ccache->mutex);
504 ret = krb5_store_creds(response, &c->cred);
505 HEIMDAL_MUTEX_unlock(&ccache->mutex);
507 kcm_release_ccache(context, &ccache);
520 static krb5_error_code
521 kcm_op_end_get(krb5_context context,
523 kcm_operation opcode,
524 krb5_storage *request,
525 krb5_storage *response)
530 ret = krb5_ret_stringz(request, &name);
534 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
549 static krb5_error_code
550 kcm_op_remove_cred(krb5_context context,
552 kcm_operation opcode,
553 krb5_storage *request,
554 krb5_storage *response)
556 uint32_t whichfields;
562 ret = krb5_ret_stringz(request, &name);
566 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
568 ret = krb5_ret_uint32(request, &whichfields);
574 ret = krb5_ret_creds_tag(request, &mcreds);
580 ret = kcm_ccache_resolve_client(context, client, opcode,
584 krb5_free_cred_contents(context, &mcreds);
588 ret = kcm_ccache_remove_cred(context, ccache, whichfields, &mcreds);
590 /* XXX need to remove any events that match */
593 krb5_free_cred_contents(context, &mcreds);
594 kcm_release_ccache(context, &ccache);
607 static krb5_error_code
608 kcm_op_set_flags(krb5_context context,
610 kcm_operation opcode,
611 krb5_storage *request,
612 krb5_storage *response)
619 ret = krb5_ret_stringz(request, &name);
623 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
625 ret = krb5_ret_uint32(request, &flags);
631 ret = kcm_ccache_resolve_client(context, client, opcode,
638 /* we don't really support any flags yet */
640 kcm_release_ccache(context, &ccache);
654 static krb5_error_code
655 kcm_op_chown(krb5_context context,
657 kcm_operation opcode,
658 krb5_storage *request,
659 krb5_storage *response)
667 ret = krb5_ret_stringz(request, &name);
671 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
673 ret = krb5_ret_uint32(request, &uid);
679 ret = krb5_ret_uint32(request, &gid);
685 ret = kcm_ccache_resolve_client(context, client, opcode,
692 ret = kcm_chown(context, client, ccache, uid, gid);
695 kcm_release_ccache(context, &ccache);
708 static krb5_error_code
709 kcm_op_chmod(krb5_context context,
711 kcm_operation opcode,
712 krb5_storage *request,
713 krb5_storage *response)
720 ret = krb5_ret_stringz(request, &name);
724 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
726 ret = krb5_ret_uint16(request, &mode);
732 ret = kcm_ccache_resolve_client(context, client, opcode,
739 ret = kcm_chmod(context, client, ccache, mode);
742 kcm_release_ccache(context, &ccache);
748 * Protocol extensions for moving ticket acquisition responsibility
749 * from client to KCM follow.
755 * ServerPrincipalPresent
756 * ServerPrincipal OPTIONAL
762 static krb5_error_code
763 kcm_op_get_initial_ticket(krb5_context context,
765 kcm_operation opcode,
766 krb5_storage *request,
767 krb5_storage *response)
773 krb5_principal server = NULL;
776 krb5_keyblock_zero(&key);
778 ret = krb5_ret_stringz(request, &name);
782 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
784 ret = krb5_ret_int8(request, ¬_tgt);
791 ret = krb5_ret_principal(request, &server);
798 ret = krb5_ret_keyblock(request, &key);
802 krb5_free_principal(context, server);
806 ret = kcm_ccache_resolve_client(context, client, opcode,
809 HEIMDAL_MUTEX_lock(&ccache->mutex);
811 if (ccache->server != NULL) {
812 krb5_free_principal(context, ccache->server);
813 ccache->server = NULL;
816 krb5_free_keyblock(context, &ccache->key.keyblock);
818 ccache->server = server;
819 ccache->key.keyblock = key;
820 ccache->flags |= KCM_FLAGS_USE_CACHED_KEY;
822 ret = kcm_ccache_enqueue_default(context, ccache, NULL);
824 ccache->server = NULL;
825 krb5_keyblock_zero(&ccache->key.keyblock);
826 ccache->flags &= ~(KCM_FLAGS_USE_CACHED_KEY);
829 HEIMDAL_MUTEX_unlock(&ccache->mutex);
835 krb5_free_principal(context, server);
836 krb5_free_keyblock(context, &key);
839 kcm_release_ccache(context, &ccache);
854 static krb5_error_code
855 kcm_op_get_ticket(krb5_context context,
857 kcm_operation opcode,
858 krb5_storage *request,
859 krb5_storage *response)
864 krb5_principal server = NULL;
865 krb5_ccache_data ccdata;
867 krb5_kdc_flags flags;
869 memset(&in, 0, sizeof(in));
871 ret = krb5_ret_stringz(request, &name);
875 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
877 ret = krb5_ret_uint32(request, &flags.i);
883 ret = krb5_ret_int32(request, &in.session.keytype);
889 ret = krb5_ret_principal(request, &server);
895 ret = kcm_ccache_resolve_client(context, client, opcode,
898 krb5_free_principal(context, server);
903 HEIMDAL_MUTEX_lock(&ccache->mutex);
905 /* Fake up an internal ccache */
906 kcm_internal_ccache(context, ccache, &ccdata);
908 in.client = ccache->client;
910 in.times.endtime = 0;
912 /* glue cc layer will store creds */
913 ret = krb5_get_credentials_with_flags(context, 0, flags,
916 HEIMDAL_MUTEX_unlock(&ccache->mutex);
919 krb5_free_cred_contents(context, out);
934 static krb5_error_code
935 kcm_op_move_cache(krb5_context context,
937 kcm_operation opcode,
938 krb5_storage *request,
939 krb5_storage *response)
942 kcm_ccache oldid, newid;
943 char *oldname, *newname;
945 ret = krb5_ret_stringz(request, &oldname);
949 KCM_LOG_REQUEST_NAME(context, client, opcode, oldname);
951 ret = krb5_ret_stringz(request, &newname);
957 ret = kcm_ccache_resolve_client(context, client, opcode, oldname, &oldid);
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);
972 kcm_release_ccache(context, &oldid);
976 HEIMDAL_MUTEX_lock(&oldid->mutex);
977 HEIMDAL_MUTEX_lock(&newid->mutex);
983 #define MOVE(n,o,f) { tmp.f = n->f ; n->f = o->f; o->f = tmp.f; }
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);
996 HEIMDAL_MUTEX_unlock(&oldid->mutex);
997 HEIMDAL_MUTEX_unlock(&newid->mutex);
999 kcm_release_ccache(context, &oldid);
1000 kcm_release_ccache(context, &newid);
1002 ret = kcm_ccache_destroy_client(context, client, oldname);
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 }
1033 const char *kcm_op2string(kcm_operation opcode)
1035 if (opcode >= sizeof(kcm_ops)/sizeof(kcm_ops[0]))
1036 return "Unknown operation";
1038 return kcm_ops[opcode].name;
1042 kcm_dispatch(krb5_context context,
1044 krb5_data *req_data,
1045 krb5_data *resp_data)
1047 krb5_error_code ret;
1049 krb5_storage *req_sp = NULL;
1050 krb5_storage *resp_sp = NULL;
1053 resp_sp = krb5_storage_emem();
1054 if (resp_sp == NULL) {
1058 if (client->pid == -1) {
1059 kcm_log(0, "Client had invalid process number");
1060 ret = KRB5_FCC_INTERNAL;
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",
1072 ret = krb5_ret_uint16(req_sp, &opcode);
1074 kcm_log(0, "Process %d: didn't send a message", client->pid);
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;
1084 method = kcm_ops[opcode].method;
1086 /* seek past place for status code */
1087 krb5_storage_seek(resp_sp, 4, SEEK_SET);
1089 ret = (*method)(context, client, opcode, req_sp, resp_sp);
1092 if (req_sp != NULL) {
1093 krb5_storage_free(req_sp);
1096 krb5_storage_seek(resp_sp, 0, SEEK_SET);
1097 krb5_store_int32(resp_sp, ret);
1099 ret = krb5_storage_to_data(resp_sp, resp_data);
1100 krb5_storage_free(resp_sp);