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("$Heimdal: protocol.c 22112 2007-12-03 19:34:33Z lha $"
38 static krb5_error_code
39 kcm_op_noop(krb5_context context
,
42 krb5_storage
*request
,
43 krb5_storage
*response
)
45 KCM_LOG_REQUEST(context
, client
, opcode
);
57 static krb5_error_code
58 kcm_op_get_name(krb5_context context
,
61 krb5_storage
*request
,
62 krb5_storage
*response
)
69 ret
= krb5_ret_stringz(request
, &name
);
73 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
75 ret
= kcm_ccache_resolve_client(context
, client
, opcode
,
82 ret
= krb5_store_stringz(response
, ccache
->name
);
84 kcm_release_ccache(context
, &ccache
);
90 kcm_release_ccache(context
, &ccache
);
100 static krb5_error_code
101 kcm_op_gen_new(krb5_context context
,
103 kcm_operation opcode
,
104 krb5_storage
*request
,
105 krb5_storage
*response
)
110 KCM_LOG_REQUEST(context
, client
, opcode
);
112 name
= kcm_ccache_nextid(client
->pid
, client
->uid
, client
->gid
);
114 return KRB5_CC_NOMEM
;
117 ret
= krb5_store_stringz(response
, name
);
131 static krb5_error_code
132 kcm_op_initialize(krb5_context context
,
134 kcm_operation opcode
,
135 krb5_storage
*request
,
136 krb5_storage
*response
)
139 krb5_principal principal
;
146 KCM_LOG_REQUEST(context
, client
, opcode
);
148 ret
= krb5_ret_stringz(request
, &name
);
152 ret
= krb5_ret_principal(request
, &principal
);
158 ret
= kcm_ccache_new_client(context
, client
, name
, &ccache
);
161 krb5_free_principal(context
, principal
);
165 ccache
->client
= principal
;
171 * Create a new credentials cache. To mitigate DoS attacks we will
172 * expire it in 30 minutes unless it has some credentials added
176 event
.fire_time
= 30 * 60;
177 event
.expire_time
= 0;
178 event
.backoff_time
= 0;
179 event
.action
= KCM_EVENT_DESTROY_EMPTY_CACHE
;
180 event
.ccache
= ccache
;
182 ret
= kcm_enqueue_event_relative(context
, &event
);
185 kcm_release_ccache(context
, &ccache
);
197 static krb5_error_code
198 kcm_op_destroy(krb5_context context
,
200 kcm_operation opcode
,
201 krb5_storage
*request
,
202 krb5_storage
*response
)
207 ret
= krb5_ret_stringz(request
, &name
);
211 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
213 ret
= kcm_ccache_destroy_client(context
, client
, name
);
228 static krb5_error_code
229 kcm_op_store(krb5_context context
,
231 kcm_operation opcode
,
232 krb5_storage
*request
,
233 krb5_storage
*response
)
240 ret
= krb5_ret_stringz(request
, &name
);
244 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
246 ret
= krb5_ret_creds(request
, &creds
);
252 ret
= kcm_ccache_resolve_client(context
, client
, opcode
,
256 krb5_free_cred_contents(context
, &creds
);
260 ret
= kcm_ccache_store_cred(context
, ccache
, &creds
, 0);
263 krb5_free_cred_contents(context
, &creds
);
264 kcm_release_ccache(context
, &ccache
);
268 kcm_ccache_enqueue_default(context
, ccache
, &creds
);
271 kcm_release_ccache(context
, &ccache
);
286 static krb5_error_code
287 kcm_op_retrieve(krb5_context context
,
289 kcm_operation opcode
,
290 krb5_storage
*request
,
291 krb5_storage
*response
)
301 ret
= krb5_ret_stringz(request
, &name
);
305 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
307 ret
= krb5_ret_uint32(request
, &flags
);
313 ret
= krb5_ret_creds_tag(request
, &mcreds
);
319 if (disallow_getting_krbtgt
&&
320 mcreds
.server
->name
.name_string
.len
== 2 &&
321 strcmp(mcreds
.server
->name
.name_string
.val
[0], KRB5_TGS_NAME
) == 0)
324 krb5_free_cred_contents(context
, &mcreds
);
325 return KRB5_FCC_PERM
;
328 ret
= kcm_ccache_resolve_client(context
, client
, opcode
,
332 krb5_free_cred_contents(context
, &mcreds
);
336 ret
= kcm_ccache_retrieve_cred(context
, ccache
, flags
,
338 if (ret
&& ((flags
& KRB5_GC_CACHED
) == 0)) {
339 krb5_ccache_data ccdata
;
341 /* try and acquire */
342 HEIMDAL_MUTEX_lock(&ccache
->mutex
);
344 /* Fake up an internal ccache */
345 kcm_internal_ccache(context
, ccache
, &ccdata
);
347 /* glue cc layer will store creds */
348 ret
= krb5_get_credentials(context
, 0, &ccdata
, &mcreds
, &credp
);
352 HEIMDAL_MUTEX_unlock(&ccache
->mutex
);
356 ret
= krb5_store_creds(response
, credp
);
360 krb5_free_cred_contents(context
, &mcreds
);
361 kcm_release_ccache(context
, &ccache
);
364 krb5_free_cred_contents(context
, credp
);
376 static krb5_error_code
377 kcm_op_get_principal(krb5_context context
,
379 kcm_operation opcode
,
380 krb5_storage
*request
,
381 krb5_storage
*response
)
387 ret
= krb5_ret_stringz(request
, &name
);
391 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
393 ret
= kcm_ccache_resolve_client(context
, client
, opcode
,
400 if (ccache
->client
== NULL
)
401 ret
= KRB5_CC_NOTFOUND
;
403 ret
= krb5_store_principal(response
, ccache
->client
);
406 kcm_release_ccache(context
, &ccache
);
419 static krb5_error_code
420 kcm_op_get_first(krb5_context context
,
422 kcm_operation opcode
,
423 krb5_storage
*request
,
424 krb5_storage
*response
)
431 ret
= krb5_ret_stringz(request
, &name
);
435 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
437 ret
= kcm_ccache_resolve_client(context
, client
, opcode
,
444 ret
= kcm_cursor_new(context
, client
->pid
, ccache
, &cursor
);
446 kcm_release_ccache(context
, &ccache
);
451 ret
= krb5_store_int32(response
, cursor
);
454 kcm_release_ccache(context
, &ccache
);
467 static krb5_error_code
468 kcm_op_get_next(krb5_context context
,
470 kcm_operation opcode
,
471 krb5_storage
*request
,
472 krb5_storage
*response
)
480 ret
= krb5_ret_stringz(request
, &name
);
484 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
486 ret
= krb5_ret_uint32(request
, &cursor
);
492 ret
= kcm_ccache_resolve_client(context
, client
, opcode
,
499 ret
= kcm_cursor_find(context
, client
->pid
, ccache
, cursor
, &c
);
501 kcm_release_ccache(context
, &ccache
);
506 HEIMDAL_MUTEX_lock(&ccache
->mutex
);
507 if (c
->credp
== NULL
) {
510 ret
= krb5_store_creds(response
, &c
->credp
->cred
);
511 c
->credp
= c
->credp
->next
;
513 HEIMDAL_MUTEX_unlock(&ccache
->mutex
);
516 kcm_release_ccache(context
, &ccache
);
529 static krb5_error_code
530 kcm_op_end_get(krb5_context context
,
532 kcm_operation opcode
,
533 krb5_storage
*request
,
534 krb5_storage
*response
)
541 ret
= krb5_ret_stringz(request
, &name
);
545 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
547 ret
= krb5_ret_uint32(request
, &cursor
);
553 ret
= kcm_ccache_resolve_client(context
, client
, opcode
,
560 ret
= kcm_cursor_delete(context
, client
->pid
, ccache
, cursor
);
563 kcm_release_ccache(context
, &ccache
);
577 static krb5_error_code
578 kcm_op_remove_cred(krb5_context context
,
580 kcm_operation opcode
,
581 krb5_storage
*request
,
582 krb5_storage
*response
)
584 uint32_t whichfields
;
590 ret
= krb5_ret_stringz(request
, &name
);
594 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
596 ret
= krb5_ret_uint32(request
, &whichfields
);
602 ret
= krb5_ret_creds_tag(request
, &mcreds
);
608 ret
= kcm_ccache_resolve_client(context
, client
, opcode
,
612 krb5_free_cred_contents(context
, &mcreds
);
616 ret
= kcm_ccache_remove_cred(context
, ccache
, whichfields
, &mcreds
);
618 /* XXX need to remove any events that match */
621 krb5_free_cred_contents(context
, &mcreds
);
622 kcm_release_ccache(context
, &ccache
);
635 static krb5_error_code
636 kcm_op_set_flags(krb5_context context
,
638 kcm_operation opcode
,
639 krb5_storage
*request
,
640 krb5_storage
*response
)
647 ret
= krb5_ret_stringz(request
, &name
);
651 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
653 ret
= krb5_ret_uint32(request
, &flags
);
659 ret
= kcm_ccache_resolve_client(context
, client
, opcode
,
666 /* we don't really support any flags yet */
668 kcm_release_ccache(context
, &ccache
);
682 static krb5_error_code
683 kcm_op_chown(krb5_context context
,
685 kcm_operation opcode
,
686 krb5_storage
*request
,
687 krb5_storage
*response
)
695 ret
= krb5_ret_stringz(request
, &name
);
699 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
701 ret
= krb5_ret_uint32(request
, &uid
);
707 ret
= krb5_ret_uint32(request
, &gid
);
713 ret
= kcm_ccache_resolve_client(context
, client
, opcode
,
720 ret
= kcm_chown(context
, client
, ccache
, uid
, gid
);
723 kcm_release_ccache(context
, &ccache
);
736 static krb5_error_code
737 kcm_op_chmod(krb5_context context
,
739 kcm_operation opcode
,
740 krb5_storage
*request
,
741 krb5_storage
*response
)
748 ret
= krb5_ret_stringz(request
, &name
);
752 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
754 ret
= krb5_ret_uint16(request
, &mode
);
760 ret
= kcm_ccache_resolve_client(context
, client
, opcode
,
767 ret
= kcm_chmod(context
, client
, ccache
, mode
);
770 kcm_release_ccache(context
, &ccache
);
776 * Protocol extensions for moving ticket acquisition responsibility
777 * from client to KCM follow.
783 * ServerPrincipalPresent
784 * ServerPrincipal OPTIONAL
790 static krb5_error_code
791 kcm_op_get_initial_ticket(krb5_context context
,
793 kcm_operation opcode
,
794 krb5_storage
*request
,
795 krb5_storage
*response
)
801 krb5_principal server
= NULL
;
804 krb5_keyblock_zero(&key
);
806 ret
= krb5_ret_stringz(request
, &name
);
810 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
812 ret
= krb5_ret_int8(request
, ¬_tgt
);
819 ret
= krb5_ret_principal(request
, &server
);
826 ret
= krb5_ret_keyblock(request
, &key
);
830 krb5_free_principal(context
, server
);
834 ret
= kcm_ccache_resolve_client(context
, client
, opcode
,
837 HEIMDAL_MUTEX_lock(&ccache
->mutex
);
839 if (ccache
->server
!= NULL
) {
840 krb5_free_principal(context
, ccache
->server
);
841 ccache
->server
= NULL
;
844 krb5_free_keyblock(context
, &ccache
->key
.keyblock
);
846 ccache
->server
= server
;
847 ccache
->key
.keyblock
= key
;
848 ccache
->flags
|= KCM_FLAGS_USE_CACHED_KEY
;
850 ret
= kcm_ccache_enqueue_default(context
, ccache
, NULL
);
852 ccache
->server
= NULL
;
853 krb5_keyblock_zero(&ccache
->key
.keyblock
);
854 ccache
->flags
&= ~(KCM_FLAGS_USE_CACHED_KEY
);
857 HEIMDAL_MUTEX_unlock(&ccache
->mutex
);
863 krb5_free_principal(context
, server
);
864 krb5_free_keyblock(context
, &key
);
867 kcm_release_ccache(context
, &ccache
);
882 static krb5_error_code
883 kcm_op_get_ticket(krb5_context context
,
885 kcm_operation opcode
,
886 krb5_storage
*request
,
887 krb5_storage
*response
)
892 krb5_principal server
= NULL
;
893 krb5_ccache_data ccdata
;
895 krb5_kdc_flags flags
;
897 memset(&in
, 0, sizeof(in
));
899 ret
= krb5_ret_stringz(request
, &name
);
903 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
905 ret
= krb5_ret_uint32(request
, &flags
.i
);
911 ret
= krb5_ret_int32(request
, &in
.session
.keytype
);
917 ret
= krb5_ret_principal(request
, &server
);
923 ret
= kcm_ccache_resolve_client(context
, client
, opcode
,
926 krb5_free_principal(context
, server
);
931 HEIMDAL_MUTEX_lock(&ccache
->mutex
);
933 /* Fake up an internal ccache */
934 kcm_internal_ccache(context
, ccache
, &ccdata
);
936 in
.client
= ccache
->client
;
938 in
.times
.endtime
= 0;
940 /* glue cc layer will store creds */
941 ret
= krb5_get_credentials_with_flags(context
, 0, flags
,
944 HEIMDAL_MUTEX_unlock(&ccache
->mutex
);
947 krb5_free_cred_contents(context
, out
);
954 static struct kcm_op kcm_ops
[] = {
955 { "NOOP", kcm_op_noop
},
956 { "GET_NAME", kcm_op_get_name
},
957 { "RESOLVE", kcm_op_noop
},
958 { "GEN_NEW", kcm_op_gen_new
},
959 { "INITIALIZE", kcm_op_initialize
},
960 { "DESTROY", kcm_op_destroy
},
961 { "STORE", kcm_op_store
},
962 { "RETRIEVE", kcm_op_retrieve
},
963 { "GET_PRINCIPAL", kcm_op_get_principal
},
964 { "GET_FIRST", kcm_op_get_first
},
965 { "GET_NEXT", kcm_op_get_next
},
966 { "END_GET", kcm_op_end_get
},
967 { "REMOVE_CRED", kcm_op_remove_cred
},
968 { "SET_FLAGS", kcm_op_set_flags
},
969 { "CHOWN", kcm_op_chown
},
970 { "CHMOD", kcm_op_chmod
},
971 { "GET_INITIAL_TICKET", kcm_op_get_initial_ticket
},
972 { "GET_TICKET", kcm_op_get_ticket
}
976 const char *kcm_op2string(kcm_operation opcode
)
978 if (opcode
>= sizeof(kcm_ops
)/sizeof(kcm_ops
[0]))
979 return "Unknown operation";
981 return kcm_ops
[opcode
].name
;
985 kcm_dispatch(krb5_context context
,
988 krb5_data
*resp_data
)
992 krb5_storage
*req_sp
= NULL
;
993 krb5_storage
*resp_sp
= NULL
;
996 resp_sp
= krb5_storage_emem();
997 if (resp_sp
== NULL
) {
1001 if (client
->pid
== -1) {
1002 kcm_log(0, "Client had invalid process number");
1003 ret
= KRB5_FCC_INTERNAL
;
1007 req_sp
= krb5_storage_from_data(req_data
);
1008 if (req_sp
== NULL
) {
1009 kcm_log(0, "Process %d: failed to initialize storage from data",
1015 ret
= krb5_ret_uint16(req_sp
, &opcode
);
1017 kcm_log(0, "Process %d: didn't send a message", client
->pid
);
1021 if (opcode
>= sizeof(kcm_ops
)/sizeof(kcm_ops
[0])) {
1022 kcm_log(0, "Process %d: invalid operation code %d",
1023 client
->pid
, opcode
);
1024 ret
= KRB5_FCC_INTERNAL
;
1027 method
= kcm_ops
[opcode
].method
;
1029 /* seek past place for status code */
1030 krb5_storage_seek(resp_sp
, 4, SEEK_SET
);
1032 ret
= (*method
)(context
, client
, opcode
, req_sp
, resp_sp
);
1035 if (req_sp
!= NULL
) {
1036 krb5_storage_free(req_sp
);
1039 krb5_storage_seek(resp_sp
, 0, SEEK_SET
);
1040 krb5_store_int32(resp_sp
, ret
);
1042 ret
= krb5_storage_to_data(resp_sp
, resp_data
);
1043 krb5_storage_free(resp_sp
);