2 * Copyright (c) 2005, PADL Software Pty Ltd.
5 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of PADL Software nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 kcm_drop_default_cache(krb5_context context
, kcm_client
*client
, char *name
);
43 kcm_is_same_session(kcm_client
*client
, uid_t uid
, pid_t session
)
45 #if 0 /* XXX pppd is running in diffrent session the user */
47 return (client
->session
== session
);
50 return (client
->uid
== uid
);
53 static krb5_error_code
54 kcm_op_noop(krb5_context context
,
57 krb5_storage
*request
,
58 krb5_storage
*response
)
60 KCM_LOG_REQUEST(context
, client
, opcode
);
72 static krb5_error_code
73 kcm_op_get_name(krb5_context context
,
76 krb5_storage
*request
,
77 krb5_storage
*response
)
84 ret
= krb5_ret_stringz(request
, &name
);
88 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
90 ret
= kcm_ccache_resolve_client(context
, client
, opcode
,
97 ret
= krb5_store_stringz(response
, ccache
->name
);
99 kcm_release_ccache(context
, ccache
);
105 kcm_release_ccache(context
, ccache
);
115 static krb5_error_code
116 kcm_op_gen_new(krb5_context context
,
118 kcm_operation opcode
,
119 krb5_storage
*request
,
120 krb5_storage
*response
)
125 KCM_LOG_REQUEST(context
, client
, opcode
);
127 name
= kcm_ccache_nextid(client
->pid
, client
->uid
, client
->gid
);
129 return KRB5_CC_NOMEM
;
132 ret
= krb5_store_stringz(response
, name
);
146 static krb5_error_code
147 kcm_op_initialize(krb5_context context
,
149 kcm_operation opcode
,
150 krb5_storage
*request
,
151 krb5_storage
*response
)
154 krb5_principal principal
;
161 KCM_LOG_REQUEST(context
, client
, opcode
);
163 ret
= krb5_ret_stringz(request
, &name
);
167 ret
= krb5_ret_principal(request
, &principal
);
173 ret
= kcm_ccache_new_client(context
, client
, name
, &ccache
);
176 krb5_free_principal(context
, principal
);
180 ccache
->client
= principal
;
186 * Create a new credentials cache. To mitigate DoS attacks we will
187 * expire it in 30 minutes unless it has some credentials added
191 event
.fire_time
= 30 * 60;
192 event
.expire_time
= 0;
193 event
.backoff_time
= 0;
194 event
.action
= KCM_EVENT_DESTROY_EMPTY_CACHE
;
195 event
.ccache
= ccache
;
197 ret
= kcm_enqueue_event_relative(context
, &event
);
200 kcm_release_ccache(context
, ccache
);
212 static krb5_error_code
213 kcm_op_destroy(krb5_context context
,
215 kcm_operation opcode
,
216 krb5_storage
*request
,
217 krb5_storage
*response
)
222 ret
= krb5_ret_stringz(request
, &name
);
226 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
228 ret
= kcm_ccache_destroy_client(context
, client
, name
);
230 kcm_drop_default_cache(context
, client
, name
);
245 static krb5_error_code
246 kcm_op_store(krb5_context context
,
248 kcm_operation opcode
,
249 krb5_storage
*request
,
250 krb5_storage
*response
)
257 ret
= krb5_ret_stringz(request
, &name
);
261 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
263 ret
= krb5_ret_creds(request
, &creds
);
269 ret
= kcm_ccache_resolve_client(context
, client
, opcode
,
273 krb5_free_cred_contents(context
, &creds
);
277 ret
= kcm_ccache_store_cred(context
, ccache
, &creds
, 0);
280 krb5_free_cred_contents(context
, &creds
);
281 kcm_release_ccache(context
, ccache
);
285 kcm_ccache_enqueue_default(context
, ccache
, &creds
);
288 kcm_release_ccache(context
, ccache
);
303 static krb5_error_code
304 kcm_op_retrieve(krb5_context context
,
306 kcm_operation opcode
,
307 krb5_storage
*request
,
308 krb5_storage
*response
)
318 ret
= krb5_ret_stringz(request
, &name
);
322 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
324 ret
= krb5_ret_uint32(request
, &flags
);
330 ret
= krb5_ret_creds_tag(request
, &mcreds
);
336 if (disallow_getting_krbtgt
&&
337 mcreds
.server
->name
.name_string
.len
== 2 &&
338 strcmp(mcreds
.server
->name
.name_string
.val
[0], KRB5_TGS_NAME
) == 0)
341 krb5_free_cred_contents(context
, &mcreds
);
342 return KRB5_FCC_PERM
;
345 ret
= kcm_ccache_resolve_client(context
, client
, opcode
,
349 krb5_free_cred_contents(context
, &mcreds
);
353 ret
= kcm_ccache_retrieve_cred(context
, ccache
, flags
,
355 if (ret
&& ((flags
& KRB5_GC_CACHED
) == 0) &&
356 !krb5_is_config_principal(context
, mcreds
.server
)) {
357 krb5_ccache_data ccdata
;
359 /* try and acquire */
360 HEIMDAL_MUTEX_lock(&ccache
->mutex
);
362 /* Fake up an internal ccache */
363 kcm_internal_ccache(context
, ccache
, &ccdata
);
365 /* glue cc layer will store creds */
366 ret
= krb5_get_credentials(context
, 0, &ccdata
, &mcreds
, &credp
);
370 HEIMDAL_MUTEX_unlock(&ccache
->mutex
);
374 ret
= krb5_store_creds(response
, credp
);
378 krb5_free_cred_contents(context
, &mcreds
);
379 kcm_release_ccache(context
, ccache
);
382 krb5_free_cred_contents(context
, credp
);
394 static krb5_error_code
395 kcm_op_get_principal(krb5_context context
,
397 kcm_operation opcode
,
398 krb5_storage
*request
,
399 krb5_storage
*response
)
405 ret
= krb5_ret_stringz(request
, &name
);
409 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
411 ret
= kcm_ccache_resolve_client(context
, client
, opcode
,
418 if (ccache
->client
== NULL
)
419 ret
= KRB5_CC_NOTFOUND
;
421 ret
= krb5_store_principal(response
, ccache
->client
);
424 kcm_release_ccache(context
, ccache
);
437 static krb5_error_code
438 kcm_op_get_cred_uuid_list(krb5_context context
,
440 kcm_operation opcode
,
441 krb5_storage
*request
,
442 krb5_storage
*response
)
444 struct kcm_creds
*creds
;
449 ret
= krb5_ret_stringz(request
, &name
);
453 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
455 ret
= kcm_ccache_resolve_client(context
, client
, opcode
,
461 for (creds
= ccache
->creds
; creds
; creds
= creds
->next
) {
463 sret
= krb5_storage_write(response
, &creds
->uuid
, sizeof(creds
->uuid
));
464 if (sret
!= sizeof(creds
->uuid
)) {
470 kcm_release_ccache(context
, ccache
);
483 static krb5_error_code
484 kcm_op_get_cred_by_uuid(krb5_context context
,
486 kcm_operation opcode
,
487 krb5_storage
*request
,
488 krb5_storage
*response
)
497 ret
= krb5_ret_stringz(request
, &name
);
501 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
503 ret
= kcm_ccache_resolve_client(context
, client
, opcode
,
509 sret
= krb5_storage_read(request
, &uuid
, sizeof(uuid
));
510 if (sret
!= sizeof(uuid
)) {
511 kcm_release_ccache(context
, ccache
);
512 krb5_clear_error_message(context
);
516 c
= kcm_ccache_find_cred_uuid(context
, ccache
, uuid
);
518 kcm_release_ccache(context
, ccache
);
522 HEIMDAL_MUTEX_lock(&ccache
->mutex
);
523 ret
= krb5_store_creds(response
, &c
->cred
);
524 HEIMDAL_MUTEX_unlock(&ccache
->mutex
);
526 kcm_release_ccache(context
, ccache
);
540 static krb5_error_code
541 kcm_op_remove_cred(krb5_context context
,
543 kcm_operation opcode
,
544 krb5_storage
*request
,
545 krb5_storage
*response
)
547 uint32_t whichfields
;
553 ret
= krb5_ret_stringz(request
, &name
);
557 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
559 ret
= krb5_ret_uint32(request
, &whichfields
);
565 ret
= krb5_ret_creds_tag(request
, &mcreds
);
571 ret
= kcm_ccache_resolve_client(context
, client
, opcode
,
575 krb5_free_cred_contents(context
, &mcreds
);
579 ret
= kcm_ccache_remove_cred(context
, ccache
, whichfields
, &mcreds
);
581 /* XXX need to remove any events that match */
584 krb5_free_cred_contents(context
, &mcreds
);
585 kcm_release_ccache(context
, ccache
);
598 static krb5_error_code
599 kcm_op_set_flags(krb5_context context
,
601 kcm_operation opcode
,
602 krb5_storage
*request
,
603 krb5_storage
*response
)
610 ret
= krb5_ret_stringz(request
, &name
);
614 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
616 ret
= krb5_ret_uint32(request
, &flags
);
622 ret
= kcm_ccache_resolve_client(context
, client
, opcode
,
629 /* we don't really support any flags yet */
631 kcm_release_ccache(context
, ccache
);
645 static krb5_error_code
646 kcm_op_chown(krb5_context context
,
648 kcm_operation opcode
,
649 krb5_storage
*request
,
650 krb5_storage
*response
)
658 ret
= krb5_ret_stringz(request
, &name
);
662 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
664 ret
= krb5_ret_uint32(request
, &uid
);
670 ret
= krb5_ret_uint32(request
, &gid
);
676 ret
= kcm_ccache_resolve_client(context
, client
, opcode
,
683 ret
= kcm_chown(context
, client
, ccache
, uid
, gid
);
686 kcm_release_ccache(context
, ccache
);
699 static krb5_error_code
700 kcm_op_chmod(krb5_context context
,
702 kcm_operation opcode
,
703 krb5_storage
*request
,
704 krb5_storage
*response
)
711 ret
= krb5_ret_stringz(request
, &name
);
715 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
717 ret
= krb5_ret_uint16(request
, &mode
);
723 ret
= kcm_ccache_resolve_client(context
, client
, opcode
,
730 ret
= kcm_chmod(context
, client
, ccache
, mode
);
733 kcm_release_ccache(context
, ccache
);
739 * Protocol extensions for moving ticket acquisition responsibility
740 * from client to KCM follow.
746 * ServerPrincipalPresent
747 * ServerPrincipal OPTIONAL
753 static krb5_error_code
754 kcm_op_get_initial_ticket(krb5_context context
,
756 kcm_operation opcode
,
757 krb5_storage
*request
,
758 krb5_storage
*response
)
764 krb5_principal server
= NULL
;
767 krb5_keyblock_zero(&key
);
769 ret
= krb5_ret_stringz(request
, &name
);
773 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
775 ret
= krb5_ret_int8(request
, ¬_tgt
);
782 ret
= krb5_ret_principal(request
, &server
);
789 ret
= krb5_ret_keyblock(request
, &key
);
793 krb5_free_principal(context
, server
);
797 ret
= kcm_ccache_resolve_client(context
, client
, opcode
,
800 HEIMDAL_MUTEX_lock(&ccache
->mutex
);
802 if (ccache
->server
!= NULL
) {
803 krb5_free_principal(context
, ccache
->server
);
804 ccache
->server
= NULL
;
807 krb5_free_keyblock(context
, &ccache
->key
.keyblock
);
809 ccache
->server
= server
;
810 ccache
->key
.keyblock
= key
;
811 ccache
->flags
|= KCM_FLAGS_USE_CACHED_KEY
;
813 ret
= kcm_ccache_enqueue_default(context
, ccache
, NULL
);
815 ccache
->server
= NULL
;
816 krb5_keyblock_zero(&ccache
->key
.keyblock
);
817 ccache
->flags
&= ~(KCM_FLAGS_USE_CACHED_KEY
);
820 HEIMDAL_MUTEX_unlock(&ccache
->mutex
);
826 krb5_free_principal(context
, server
);
827 krb5_free_keyblock(context
, &key
);
830 kcm_release_ccache(context
, ccache
);
845 static krb5_error_code
846 kcm_op_get_ticket(krb5_context context
,
848 kcm_operation opcode
,
849 krb5_storage
*request
,
850 krb5_storage
*response
)
855 krb5_principal server
= NULL
;
856 krb5_ccache_data ccdata
;
858 krb5_kdc_flags flags
;
860 memset(&in
, 0, sizeof(in
));
862 ret
= krb5_ret_stringz(request
, &name
);
866 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
868 ret
= krb5_ret_uint32(request
, &flags
.i
);
874 ret
= krb5_ret_int32(request
, &in
.session
.keytype
);
880 ret
= krb5_ret_principal(request
, &server
);
886 ret
= kcm_ccache_resolve_client(context
, client
, opcode
,
889 krb5_free_principal(context
, server
);
894 HEIMDAL_MUTEX_lock(&ccache
->mutex
);
896 /* Fake up an internal ccache */
897 kcm_internal_ccache(context
, ccache
, &ccdata
);
899 in
.client
= ccache
->client
;
901 in
.times
.endtime
= 0;
903 /* glue cc layer will store creds */
904 ret
= krb5_get_credentials_with_flags(context
, 0, flags
,
907 HEIMDAL_MUTEX_unlock(&ccache
->mutex
);
909 krb5_free_principal(context
, server
);
912 krb5_free_cred_contents(context
, out
);
914 kcm_release_ccache(context
, ccache
);
928 static krb5_error_code
929 kcm_op_move_cache(krb5_context context
,
931 kcm_operation opcode
,
932 krb5_storage
*request
,
933 krb5_storage
*response
)
936 kcm_ccache oldid
, newid
;
937 char *oldname
, *newname
;
939 ret
= krb5_ret_stringz(request
, &oldname
);
943 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, oldname
);
945 ret
= krb5_ret_stringz(request
, &newname
);
951 /* move to ourself is simple, done! */
952 if (strcmp(oldname
, newname
) == 0) {
958 ret
= kcm_ccache_resolve_client(context
, client
, opcode
, oldname
, &oldid
);
965 /* Check if new credential cache exists, if not create one. */
966 ret
= kcm_ccache_resolve_client(context
, client
, opcode
, newname
, &newid
);
967 if (ret
== KRB5_FCC_NOFILE
)
968 ret
= kcm_ccache_new_client(context
, client
, newname
, &newid
);
973 kcm_release_ccache(context
, oldid
);
977 HEIMDAL_MUTEX_lock(&oldid
->mutex
);
978 HEIMDAL_MUTEX_lock(&newid
->mutex
);
984 #define MOVE(n,o,f) { tmp.f = n->f ; n->f = o->f; o->f = tmp.f; }
986 MOVE(newid
, oldid
, flags
);
987 MOVE(newid
, oldid
, client
);
988 MOVE(newid
, oldid
, server
);
989 MOVE(newid
, oldid
, creds
);
990 MOVE(newid
, oldid
, tkt_life
);
991 MOVE(newid
, oldid
, renew_life
);
992 MOVE(newid
, oldid
, key
);
993 MOVE(newid
, oldid
, kdc_offset
);
997 HEIMDAL_MUTEX_unlock(&oldid
->mutex
);
998 HEIMDAL_MUTEX_unlock(&newid
->mutex
);
1000 kcm_release_ccache(context
, oldid
);
1001 kcm_release_ccache(context
, newid
);
1003 ret
= kcm_ccache_destroy_client(context
, client
, oldname
);
1005 kcm_drop_default_cache(context
, client
, oldname
);
1012 static krb5_error_code
1013 kcm_op_get_cache_uuid_list(krb5_context context
,
1015 kcm_operation opcode
,
1016 krb5_storage
*request
,
1017 krb5_storage
*response
)
1019 KCM_LOG_REQUEST(context
, client
, opcode
);
1021 return kcm_ccache_get_uuids(context
, client
, opcode
, response
);
1024 static krb5_error_code
1025 kcm_op_get_cache_by_uuid(krb5_context context
,
1027 kcm_operation opcode
,
1028 krb5_storage
*request
,
1029 krb5_storage
*response
)
1031 krb5_error_code ret
;
1036 KCM_LOG_REQUEST(context
, client
, opcode
);
1038 sret
= krb5_storage_read(request
, &uuid
, sizeof(uuid
));
1039 if (sret
!= sizeof(uuid
)) {
1040 krb5_clear_error_message(context
);
1044 ret
= kcm_ccache_resolve_by_uuid(context
, uuid
, &cache
);
1048 ret
= kcm_access(context
, client
, opcode
, cache
);
1050 ret
= KRB5_FCC_NOFILE
;
1053 ret
= krb5_store_stringz(response
, cache
->name
);
1055 kcm_release_ccache(context
, cache
);
1060 struct kcm_default_cache
*default_caches
;
1062 static krb5_error_code
1063 kcm_op_get_default_cache(krb5_context context
,
1065 kcm_operation opcode
,
1066 krb5_storage
*request
,
1067 krb5_storage
*response
)
1069 struct kcm_default_cache
*c
;
1070 krb5_error_code ret
;
1071 const char *name
= NULL
;
1074 KCM_LOG_REQUEST(context
, client
, opcode
);
1076 for (c
= default_caches
; c
!= NULL
; c
= c
->next
) {
1077 if (kcm_is_same_session(client
, c
->uid
, c
->session
)) {
1083 name
= n
= kcm_ccache_first_name(client
);
1086 asprintf(&n
, "%d", (int)client
->uid
);
1091 ret
= krb5_store_stringz(response
, name
);
1098 kcm_drop_default_cache(krb5_context context
, kcm_client
*client
, char *name
)
1100 struct kcm_default_cache
**c
;
1102 for (c
= &default_caches
; *c
!= NULL
; c
= &(*c
)->next
) {
1103 if (!kcm_is_same_session(client
, (*c
)->uid
, (*c
)->session
))
1105 if (strcmp((*c
)->name
, name
) == 0) {
1106 struct kcm_default_cache
*h
= *c
;
1115 static krb5_error_code
1116 kcm_op_set_default_cache(krb5_context context
,
1118 kcm_operation opcode
,
1119 krb5_storage
*request
,
1120 krb5_storage
*response
)
1122 struct kcm_default_cache
*c
;
1123 krb5_error_code ret
;
1126 ret
= krb5_ret_stringz(request
, &name
);
1130 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
1132 for (c
= default_caches
; c
!= NULL
; c
= c
->next
) {
1133 if (kcm_is_same_session(client
, c
->uid
, c
->session
))
1137 c
= malloc(sizeof(*c
));
1140 c
->session
= client
->session
;
1141 c
->uid
= client
->uid
;
1142 c
->name
= strdup(name
);
1144 c
->next
= default_caches
;
1148 c
->name
= strdup(name
);
1154 static krb5_error_code
1155 kcm_op_get_kdc_offset(krb5_context context
,
1157 kcm_operation opcode
,
1158 krb5_storage
*request
,
1159 krb5_storage
*response
)
1161 krb5_error_code ret
;
1165 ret
= krb5_ret_stringz(request
, &name
);
1169 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
1171 ret
= kcm_ccache_resolve_client(context
, client
, opcode
, name
, &ccache
);
1176 HEIMDAL_MUTEX_lock(&ccache
->mutex
);
1177 ret
= krb5_store_int32(response
, ccache
->kdc_offset
);
1178 HEIMDAL_MUTEX_unlock(&ccache
->mutex
);
1180 kcm_release_ccache(context
, ccache
);
1185 static krb5_error_code
1186 kcm_op_set_kdc_offset(krb5_context context
,
1188 kcm_operation opcode
,
1189 krb5_storage
*request
,
1190 krb5_storage
*response
)
1192 krb5_error_code ret
;
1197 ret
= krb5_ret_stringz(request
, &name
);
1201 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
1203 ret
= krb5_ret_int32(request
, &offset
);
1209 ret
= kcm_ccache_resolve_client(context
, client
, opcode
, name
, &ccache
);
1214 HEIMDAL_MUTEX_lock(&ccache
->mutex
);
1215 ccache
->kdc_offset
= offset
;
1216 HEIMDAL_MUTEX_unlock(&ccache
->mutex
);
1218 kcm_release_ccache(context
, ccache
);
1223 struct kcm_ntlm_cred
{
1230 struct kcm_ntlm_cred
*next
;
1233 static struct kcm_ntlm_cred
*ntlm_head
;
1236 free_cred(struct kcm_ntlm_cred
*cred
)
1240 krb5_data_free(&cred
->nthash
);
1254 static struct kcm_ntlm_cred
*
1255 find_ntlm_cred(const char *user
, const char *domain
, kcm_client
*client
)
1257 struct kcm_ntlm_cred
*c
;
1259 for (c
= ntlm_head
; c
!= NULL
; c
= c
->next
)
1260 if ((user
[0] == '\0' || strcmp(user
, c
->user
) == 0) &&
1261 (domain
== NULL
|| strcmp(domain
, c
->domain
) == 0) &&
1262 kcm_is_same_session(client
, c
->uid
, c
->session
))
1268 static krb5_error_code
1269 kcm_op_add_ntlm_cred(krb5_context context
,
1271 kcm_operation opcode
,
1272 krb5_storage
*request
,
1273 krb5_storage
*response
)
1275 struct kcm_ntlm_cred
*cred
, *c
;
1276 krb5_error_code ret
;
1278 cred
= calloc(1, sizeof(*cred
));
1282 RAND_bytes(cred
->uuid
, sizeof(cred
->uuid
));
1284 ret
= krb5_ret_stringz(request
, &cred
->user
);
1288 ret
= krb5_ret_stringz(request
, &cred
->domain
);
1292 ret
= krb5_ret_data(request
, &cred
->nthash
);
1296 /* search for dups */
1297 c
= find_ntlm_cred(cred
->user
, cred
->domain
, client
);
1299 krb5_data hash
= c
->nthash
;
1300 c
->nthash
= cred
->nthash
;
1301 cred
->nthash
= hash
;
1305 cred
->next
= ntlm_head
;
1309 cred
->uid
= client
->uid
;
1310 cred
->session
= client
->session
;
1312 /* write response */
1313 (void)krb5_storage_write(response
, &cred
->uuid
, sizeof(cred
->uuid
));
1324 * { "HAVE_NTLM_CRED", NULL },
1331 static krb5_error_code
1332 kcm_op_have_ntlm_cred(krb5_context context
,
1334 kcm_operation opcode
,
1335 krb5_storage
*request
,
1336 krb5_storage
*response
)
1338 struct kcm_ntlm_cred
*c
;
1339 char *user
= NULL
, *domain
= NULL
;
1340 krb5_error_code ret
;
1342 ret
= krb5_ret_stringz(request
, &user
);
1346 ret
= krb5_ret_stringz(request
, &domain
);
1350 if (domain
[0] == '\0') {
1355 c
= find_ntlm_cred(user
, domain
, client
);
1368 * { "DEL_NTLM_CRED", NULL },
1375 static krb5_error_code
1376 kcm_op_del_ntlm_cred(krb5_context context
,
1378 kcm_operation opcode
,
1379 krb5_storage
*request
,
1380 krb5_storage
*response
)
1382 struct kcm_ntlm_cred
**cp
, *c
;
1383 char *user
= NULL
, *domain
= NULL
;
1384 krb5_error_code ret
;
1386 ret
= krb5_ret_stringz(request
, &user
);
1390 ret
= krb5_ret_stringz(request
, &domain
);
1394 for (cp
= &ntlm_head
; *cp
!= NULL
; cp
= &(*cp
)->next
) {
1395 if (strcmp(user
, (*cp
)->user
) == 0 && strcmp(domain
, (*cp
)->domain
) == 0 &&
1396 kcm_is_same_session(client
, (*cp
)->uid
, (*cp
)->session
))
1414 * { "DO_NTLM_AUTH", NULL },
1427 #define NTLM_FLAG_SESSIONKEY 1
1428 #define NTLM_FLAG_NTLM2_SESSION 2
1429 #define NTLM_FLAG_KEYEX 4
1431 static krb5_error_code
1432 kcm_op_do_ntlm(krb5_context context
,
1434 kcm_operation opcode
,
1435 krb5_storage
*request
,
1436 krb5_storage
*response
)
1438 struct kcm_ntlm_cred
*c
;
1439 struct ntlm_type2 type2
;
1440 struct ntlm_type3 type3
;
1441 char *user
= NULL
, *domain
= NULL
;
1442 struct ntlm_buf ndata
, sessionkey
;
1444 krb5_error_code ret
;
1447 memset(&type2
, 0, sizeof(type2
));
1448 memset(&type3
, 0, sizeof(type3
));
1449 sessionkey
.data
= NULL
;
1450 sessionkey
.length
= 0;
1452 ret
= krb5_ret_stringz(request
, &user
);
1456 ret
= krb5_ret_stringz(request
, &domain
);
1460 if (domain
[0] == '\0') {
1465 c
= find_ntlm_cred(user
, domain
, client
);
1471 ret
= krb5_ret_data(request
, &data
);
1475 ndata
.data
= data
.data
;
1476 ndata
.length
= data
.length
;
1478 ret
= heim_ntlm_decode_type2(&ndata
, &type2
);
1479 krb5_data_free(&data
);
1483 if (domain
&& strcmp(domain
, type2
.targetname
) == 0) {
1488 type3
.username
= c
->user
;
1489 type3
.flags
= type2
.flags
;
1490 type3
.targetname
= type2
.targetname
;
1491 type3
.ws
= rk_UNCONST("workstation");
1494 * NTLM Version 1 if no targetinfo buffer.
1497 if (1 || type2
.targetinfo
.length
== 0) {
1498 struct ntlm_buf sessionkey
;
1500 if (type2
.flags
& NTLM_NEG_NTLM2_SESSION
) {
1501 unsigned char nonce
[8];
1503 if (RAND_bytes(nonce
, sizeof(nonce
)) != 1) {
1508 ret
= heim_ntlm_calculate_ntlm2_sess(nonce
,
1514 ret
= heim_ntlm_calculate_ntlm1(c
->nthash
.data
,
1523 ret
= heim_ntlm_build_ntlm1_master(c
->nthash
.data
,
1529 free(type3
.lm
.data
);
1530 if (type3
.ntlm
.data
)
1531 free(type3
.ntlm
.data
);
1535 free(sessionkey
.data
);
1538 free(type3
.lm
.data
);
1539 if (type3
.ntlm
.data
)
1540 free(type3
.ntlm
.data
);
1543 flags
|= NTLM_FLAG_SESSIONKEY
;
1546 struct ntlm_buf sessionkey
;
1547 unsigned char ntlmv2
[16];
1548 struct ntlm_targetinfo ti
;
1550 /* verify infotarget */
1552 ret
= heim_ntlm_decode_targetinfo(&type2
.targetinfo
, 1, &ti
);
1554 _gss_ntlm_delete_sec_context(minor_status
,
1555 context_handle
, NULL
);
1556 *minor_status
= ret
;
1557 return GSS_S_FAILURE
;
1560 if (ti
.domainname
&& strcmp(ti
.domainname
, name
->domain
) != 0) {
1561 _gss_ntlm_delete_sec_context(minor_status
,
1562 context_handle
, NULL
);
1563 *minor_status
= EINVAL
;
1564 return GSS_S_FAILURE
;
1567 ret
= heim_ntlm_calculate_ntlm2(ctx
->client
->key
.data
,
1568 ctx
->client
->key
.length
,
1576 _gss_ntlm_delete_sec_context(minor_status
,
1577 context_handle
, NULL
);
1578 *minor_status
= ret
;
1579 return GSS_S_FAILURE
;
1582 ret
= heim_ntlm_build_ntlm1_master(ntlmv2
, sizeof(ntlmv2
),
1585 memset(ntlmv2
, 0, sizeof(ntlmv2
));
1587 _gss_ntlm_delete_sec_context(minor_status
,
1588 context_handle
, NULL
);
1589 *minor_status
= ret
;
1590 return GSS_S_FAILURE
;
1593 flags
|= NTLM_FLAG_NTLM2_SESSION
|
1596 if (type3
.flags
& NTLM_NEG_KEYEX
)
1597 flags
|= NTLM_FLAG_KEYEX
;
1599 ret
= krb5_data_copy(&ctx
->sessionkey
,
1600 sessionkey
.data
, sessionkey
.length
);
1601 free(sessionkey
.data
);
1603 _gss_ntlm_delete_sec_context(minor_status
,
1604 context_handle
, NULL
);
1605 *minor_status
= ret
;
1606 return GSS_S_FAILURE
;
1612 if (flags
& NTLM_FLAG_NTLM2_SESSION
) {
1613 _gss_ntlm_set_key(&ctx
->u
.v2
.send
, 0, (ctx
->flags
& NTLM_NEG_KEYEX
),
1614 ctx
->sessionkey
.data
,
1615 ctx
->sessionkey
.length
);
1616 _gss_ntlm_set_key(&ctx
->u
.v2
.recv
, 1, (ctx
->flags
& NTLM_NEG_KEYEX
),
1617 ctx
->sessionkey
.data
,
1618 ctx
->sessionkey
.length
);
1620 flags
|= NTLM_FLAG_SESSION
;
1621 RC4_set_key(&ctx
->u
.v1
.crypto_recv
.key
,
1622 ctx
->sessionkey
.length
,
1623 ctx
->sessionkey
.data
);
1624 RC4_set_key(&ctx
->u
.v1
.crypto_send
.key
,
1625 ctx
->sessionkey
.length
,
1626 ctx
->sessionkey
.data
);
1630 ret
= heim_ntlm_encode_type3(&type3
, &ndata
);
1634 data
.data
= ndata
.data
;
1635 data
.length
= ndata
.length
;
1636 ret
= krb5_store_data(response
, data
);
1637 heim_ntlm_free_buf(&ndata
);
1638 if (ret
) goto error
;
1640 ret
= krb5_store_int32(response
, flags
);
1641 if (ret
) goto error
;
1643 data
.data
= sessionkey
.data
;
1644 data
.length
= sessionkey
.length
;
1646 ret
= krb5_store_data(response
, data
);
1647 if (ret
) goto error
;
1650 free(type3
.username
);
1651 heim_ntlm_free_type2(&type2
);
1661 * { "GET_NTLM_UUID_LIST", NULL }
1668 static krb5_error_code
1669 kcm_op_get_ntlm_user_list(krb5_context context
,
1671 kcm_operation opcode
,
1672 krb5_storage
*request
,
1673 krb5_storage
*response
)
1675 struct kcm_ntlm_cred
*c
;
1676 krb5_error_code ret
;
1678 for (c
= ntlm_head
; c
!= NULL
; c
= c
->next
) {
1679 if (!kcm_is_same_session(client
, c
->uid
, c
->session
))
1682 ret
= krb5_store_uint32(response
, 1);
1685 ret
= krb5_store_stringz(response
, c
->user
);
1688 ret
= krb5_store_stringz(response
, c
->domain
);
1692 return krb5_store_uint32(response
, 0);
1699 static struct kcm_op kcm_ops
[] = {
1700 { "NOOP", kcm_op_noop
},
1701 { "GET_NAME", kcm_op_get_name
},
1702 { "RESOLVE", kcm_op_noop
},
1703 { "GEN_NEW", kcm_op_gen_new
},
1704 { "INITIALIZE", kcm_op_initialize
},
1705 { "DESTROY", kcm_op_destroy
},
1706 { "STORE", kcm_op_store
},
1707 { "RETRIEVE", kcm_op_retrieve
},
1708 { "GET_PRINCIPAL", kcm_op_get_principal
},
1709 { "GET_CRED_UUID_LIST", kcm_op_get_cred_uuid_list
},
1710 { "GET_CRED_BY_UUID", kcm_op_get_cred_by_uuid
},
1711 { "REMOVE_CRED", kcm_op_remove_cred
},
1712 { "SET_FLAGS", kcm_op_set_flags
},
1713 { "CHOWN", kcm_op_chown
},
1714 { "CHMOD", kcm_op_chmod
},
1715 { "GET_INITIAL_TICKET", kcm_op_get_initial_ticket
},
1716 { "GET_TICKET", kcm_op_get_ticket
},
1717 { "MOVE_CACHE", kcm_op_move_cache
},
1718 { "GET_CACHE_UUID_LIST", kcm_op_get_cache_uuid_list
},
1719 { "GET_CACHE_BY_UUID", kcm_op_get_cache_by_uuid
},
1720 { "GET_DEFAULT_CACHE", kcm_op_get_default_cache
},
1721 { "SET_DEFAULT_CACHE", kcm_op_set_default_cache
},
1722 { "GET_KDC_OFFSET", kcm_op_get_kdc_offset
},
1723 { "SET_KDC_OFFSET", kcm_op_set_kdc_offset
},
1724 { "ADD_NTLM_CRED", kcm_op_add_ntlm_cred
},
1725 { "HAVE_USER_CRED", kcm_op_have_ntlm_cred
},
1726 { "DEL_NTLM_CRED", kcm_op_del_ntlm_cred
},
1727 { "DO_NTLM_AUTH", kcm_op_do_ntlm
},
1728 { "GET_NTLM_USER_LIST", kcm_op_get_ntlm_user_list
}
1733 kcm_op2string(kcm_operation opcode
)
1735 if (opcode
>= sizeof(kcm_ops
)/sizeof(kcm_ops
[0]))
1736 return "Unknown operation";
1738 return kcm_ops
[opcode
].name
;
1742 kcm_dispatch(krb5_context context
,
1744 krb5_data
*req_data
,
1745 krb5_data
*resp_data
)
1747 krb5_error_code ret
;
1749 krb5_storage
*req_sp
= NULL
;
1750 krb5_storage
*resp_sp
= NULL
;
1753 resp_sp
= krb5_storage_emem();
1754 if (resp_sp
== NULL
) {
1758 if (client
->pid
== -1) {
1759 kcm_log(0, "Client had invalid process number");
1760 ret
= KRB5_FCC_INTERNAL
;
1764 req_sp
= krb5_storage_from_data(req_data
);
1765 if (req_sp
== NULL
) {
1766 kcm_log(0, "Process %d: failed to initialize storage from data",
1772 ret
= krb5_ret_uint16(req_sp
, &opcode
);
1774 kcm_log(0, "Process %d: didn't send a message", client
->pid
);
1778 if (opcode
>= sizeof(kcm_ops
)/sizeof(kcm_ops
[0])) {
1779 kcm_log(0, "Process %d: invalid operation code %d",
1780 client
->pid
, opcode
);
1781 ret
= KRB5_FCC_INTERNAL
;
1784 method
= kcm_ops
[opcode
].method
;
1785 if (method
== NULL
) {
1786 kcm_log(0, "Process %d: operation code %s not implemented",
1787 client
->pid
, kcm_op2string(opcode
));
1788 ret
= KRB5_FCC_INTERNAL
;
1792 /* seek past place for status code */
1793 krb5_storage_seek(resp_sp
, 4, SEEK_SET
);
1795 ret
= (*method
)(context
, client
, opcode
, req_sp
, resp_sp
);
1798 if (req_sp
!= NULL
) {
1799 krb5_storage_free(req_sp
);
1802 krb5_storage_seek(resp_sp
, 0, SEEK_SET
);
1803 krb5_store_int32(resp_sp
, ret
);
1805 ret
= krb5_storage_to_data(resp_sp
, resp_data
);
1806 krb5_storage_free(resp_sp
);