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
);