1 /* $NetBSD: protocol.c,v 1.1.1.2 2014/04/24 12:45:27 pettai Exp $ */
4 * Copyright (c) 2005, PADL Software Pty Ltd.
7 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of PADL Software nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 #include <krb5/heimntlm.h>
41 kcm_drop_default_cache(krb5_context context
, kcm_client
*client
, char *name
);
45 kcm_is_same_session(kcm_client
*client
, uid_t uid
, pid_t session
)
47 #if 0 /* XXX pppd is running in diffrent session the user */
49 return (client
->session
== session
);
52 return (client
->uid
== uid
);
55 static krb5_error_code
56 kcm_op_noop(krb5_context context
,
59 krb5_storage
*request
,
60 krb5_storage
*response
)
62 KCM_LOG_REQUEST(context
, client
, opcode
);
74 static krb5_error_code
75 kcm_op_get_name(krb5_context context
,
78 krb5_storage
*request
,
79 krb5_storage
*response
)
86 ret
= krb5_ret_stringz(request
, &name
);
90 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
92 ret
= kcm_ccache_resolve_client(context
, client
, opcode
,
99 ret
= krb5_store_stringz(response
, ccache
->name
);
101 kcm_release_ccache(context
, ccache
);
107 kcm_release_ccache(context
, ccache
);
117 static krb5_error_code
118 kcm_op_gen_new(krb5_context context
,
120 kcm_operation opcode
,
121 krb5_storage
*request
,
122 krb5_storage
*response
)
127 KCM_LOG_REQUEST(context
, client
, opcode
);
129 name
= kcm_ccache_nextid(client
->pid
, client
->uid
, client
->gid
);
131 return KRB5_CC_NOMEM
;
134 ret
= krb5_store_stringz(response
, name
);
148 static krb5_error_code
149 kcm_op_initialize(krb5_context context
,
151 kcm_operation opcode
,
152 krb5_storage
*request
,
153 krb5_storage
*response
)
156 krb5_principal principal
;
163 KCM_LOG_REQUEST(context
, client
, opcode
);
165 ret
= krb5_ret_stringz(request
, &name
);
169 ret
= krb5_ret_principal(request
, &principal
);
175 ret
= kcm_ccache_new_client(context
, client
, name
, &ccache
);
178 krb5_free_principal(context
, principal
);
182 ccache
->client
= principal
;
188 * Create a new credentials cache. To mitigate DoS attacks we will
189 * expire it in 30 minutes unless it has some credentials added
193 event
.fire_time
= 30 * 60;
194 event
.expire_time
= 0;
195 event
.backoff_time
= 0;
196 event
.action
= KCM_EVENT_DESTROY_EMPTY_CACHE
;
197 event
.ccache
= ccache
;
199 ret
= kcm_enqueue_event_relative(context
, &event
);
202 kcm_release_ccache(context
, ccache
);
214 static krb5_error_code
215 kcm_op_destroy(krb5_context context
,
217 kcm_operation opcode
,
218 krb5_storage
*request
,
219 krb5_storage
*response
)
224 ret
= krb5_ret_stringz(request
, &name
);
228 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
230 ret
= kcm_ccache_destroy_client(context
, client
, name
);
232 kcm_drop_default_cache(context
, client
, name
);
247 static krb5_error_code
248 kcm_op_store(krb5_context context
,
250 kcm_operation opcode
,
251 krb5_storage
*request
,
252 krb5_storage
*response
)
259 ret
= krb5_ret_stringz(request
, &name
);
263 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
265 ret
= krb5_ret_creds(request
, &creds
);
271 ret
= kcm_ccache_resolve_client(context
, client
, opcode
,
275 krb5_free_cred_contents(context
, &creds
);
279 ret
= kcm_ccache_store_cred(context
, ccache
, &creds
, 0);
282 krb5_free_cred_contents(context
, &creds
);
283 kcm_release_ccache(context
, ccache
);
287 kcm_ccache_enqueue_default(context
, ccache
, &creds
);
290 kcm_release_ccache(context
, ccache
);
305 static krb5_error_code
306 kcm_op_retrieve(krb5_context context
,
308 kcm_operation opcode
,
309 krb5_storage
*request
,
310 krb5_storage
*response
)
320 ret
= krb5_ret_stringz(request
, &name
);
324 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
326 ret
= krb5_ret_uint32(request
, &flags
);
332 ret
= krb5_ret_creds_tag(request
, &mcreds
);
338 if (disallow_getting_krbtgt
&&
339 mcreds
.server
->name
.name_string
.len
== 2 &&
340 strcmp(mcreds
.server
->name
.name_string
.val
[0], KRB5_TGS_NAME
) == 0)
343 krb5_free_cred_contents(context
, &mcreds
);
344 return KRB5_FCC_PERM
;
347 ret
= kcm_ccache_resolve_client(context
, client
, opcode
,
351 krb5_free_cred_contents(context
, &mcreds
);
355 ret
= kcm_ccache_retrieve_cred(context
, ccache
, flags
,
357 if (ret
&& ((flags
& KRB5_GC_CACHED
) == 0) &&
358 !krb5_is_config_principal(context
, mcreds
.server
)) {
359 krb5_ccache_data ccdata
;
361 /* try and acquire */
362 HEIMDAL_MUTEX_lock(&ccache
->mutex
);
364 /* Fake up an internal ccache */
365 kcm_internal_ccache(context
, ccache
, &ccdata
);
367 /* glue cc layer will store creds */
368 ret
= krb5_get_credentials(context
, 0, &ccdata
, &mcreds
, &credp
);
372 HEIMDAL_MUTEX_unlock(&ccache
->mutex
);
376 ret
= krb5_store_creds(response
, credp
);
380 krb5_free_cred_contents(context
, &mcreds
);
381 kcm_release_ccache(context
, ccache
);
384 krb5_free_cred_contents(context
, credp
);
396 static krb5_error_code
397 kcm_op_get_principal(krb5_context context
,
399 kcm_operation opcode
,
400 krb5_storage
*request
,
401 krb5_storage
*response
)
407 ret
= krb5_ret_stringz(request
, &name
);
411 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
413 ret
= kcm_ccache_resolve_client(context
, client
, opcode
,
420 if (ccache
->client
== NULL
)
421 ret
= KRB5_CC_NOTFOUND
;
423 ret
= krb5_store_principal(response
, ccache
->client
);
426 kcm_release_ccache(context
, ccache
);
439 static krb5_error_code
440 kcm_op_get_cred_uuid_list(krb5_context context
,
442 kcm_operation opcode
,
443 krb5_storage
*request
,
444 krb5_storage
*response
)
446 struct kcm_creds
*creds
;
451 ret
= krb5_ret_stringz(request
, &name
);
455 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
457 ret
= kcm_ccache_resolve_client(context
, client
, opcode
,
463 for (creds
= ccache
->creds
; creds
; creds
= creds
->next
) {
465 sret
= krb5_storage_write(response
, &creds
->uuid
, sizeof(creds
->uuid
));
466 if (sret
!= sizeof(creds
->uuid
)) {
472 kcm_release_ccache(context
, ccache
);
485 static krb5_error_code
486 kcm_op_get_cred_by_uuid(krb5_context context
,
488 kcm_operation opcode
,
489 krb5_storage
*request
,
490 krb5_storage
*response
)
499 ret
= krb5_ret_stringz(request
, &name
);
503 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
505 ret
= kcm_ccache_resolve_client(context
, client
, opcode
,
511 sret
= krb5_storage_read(request
, &uuid
, sizeof(uuid
));
512 if (sret
!= sizeof(uuid
)) {
513 kcm_release_ccache(context
, ccache
);
514 krb5_clear_error_message(context
);
518 c
= kcm_ccache_find_cred_uuid(context
, ccache
, uuid
);
520 kcm_release_ccache(context
, ccache
);
524 HEIMDAL_MUTEX_lock(&ccache
->mutex
);
525 ret
= krb5_store_creds(response
, &c
->cred
);
526 HEIMDAL_MUTEX_unlock(&ccache
->mutex
);
528 kcm_release_ccache(context
, ccache
);
542 static krb5_error_code
543 kcm_op_remove_cred(krb5_context context
,
545 kcm_operation opcode
,
546 krb5_storage
*request
,
547 krb5_storage
*response
)
549 uint32_t whichfields
;
555 ret
= krb5_ret_stringz(request
, &name
);
559 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
561 ret
= krb5_ret_uint32(request
, &whichfields
);
567 ret
= krb5_ret_creds_tag(request
, &mcreds
);
573 ret
= kcm_ccache_resolve_client(context
, client
, opcode
,
577 krb5_free_cred_contents(context
, &mcreds
);
581 ret
= kcm_ccache_remove_cred(context
, ccache
, whichfields
, &mcreds
);
583 /* XXX need to remove any events that match */
586 krb5_free_cred_contents(context
, &mcreds
);
587 kcm_release_ccache(context
, ccache
);
600 static krb5_error_code
601 kcm_op_set_flags(krb5_context context
,
603 kcm_operation opcode
,
604 krb5_storage
*request
,
605 krb5_storage
*response
)
612 ret
= krb5_ret_stringz(request
, &name
);
616 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
618 ret
= krb5_ret_uint32(request
, &flags
);
624 ret
= kcm_ccache_resolve_client(context
, client
, opcode
,
631 /* we don't really support any flags yet */
633 kcm_release_ccache(context
, ccache
);
647 static krb5_error_code
648 kcm_op_chown(krb5_context context
,
650 kcm_operation opcode
,
651 krb5_storage
*request
,
652 krb5_storage
*response
)
660 ret
= krb5_ret_stringz(request
, &name
);
664 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
666 ret
= krb5_ret_uint32(request
, &uid
);
672 ret
= krb5_ret_uint32(request
, &gid
);
678 ret
= kcm_ccache_resolve_client(context
, client
, opcode
,
685 ret
= kcm_chown(context
, client
, ccache
, uid
, gid
);
688 kcm_release_ccache(context
, ccache
);
701 static krb5_error_code
702 kcm_op_chmod(krb5_context context
,
704 kcm_operation opcode
,
705 krb5_storage
*request
,
706 krb5_storage
*response
)
713 ret
= krb5_ret_stringz(request
, &name
);
717 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
719 ret
= krb5_ret_uint16(request
, &mode
);
725 ret
= kcm_ccache_resolve_client(context
, client
, opcode
,
732 ret
= kcm_chmod(context
, client
, ccache
, mode
);
735 kcm_release_ccache(context
, ccache
);
741 * Protocol extensions for moving ticket acquisition responsibility
742 * from client to KCM follow.
748 * ServerPrincipalPresent
749 * ServerPrincipal OPTIONAL
755 static krb5_error_code
756 kcm_op_get_initial_ticket(krb5_context context
,
758 kcm_operation opcode
,
759 krb5_storage
*request
,
760 krb5_storage
*response
)
766 krb5_principal server
= NULL
;
769 krb5_keyblock_zero(&key
);
771 ret
= krb5_ret_stringz(request
, &name
);
775 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
777 ret
= krb5_ret_int8(request
, ¬_tgt
);
784 ret
= krb5_ret_principal(request
, &server
);
791 ret
= krb5_ret_keyblock(request
, &key
);
795 krb5_free_principal(context
, server
);
799 ret
= kcm_ccache_resolve_client(context
, client
, opcode
,
802 HEIMDAL_MUTEX_lock(&ccache
->mutex
);
804 if (ccache
->server
!= NULL
) {
805 krb5_free_principal(context
, ccache
->server
);
806 ccache
->server
= NULL
;
809 krb5_free_keyblock(context
, &ccache
->key
.keyblock
);
811 ccache
->server
= server
;
812 ccache
->key
.keyblock
= key
;
813 ccache
->flags
|= KCM_FLAGS_USE_CACHED_KEY
;
815 ret
= kcm_ccache_enqueue_default(context
, ccache
, NULL
);
817 ccache
->server
= NULL
;
818 krb5_keyblock_zero(&ccache
->key
.keyblock
);
819 ccache
->flags
&= ~(KCM_FLAGS_USE_CACHED_KEY
);
822 HEIMDAL_MUTEX_unlock(&ccache
->mutex
);
828 krb5_free_principal(context
, server
);
829 krb5_free_keyblock(context
, &key
);
832 kcm_release_ccache(context
, ccache
);
847 static krb5_error_code
848 kcm_op_get_ticket(krb5_context context
,
850 kcm_operation opcode
,
851 krb5_storage
*request
,
852 krb5_storage
*response
)
857 krb5_principal server
= NULL
;
858 krb5_ccache_data ccdata
;
860 krb5_kdc_flags flags
;
862 memset(&in
, 0, sizeof(in
));
864 ret
= krb5_ret_stringz(request
, &name
);
868 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
870 ret
= krb5_ret_uint32(request
, &flags
.i
);
876 ret
= krb5_ret_int32(request
, &in
.session
.keytype
);
882 ret
= krb5_ret_principal(request
, &server
);
888 ret
= kcm_ccache_resolve_client(context
, client
, opcode
,
891 krb5_free_principal(context
, server
);
896 HEIMDAL_MUTEX_lock(&ccache
->mutex
);
898 /* Fake up an internal ccache */
899 kcm_internal_ccache(context
, ccache
, &ccdata
);
901 in
.client
= ccache
->client
;
903 in
.times
.endtime
= 0;
905 /* glue cc layer will store creds */
906 ret
= krb5_get_credentials_with_flags(context
, 0, flags
,
909 HEIMDAL_MUTEX_unlock(&ccache
->mutex
);
911 krb5_free_principal(context
, server
);
914 krb5_free_cred_contents(context
, out
);
916 kcm_release_ccache(context
, ccache
);
930 static krb5_error_code
931 kcm_op_move_cache(krb5_context context
,
933 kcm_operation opcode
,
934 krb5_storage
*request
,
935 krb5_storage
*response
)
938 kcm_ccache oldid
, newid
;
939 char *oldname
, *newname
;
941 ret
= krb5_ret_stringz(request
, &oldname
);
945 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, oldname
);
947 ret
= krb5_ret_stringz(request
, &newname
);
953 /* move to ourself is simple, done! */
954 if (strcmp(oldname
, newname
) == 0) {
960 ret
= kcm_ccache_resolve_client(context
, client
, opcode
, oldname
, &oldid
);
967 /* Check if new credential cache exists, if not create one. */
968 ret
= kcm_ccache_resolve_client(context
, client
, opcode
, newname
, &newid
);
969 if (ret
== KRB5_FCC_NOFILE
)
970 ret
= kcm_ccache_new_client(context
, client
, newname
, &newid
);
975 kcm_release_ccache(context
, oldid
);
979 HEIMDAL_MUTEX_lock(&oldid
->mutex
);
980 HEIMDAL_MUTEX_lock(&newid
->mutex
);
986 #define MOVE(n,o,f) { tmp.f = n->f ; n->f = o->f; o->f = tmp.f; }
988 MOVE(newid
, oldid
, flags
);
989 MOVE(newid
, oldid
, client
);
990 MOVE(newid
, oldid
, server
);
991 MOVE(newid
, oldid
, creds
);
992 MOVE(newid
, oldid
, tkt_life
);
993 MOVE(newid
, oldid
, renew_life
);
994 MOVE(newid
, oldid
, key
);
995 MOVE(newid
, oldid
, kdc_offset
);
999 HEIMDAL_MUTEX_unlock(&oldid
->mutex
);
1000 HEIMDAL_MUTEX_unlock(&newid
->mutex
);
1002 kcm_release_ccache(context
, oldid
);
1003 kcm_release_ccache(context
, newid
);
1005 ret
= kcm_ccache_destroy_client(context
, client
, oldname
);
1007 kcm_drop_default_cache(context
, client
, oldname
);
1014 static krb5_error_code
1015 kcm_op_get_cache_uuid_list(krb5_context context
,
1017 kcm_operation opcode
,
1018 krb5_storage
*request
,
1019 krb5_storage
*response
)
1021 KCM_LOG_REQUEST(context
, client
, opcode
);
1023 return kcm_ccache_get_uuids(context
, client
, opcode
, response
);
1026 static krb5_error_code
1027 kcm_op_get_cache_by_uuid(krb5_context context
,
1029 kcm_operation opcode
,
1030 krb5_storage
*request
,
1031 krb5_storage
*response
)
1033 krb5_error_code ret
;
1038 KCM_LOG_REQUEST(context
, client
, opcode
);
1040 sret
= krb5_storage_read(request
, &uuid
, sizeof(uuid
));
1041 if (sret
!= sizeof(uuid
)) {
1042 krb5_clear_error_message(context
);
1046 ret
= kcm_ccache_resolve_by_uuid(context
, uuid
, &cache
);
1050 ret
= kcm_access(context
, client
, opcode
, cache
);
1052 ret
= KRB5_FCC_NOFILE
;
1055 ret
= krb5_store_stringz(response
, cache
->name
);
1057 kcm_release_ccache(context
, cache
);
1062 struct kcm_default_cache
*default_caches
;
1064 static krb5_error_code
1065 kcm_op_get_default_cache(krb5_context context
,
1067 kcm_operation opcode
,
1068 krb5_storage
*request
,
1069 krb5_storage
*response
)
1071 struct kcm_default_cache
*c
;
1072 krb5_error_code ret
;
1073 const char *name
= NULL
;
1076 KCM_LOG_REQUEST(context
, client
, opcode
);
1078 for (c
= default_caches
; c
!= NULL
; c
= c
->next
) {
1079 if (kcm_is_same_session(client
, c
->uid
, c
->session
)) {
1085 name
= n
= kcm_ccache_first_name(client
);
1088 asprintf(&n
, "%d", (int)client
->uid
);
1093 ret
= krb5_store_stringz(response
, name
);
1100 kcm_drop_default_cache(krb5_context context
, kcm_client
*client
, char *name
)
1102 struct kcm_default_cache
**c
;
1104 for (c
= &default_caches
; *c
!= NULL
; c
= &(*c
)->next
) {
1105 if (!kcm_is_same_session(client
, (*c
)->uid
, (*c
)->session
))
1107 if (strcmp((*c
)->name
, name
) == 0) {
1108 struct kcm_default_cache
*h
= *c
;
1117 static krb5_error_code
1118 kcm_op_set_default_cache(krb5_context context
,
1120 kcm_operation opcode
,
1121 krb5_storage
*request
,
1122 krb5_storage
*response
)
1124 struct kcm_default_cache
*c
;
1125 krb5_error_code ret
;
1128 ret
= krb5_ret_stringz(request
, &name
);
1132 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
1134 for (c
= default_caches
; c
!= NULL
; c
= c
->next
) {
1135 if (kcm_is_same_session(client
, c
->uid
, c
->session
))
1139 c
= malloc(sizeof(*c
));
1142 c
->session
= client
->session
;
1143 c
->uid
= client
->uid
;
1144 c
->name
= strdup(name
);
1146 c
->next
= default_caches
;
1150 c
->name
= strdup(name
);
1156 static krb5_error_code
1157 kcm_op_get_kdc_offset(krb5_context context
,
1159 kcm_operation opcode
,
1160 krb5_storage
*request
,
1161 krb5_storage
*response
)
1163 krb5_error_code ret
;
1167 ret
= krb5_ret_stringz(request
, &name
);
1171 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
1173 ret
= kcm_ccache_resolve_client(context
, client
, opcode
, name
, &ccache
);
1178 HEIMDAL_MUTEX_lock(&ccache
->mutex
);
1179 ret
= krb5_store_int32(response
, ccache
->kdc_offset
);
1180 HEIMDAL_MUTEX_unlock(&ccache
->mutex
);
1182 kcm_release_ccache(context
, ccache
);
1187 static krb5_error_code
1188 kcm_op_set_kdc_offset(krb5_context context
,
1190 kcm_operation opcode
,
1191 krb5_storage
*request
,
1192 krb5_storage
*response
)
1194 krb5_error_code ret
;
1199 ret
= krb5_ret_stringz(request
, &name
);
1203 KCM_LOG_REQUEST_NAME(context
, client
, opcode
, name
);
1205 ret
= krb5_ret_int32(request
, &offset
);
1211 ret
= kcm_ccache_resolve_client(context
, client
, opcode
, name
, &ccache
);
1216 HEIMDAL_MUTEX_lock(&ccache
->mutex
);
1217 ccache
->kdc_offset
= offset
;
1218 HEIMDAL_MUTEX_unlock(&ccache
->mutex
);
1220 kcm_release_ccache(context
, ccache
);
1225 struct kcm_ntlm_cred
{
1232 struct kcm_ntlm_cred
*next
;
1235 static struct kcm_ntlm_cred
*ntlm_head
;
1238 free_cred(struct kcm_ntlm_cred
*cred
)
1242 krb5_data_free(&cred
->nthash
);
1256 static struct kcm_ntlm_cred
*
1257 find_ntlm_cred(const char *user
, const char *domain
, kcm_client
*client
)
1259 struct kcm_ntlm_cred
*c
;
1261 for (c
= ntlm_head
; c
!= NULL
; c
= c
->next
)
1262 if ((user
[0] == '\0' || strcmp(user
, c
->user
) == 0) &&
1263 (domain
== NULL
|| strcmp(domain
, c
->domain
) == 0) &&
1264 kcm_is_same_session(client
, c
->uid
, c
->session
))
1270 static krb5_error_code
1271 kcm_op_add_ntlm_cred(krb5_context context
,
1273 kcm_operation opcode
,
1274 krb5_storage
*request
,
1275 krb5_storage
*response
)
1277 struct kcm_ntlm_cred
*cred
, *c
;
1278 krb5_error_code ret
;
1280 cred
= calloc(1, sizeof(*cred
));
1284 RAND_bytes(cred
->uuid
, sizeof(cred
->uuid
));
1286 ret
= krb5_ret_stringz(request
, &cred
->user
);
1290 ret
= krb5_ret_stringz(request
, &cred
->domain
);
1294 ret
= krb5_ret_data(request
, &cred
->nthash
);
1298 /* search for dups */
1299 c
= find_ntlm_cred(cred
->user
, cred
->domain
, client
);
1301 krb5_data hash
= c
->nthash
;
1302 c
->nthash
= cred
->nthash
;
1303 cred
->nthash
= hash
;
1307 cred
->next
= ntlm_head
;
1311 cred
->uid
= client
->uid
;
1312 cred
->session
= client
->session
;
1314 /* write response */
1315 (void)krb5_storage_write(response
, &cred
->uuid
, sizeof(cred
->uuid
));
1326 * { "HAVE_NTLM_CRED", NULL },
1333 static krb5_error_code
1334 kcm_op_have_ntlm_cred(krb5_context context
,
1336 kcm_operation opcode
,
1337 krb5_storage
*request
,
1338 krb5_storage
*response
)
1340 struct kcm_ntlm_cred
*c
;
1341 char *user
= NULL
, *domain
= NULL
;
1342 krb5_error_code ret
;
1344 ret
= krb5_ret_stringz(request
, &user
);
1348 ret
= krb5_ret_stringz(request
, &domain
);
1352 if (domain
[0] == '\0') {
1357 c
= find_ntlm_cred(user
, domain
, client
);
1370 * { "DEL_NTLM_CRED", NULL },
1377 static krb5_error_code
1378 kcm_op_del_ntlm_cred(krb5_context context
,
1380 kcm_operation opcode
,
1381 krb5_storage
*request
,
1382 krb5_storage
*response
)
1384 struct kcm_ntlm_cred
**cp
, *c
;
1385 char *user
= NULL
, *domain
= NULL
;
1386 krb5_error_code ret
;
1388 ret
= krb5_ret_stringz(request
, &user
);
1392 ret
= krb5_ret_stringz(request
, &domain
);
1396 for (cp
= &ntlm_head
; *cp
!= NULL
; cp
= &(*cp
)->next
) {
1397 if (strcmp(user
, (*cp
)->user
) == 0 && strcmp(domain
, (*cp
)->domain
) == 0 &&
1398 kcm_is_same_session(client
, (*cp
)->uid
, (*cp
)->session
))
1416 * { "DO_NTLM_AUTH", NULL },
1429 #define NTLM_FLAG_SESSIONKEY 1
1430 #define NTLM_FLAG_NTLM2_SESSION 2
1431 #define NTLM_FLAG_KEYEX 4
1433 static krb5_error_code
1434 kcm_op_do_ntlm(krb5_context context
,
1436 kcm_operation opcode
,
1437 krb5_storage
*request
,
1438 krb5_storage
*response
)
1440 struct kcm_ntlm_cred
*c
;
1441 struct ntlm_type2 type2
;
1442 struct ntlm_type3 type3
;
1443 char *user
= NULL
, *domain
= NULL
;
1444 struct ntlm_buf ndata
, sessionkey
;
1446 krb5_error_code ret
;
1449 memset(&type2
, 0, sizeof(type2
));
1450 memset(&type3
, 0, sizeof(type3
));
1451 sessionkey
.data
= NULL
;
1452 sessionkey
.length
= 0;
1454 ret
= krb5_ret_stringz(request
, &user
);
1458 ret
= krb5_ret_stringz(request
, &domain
);
1462 if (domain
[0] == '\0') {
1467 c
= find_ntlm_cred(user
, domain
, client
);
1473 ret
= krb5_ret_data(request
, &data
);
1477 ndata
.data
= data
.data
;
1478 ndata
.length
= data
.length
;
1480 ret
= heim_ntlm_decode_type2(&ndata
, &type2
);
1481 krb5_data_free(&data
);
1485 if (domain
&& strcmp(domain
, type2
.targetname
) == 0) {
1490 type3
.username
= c
->user
;
1491 type3
.flags
= type2
.flags
;
1492 type3
.targetname
= type2
.targetname
;
1493 type3
.ws
= rk_UNCONST("workstation");
1496 * NTLM Version 1 if no targetinfo buffer.
1499 if (1 || type2
.targetinfo
.length
== 0) {
1500 struct ntlm_buf sessionkey
;
1502 if (type2
.flags
& NTLM_NEG_NTLM2_SESSION
) {
1503 unsigned char nonce
[8];
1505 if (RAND_bytes(nonce
, sizeof(nonce
)) != 1) {
1510 ret
= heim_ntlm_calculate_ntlm2_sess(nonce
,
1516 ret
= heim_ntlm_calculate_ntlm1(c
->nthash
.data
,
1525 ret
= heim_ntlm_build_ntlm1_master(c
->nthash
.data
,
1531 free(type3
.lm
.data
);
1532 if (type3
.ntlm
.data
)
1533 free(type3
.ntlm
.data
);
1537 free(sessionkey
.data
);
1540 free(type3
.lm
.data
);
1541 if (type3
.ntlm
.data
)
1542 free(type3
.ntlm
.data
);
1545 flags
|= NTLM_FLAG_SESSIONKEY
;
1548 struct ntlm_buf sessionkey
;
1549 unsigned char ntlmv2
[16];
1550 struct ntlm_targetinfo ti
;
1552 /* verify infotarget */
1554 ret
= heim_ntlm_decode_targetinfo(&type2
.targetinfo
, 1, &ti
);
1556 _gss_ntlm_delete_sec_context(minor_status
,
1557 context_handle
, NULL
);
1558 *minor_status
= ret
;
1559 return GSS_S_FAILURE
;
1562 if (ti
.domainname
&& strcmp(ti
.domainname
, name
->domain
) != 0) {
1563 _gss_ntlm_delete_sec_context(minor_status
,
1564 context_handle
, NULL
);
1565 *minor_status
= EINVAL
;
1566 return GSS_S_FAILURE
;
1569 ret
= heim_ntlm_calculate_ntlm2(ctx
->client
->key
.data
,
1570 ctx
->client
->key
.length
,
1578 _gss_ntlm_delete_sec_context(minor_status
,
1579 context_handle
, NULL
);
1580 *minor_status
= ret
;
1581 return GSS_S_FAILURE
;
1584 ret
= heim_ntlm_build_ntlm1_master(ntlmv2
, sizeof(ntlmv2
),
1587 memset(ntlmv2
, 0, sizeof(ntlmv2
));
1589 _gss_ntlm_delete_sec_context(minor_status
,
1590 context_handle
, NULL
);
1591 *minor_status
= ret
;
1592 return GSS_S_FAILURE
;
1595 flags
|= NTLM_FLAG_NTLM2_SESSION
|
1598 if (type3
.flags
& NTLM_NEG_KEYEX
)
1599 flags
|= NTLM_FLAG_KEYEX
;
1601 ret
= krb5_data_copy(&ctx
->sessionkey
,
1602 sessionkey
.data
, sessionkey
.length
);
1603 free(sessionkey
.data
);
1605 _gss_ntlm_delete_sec_context(minor_status
,
1606 context_handle
, NULL
);
1607 *minor_status
= ret
;
1608 return GSS_S_FAILURE
;
1614 if (flags
& NTLM_FLAG_NTLM2_SESSION
) {
1615 _gss_ntlm_set_key(&ctx
->u
.v2
.send
, 0, (ctx
->flags
& NTLM_NEG_KEYEX
),
1616 ctx
->sessionkey
.data
,
1617 ctx
->sessionkey
.length
);
1618 _gss_ntlm_set_key(&ctx
->u
.v2
.recv
, 1, (ctx
->flags
& NTLM_NEG_KEYEX
),
1619 ctx
->sessionkey
.data
,
1620 ctx
->sessionkey
.length
);
1622 flags
|= NTLM_FLAG_SESSION
;
1623 RC4_set_key(&ctx
->u
.v1
.crypto_recv
.key
,
1624 ctx
->sessionkey
.length
,
1625 ctx
->sessionkey
.data
);
1626 RC4_set_key(&ctx
->u
.v1
.crypto_send
.key
,
1627 ctx
->sessionkey
.length
,
1628 ctx
->sessionkey
.data
);
1632 ret
= heim_ntlm_encode_type3(&type3
, &ndata
);
1636 data
.data
= ndata
.data
;
1637 data
.length
= ndata
.length
;
1638 ret
= krb5_store_data(response
, data
);
1639 heim_ntlm_free_buf(&ndata
);
1640 if (ret
) goto error
;
1642 ret
= krb5_store_int32(response
, flags
);
1643 if (ret
) goto error
;
1645 data
.data
= sessionkey
.data
;
1646 data
.length
= sessionkey
.length
;
1648 ret
= krb5_store_data(response
, data
);
1649 if (ret
) goto error
;
1652 free(type3
.username
);
1653 heim_ntlm_free_type2(&type2
);
1663 * { "GET_NTLM_UUID_LIST", NULL }
1670 static krb5_error_code
1671 kcm_op_get_ntlm_user_list(krb5_context context
,
1673 kcm_operation opcode
,
1674 krb5_storage
*request
,
1675 krb5_storage
*response
)
1677 struct kcm_ntlm_cred
*c
;
1678 krb5_error_code ret
;
1680 for (c
= ntlm_head
; c
!= NULL
; c
= c
->next
) {
1681 if (!kcm_is_same_session(client
, c
->uid
, c
->session
))
1684 ret
= krb5_store_uint32(response
, 1);
1687 ret
= krb5_store_stringz(response
, c
->user
);
1690 ret
= krb5_store_stringz(response
, c
->domain
);
1694 return krb5_store_uint32(response
, 0);
1701 static struct kcm_op kcm_ops
[] = {
1702 { "NOOP", kcm_op_noop
},
1703 { "GET_NAME", kcm_op_get_name
},
1704 { "RESOLVE", kcm_op_noop
},
1705 { "GEN_NEW", kcm_op_gen_new
},
1706 { "INITIALIZE", kcm_op_initialize
},
1707 { "DESTROY", kcm_op_destroy
},
1708 { "STORE", kcm_op_store
},
1709 { "RETRIEVE", kcm_op_retrieve
},
1710 { "GET_PRINCIPAL", kcm_op_get_principal
},
1711 { "GET_CRED_UUID_LIST", kcm_op_get_cred_uuid_list
},
1712 { "GET_CRED_BY_UUID", kcm_op_get_cred_by_uuid
},
1713 { "REMOVE_CRED", kcm_op_remove_cred
},
1714 { "SET_FLAGS", kcm_op_set_flags
},
1715 { "CHOWN", kcm_op_chown
},
1716 { "CHMOD", kcm_op_chmod
},
1717 { "GET_INITIAL_TICKET", kcm_op_get_initial_ticket
},
1718 { "GET_TICKET", kcm_op_get_ticket
},
1719 { "MOVE_CACHE", kcm_op_move_cache
},
1720 { "GET_CACHE_UUID_LIST", kcm_op_get_cache_uuid_list
},
1721 { "GET_CACHE_BY_UUID", kcm_op_get_cache_by_uuid
},
1722 { "GET_DEFAULT_CACHE", kcm_op_get_default_cache
},
1723 { "SET_DEFAULT_CACHE", kcm_op_set_default_cache
},
1724 { "GET_KDC_OFFSET", kcm_op_get_kdc_offset
},
1725 { "SET_KDC_OFFSET", kcm_op_set_kdc_offset
},
1726 { "ADD_NTLM_CRED", kcm_op_add_ntlm_cred
},
1727 { "HAVE_USER_CRED", kcm_op_have_ntlm_cred
},
1728 { "DEL_NTLM_CRED", kcm_op_del_ntlm_cred
},
1729 { "DO_NTLM_AUTH", kcm_op_do_ntlm
},
1730 { "GET_NTLM_USER_LIST", kcm_op_get_ntlm_user_list
}
1735 kcm_op2string(kcm_operation opcode
)
1737 if (opcode
>= sizeof(kcm_ops
)/sizeof(kcm_ops
[0]))
1738 return "Unknown operation";
1740 return kcm_ops
[opcode
].name
;
1744 kcm_dispatch(krb5_context context
,
1746 krb5_data
*req_data
,
1747 krb5_data
*resp_data
)
1749 krb5_error_code ret
;
1751 krb5_storage
*req_sp
= NULL
;
1752 krb5_storage
*resp_sp
= NULL
;
1755 resp_sp
= krb5_storage_emem();
1756 if (resp_sp
== NULL
) {
1760 if (client
->pid
== -1) {
1761 kcm_log(0, "Client had invalid process number");
1762 ret
= KRB5_FCC_INTERNAL
;
1766 req_sp
= krb5_storage_from_data(req_data
);
1767 if (req_sp
== NULL
) {
1768 kcm_log(0, "Process %d: failed to initialize storage from data",
1774 ret
= krb5_ret_uint16(req_sp
, &opcode
);
1776 kcm_log(0, "Process %d: didn't send a message", client
->pid
);
1780 if (opcode
>= sizeof(kcm_ops
)/sizeof(kcm_ops
[0])) {
1781 kcm_log(0, "Process %d: invalid operation code %d",
1782 client
->pid
, opcode
);
1783 ret
= KRB5_FCC_INTERNAL
;
1786 method
= kcm_ops
[opcode
].method
;
1787 if (method
== NULL
) {
1788 kcm_log(0, "Process %d: operation code %s not implemented",
1789 client
->pid
, kcm_op2string(opcode
));
1790 ret
= KRB5_FCC_INTERNAL
;
1794 /* seek past place for status code */
1795 krb5_storage_seek(resp_sp
, 4, SEEK_SET
);
1797 ret
= (*method
)(context
, client
, opcode
, req_sp
, resp_sp
);
1800 if (req_sp
!= NULL
) {
1801 krb5_storage_free(req_sp
);
1804 krb5_storage_seek(resp_sp
, 0, SEEK_SET
);
1805 krb5_store_int32(resp_sp
, ret
);
1807 ret
= krb5_storage_to_data(resp_sp
, resp_data
);
1808 krb5_storage_free(resp_sp
);