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
)
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
);
961 static krb5_error_code
962 kcm_op_move_cache(krb5_context context
,
964 kcm_operation opcode
,
965 krb5_storage
*request
,
966 krb5_storage
*response
)
969 kcm_ccache oldid
, newid
;
970 char *oldname
, *newname
;
972 ret
= krb5_ret_stringz(request
, &oldname
);
976 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, oldname
);
978 ret
= krb5_ret_stringz(request
, &newname
);
984 ret
= kcm_ccache_resolve_client(context
, client
, opcode
, oldname
, &oldid
);
991 /* Check if new credential cache exists, if not create one. */
992 ret
= kcm_ccache_resolve_client(context
, client
, opcode
, newname
, &newid
);
993 if (ret
== KRB5_FCC_NOFILE
)
994 ret
= kcm_ccache_new_client(context
, client
, newname
, &newid
);
999 kcm_release_ccache(context
, &oldid
);
1003 HEIMDAL_MUTEX_lock(&oldid
->mutex
);
1004 HEIMDAL_MUTEX_lock(&newid
->mutex
);
1008 kcm_ccache_data tmp
;
1010 #define MOVE(n,o,f) { tmp.f = n->f ; n->f = o->f; o->f = tmp.f; }
1012 MOVE(newid
, oldid
, flags
);
1013 MOVE(newid
, oldid
, client
);
1014 MOVE(newid
, oldid
, server
);
1015 MOVE(newid
, oldid
, creds
);
1016 MOVE(newid
, oldid
, tkt_life
);
1017 MOVE(newid
, oldid
, renew_life
);
1018 MOVE(newid
, oldid
, key
);
1019 MOVE(newid
, oldid
, key
);
1023 HEIMDAL_MUTEX_unlock(&oldid
->mutex
);
1024 HEIMDAL_MUTEX_unlock(&newid
->mutex
);
1026 kcm_release_ccache(context
, &oldid
);
1027 kcm_release_ccache(context
, &newid
);
1029 ret
= kcm_ccache_destroy_client(context
, client
, oldname
);
1037 static struct kcm_op kcm_ops
[] = {
1038 { "NOOP", kcm_op_noop
},
1039 { "GET_NAME", kcm_op_get_name
},
1040 { "RESOLVE", kcm_op_noop
},
1041 { "GEN_NEW", kcm_op_gen_new
},
1042 { "INITIALIZE", kcm_op_initialize
},
1043 { "DESTROY", kcm_op_destroy
},
1044 { "STORE", kcm_op_store
},
1045 { "RETRIEVE", kcm_op_retrieve
},
1046 { "GET_PRINCIPAL", kcm_op_get_principal
},
1047 { "GET_FIRST", kcm_op_get_first
},
1048 { "GET_NEXT", kcm_op_get_next
},
1049 { "END_GET", kcm_op_end_get
},
1050 { "REMOVE_CRED", kcm_op_remove_cred
},
1051 { "SET_FLAGS", kcm_op_set_flags
},
1052 { "CHOWN", kcm_op_chown
},
1053 { "CHMOD", kcm_op_chmod
},
1054 { "GET_INITIAL_TICKET", kcm_op_get_initial_ticket
},
1055 { "GET_TICKET", kcm_op_get_ticket
},
1056 { "MOVE_CACHE", kcm_op_move_cache
}
1060 const char *kcm_op2string(kcm_operation opcode
)
1062 if (opcode
>= sizeof(kcm_ops
)/sizeof(kcm_ops
[0]))
1063 return "Unknown operation";
1065 return kcm_ops
[opcode
].name
;
1069 kcm_dispatch(krb5_context context
,
1071 krb5_data
*req_data
,
1072 krb5_data
*resp_data
)
1074 krb5_error_code ret
;
1076 krb5_storage
*req_sp
= NULL
;
1077 krb5_storage
*resp_sp
= NULL
;
1080 resp_sp
= krb5_storage_emem();
1081 if (resp_sp
== NULL
) {
1085 if (client
->pid
== -1) {
1086 kcm_log(0, "Client had invalid process number");
1087 ret
= KRB5_FCC_INTERNAL
;
1091 req_sp
= krb5_storage_from_data(req_data
);
1092 if (req_sp
== NULL
) {
1093 kcm_log(0, "Process %d: failed to initialize storage from data",
1099 ret
= krb5_ret_uint16(req_sp
, &opcode
);
1101 kcm_log(0, "Process %d: didn't send a message", client
->pid
);
1105 if (opcode
>= sizeof(kcm_ops
)/sizeof(kcm_ops
[0])) {
1106 kcm_log(0, "Process %d: invalid operation code %d",
1107 client
->pid
, opcode
);
1108 ret
= KRB5_FCC_INTERNAL
;
1111 method
= kcm_ops
[opcode
].method
;
1113 /* seek past place for status code */
1114 krb5_storage_seek(resp_sp
, 4, SEEK_SET
);
1116 ret
= (*method
)(context
, client
, opcode
, req_sp
, resp_sp
);
1119 if (req_sp
!= NULL
) {
1120 krb5_storage_free(req_sp
);
1123 krb5_storage_seek(resp_sp
, 0, SEEK_SET
);
1124 krb5_store_int32(resp_sp
, ret
);
1126 ret
= krb5_storage_to_data(resp_sp
, resp_data
);
1127 krb5_storage_free(resp_sp
);