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 ret = kcm_ccache_resolve_client(context, client, opcode, oldname, &oldid);
958 /* Check if new credential cache exists, if not create one. */
959 ret = kcm_ccache_resolve_client(context, client, opcode, newname, &newid);
960 if (ret == KRB5_FCC_NOFILE)
961 ret = kcm_ccache_new_client(context, client, newname, &newid);
966 kcm_release_ccache(context, oldid);
970 HEIMDAL_MUTEX_lock(&oldid->mutex);
971 HEIMDAL_MUTEX_lock(&newid->mutex);
977 #define MOVE(n,o,f) { tmp.f = n->f ; n->f = o->f; o->f = tmp.f; }
979 MOVE(newid, oldid, flags);
980 MOVE(newid, oldid, client);
981 MOVE(newid, oldid, server);
982 MOVE(newid, oldid, creds);
983 MOVE(newid, oldid, tkt_life);
984 MOVE(newid, oldid, renew_life);
985 MOVE(newid, oldid, key);
986 MOVE(newid, oldid, kdc_offset);
990 HEIMDAL_MUTEX_unlock(&oldid->mutex);
991 HEIMDAL_MUTEX_unlock(&newid->mutex);
993 kcm_release_ccache(context, oldid);
994 kcm_release_ccache(context, newid);
996 ret = kcm_ccache_destroy_client(context, client, oldname);
998 kcm_drop_default_cache(context, client, oldname);
1005 static krb5_error_code
1006 kcm_op_get_cache_uuid_list(krb5_context context,
1008 kcm_operation opcode,
1009 krb5_storage *request,
1010 krb5_storage *response)
1012 KCM_LOG_REQUEST(context, client, opcode);
1014 return kcm_ccache_get_uuids(context, client, opcode, response);
1017 static krb5_error_code
1018 kcm_op_get_cache_by_uuid(krb5_context context,
1020 kcm_operation opcode,
1021 krb5_storage *request,
1022 krb5_storage *response)
1024 krb5_error_code ret;
1029 KCM_LOG_REQUEST(context, client, opcode);
1031 sret = krb5_storage_read(request, &uuid, sizeof(uuid));
1032 if (sret != sizeof(uuid)) {
1033 krb5_clear_error_message(context);
1037 ret = kcm_ccache_resolve_by_uuid(context, uuid, &cache);
1041 ret = kcm_access(context, client, opcode, cache);
1043 ret = KRB5_FCC_NOFILE;
1046 ret = krb5_store_stringz(response, cache->name);
1048 kcm_release_ccache(context, cache);
1053 struct kcm_default_cache *default_caches;
1055 static krb5_error_code
1056 kcm_op_get_default_cache(krb5_context context,
1058 kcm_operation opcode,
1059 krb5_storage *request,
1060 krb5_storage *response)
1062 struct kcm_default_cache *c;
1063 krb5_error_code ret;
1064 const char *name = NULL;
1067 KCM_LOG_REQUEST(context, client, opcode);
1069 for (c = default_caches; c != NULL; c = c->next) {
1070 if (kcm_is_same_session(client, c->uid, c->session)) {
1076 name = n = kcm_ccache_first_name(client);
1079 asprintf(&n, "%d", (int)client->uid);
1084 ret = krb5_store_stringz(response, name);
1091 kcm_drop_default_cache(krb5_context context, kcm_client *client, char *name)
1093 struct kcm_default_cache **c;
1095 for (c = &default_caches; *c != NULL; c = &(*c)->next) {
1096 if (!kcm_is_same_session(client, (*c)->uid, (*c)->session))
1098 if (strcmp((*c)->name, name) == 0) {
1099 struct kcm_default_cache *h = *c;
1108 static krb5_error_code
1109 kcm_op_set_default_cache(krb5_context context,
1111 kcm_operation opcode,
1112 krb5_storage *request,
1113 krb5_storage *response)
1115 struct kcm_default_cache *c;
1116 krb5_error_code ret;
1119 ret = krb5_ret_stringz(request, &name);
1123 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
1125 for (c = default_caches; c != NULL; c = c->next) {
1126 if (kcm_is_same_session(client, c->uid, c->session))
1130 c = malloc(sizeof(*c));
1133 c->session = client->session;
1134 c->uid = client->uid;
1135 c->name = strdup(name);
1137 c->next = default_caches;
1141 c->name = strdup(name);
1147 static krb5_error_code
1148 kcm_op_get_kdc_offset(krb5_context context,
1150 kcm_operation opcode,
1151 krb5_storage *request,
1152 krb5_storage *response)
1154 krb5_error_code ret;
1158 ret = krb5_ret_stringz(request, &name);
1162 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
1164 ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache);
1169 HEIMDAL_MUTEX_lock(&ccache->mutex);
1170 ret = krb5_store_int32(response, ccache->kdc_offset);
1171 HEIMDAL_MUTEX_unlock(&ccache->mutex);
1173 kcm_release_ccache(context, ccache);
1178 static krb5_error_code
1179 kcm_op_set_kdc_offset(krb5_context context,
1181 kcm_operation opcode,
1182 krb5_storage *request,
1183 krb5_storage *response)
1185 krb5_error_code ret;
1190 ret = krb5_ret_stringz(request, &name);
1194 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
1196 ret = krb5_ret_int32(request, &offset);
1202 ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache);
1207 HEIMDAL_MUTEX_lock(&ccache->mutex);
1208 ccache->kdc_offset = offset;
1209 HEIMDAL_MUTEX_unlock(&ccache->mutex);
1211 kcm_release_ccache(context, ccache);
1216 struct kcm_ntlm_cred {
1223 struct kcm_ntlm_cred *next;
1226 static struct kcm_ntlm_cred *ntlm_head;
1229 free_cred(struct kcm_ntlm_cred *cred)
1233 krb5_data_free(&cred->nthash);
1247 static struct kcm_ntlm_cred *
1248 find_ntlm_cred(const char *user, const char *domain, kcm_client *client)
1250 struct kcm_ntlm_cred *c;
1252 for (c = ntlm_head; c != NULL; c = c->next)
1253 if ((user[0] == '\0' || strcmp(user, c->user) == 0) &&
1254 (domain == NULL || strcmp(domain, c->domain) == 0) &&
1255 kcm_is_same_session(client, c->uid, c->session))
1261 static krb5_error_code
1262 kcm_op_add_ntlm_cred(krb5_context context,
1264 kcm_operation opcode,
1265 krb5_storage *request,
1266 krb5_storage *response)
1268 struct kcm_ntlm_cred *cred, *c;
1269 krb5_error_code ret;
1271 cred = calloc(1, sizeof(*cred));
1275 RAND_bytes(cred->uuid, sizeof(cred->uuid));
1277 ret = krb5_ret_stringz(request, &cred->user);
1281 ret = krb5_ret_stringz(request, &cred->domain);
1285 ret = krb5_ret_data(request, &cred->nthash);
1289 /* search for dups */
1290 c = find_ntlm_cred(cred->user, cred->domain, client);
1292 krb5_data hash = c->nthash;
1293 c->nthash = cred->nthash;
1294 cred->nthash = hash;
1298 cred->next = ntlm_head;
1302 cred->uid = client->uid;
1303 cred->session = client->session;
1305 /* write response */
1306 (void)krb5_storage_write(response, &cred->uuid, sizeof(cred->uuid));
1317 * { "HAVE_NTLM_CRED", NULL },
1324 static krb5_error_code
1325 kcm_op_have_ntlm_cred(krb5_context context,
1327 kcm_operation opcode,
1328 krb5_storage *request,
1329 krb5_storage *response)
1331 struct kcm_ntlm_cred *c;
1332 char *user = NULL, *domain = NULL;
1333 krb5_error_code ret;
1335 ret = krb5_ret_stringz(request, &user);
1339 ret = krb5_ret_stringz(request, &domain);
1343 if (domain[0] == '\0') {
1348 c = find_ntlm_cred(user, domain, client);
1361 * { "DEL_NTLM_CRED", NULL },
1368 static krb5_error_code
1369 kcm_op_del_ntlm_cred(krb5_context context,
1371 kcm_operation opcode,
1372 krb5_storage *request,
1373 krb5_storage *response)
1375 struct kcm_ntlm_cred **cp, *c;
1376 char *user = NULL, *domain = NULL;
1377 krb5_error_code ret;
1379 ret = krb5_ret_stringz(request, &user);
1383 ret = krb5_ret_stringz(request, &domain);
1387 for (cp = &ntlm_head; *cp != NULL; cp = &(*cp)->next) {
1388 if (strcmp(user, (*cp)->user) == 0 && strcmp(domain, (*cp)->domain) == 0 &&
1389 kcm_is_same_session(client, (*cp)->uid, (*cp)->session))
1407 * { "DO_NTLM_AUTH", NULL },
1420 #define NTLM_FLAG_SESSIONKEY 1
1421 #define NTLM_FLAG_NTLM2_SESSION 2
1422 #define NTLM_FLAG_KEYEX 4
1424 static krb5_error_code
1425 kcm_op_do_ntlm(krb5_context context,
1427 kcm_operation opcode,
1428 krb5_storage *request,
1429 krb5_storage *response)
1431 struct kcm_ntlm_cred *c;
1432 struct ntlm_type2 type2;
1433 struct ntlm_type3 type3;
1434 char *user = NULL, *domain = NULL;
1435 struct ntlm_buf ndata, sessionkey;
1437 krb5_error_code ret;
1440 memset(&type2, 0, sizeof(type2));
1441 memset(&type3, 0, sizeof(type3));
1442 sessionkey.data = NULL;
1443 sessionkey.length = 0;
1445 ret = krb5_ret_stringz(request, &user);
1449 ret = krb5_ret_stringz(request, &domain);
1453 if (domain[0] == '\0') {
1458 c = find_ntlm_cred(user, domain, client);
1464 ret = krb5_ret_data(request, &data);
1468 ndata.data = data.data;
1469 ndata.length = data.length;
1471 ret = heim_ntlm_decode_type2(&ndata, &type2);
1472 krb5_data_free(&data);
1476 if (domain && strcmp(domain, type2.targetname) == 0) {
1481 type3.username = c->user;
1482 type3.flags = type2.flags;
1483 type3.targetname = type2.targetname;
1484 type3.ws = rk_UNCONST("workstation");
1487 * NTLM Version 1 if no targetinfo buffer.
1490 if (1 || type2.targetinfo.length == 0) {
1491 struct ntlm_buf sessionkey;
1493 if (type2.flags & NTLM_NEG_NTLM2_SESSION) {
1494 unsigned char nonce[8];
1496 if (RAND_bytes(nonce, sizeof(nonce)) != 1) {
1501 ret = heim_ntlm_calculate_ntlm2_sess(nonce,
1507 ret = heim_ntlm_calculate_ntlm1(c->nthash.data,
1516 ret = heim_ntlm_build_ntlm1_master(c->nthash.data,
1522 free(type3.lm.data);
1523 if (type3.ntlm.data)
1524 free(type3.ntlm.data);
1528 free(sessionkey.data);
1531 free(type3.lm.data);
1532 if (type3.ntlm.data)
1533 free(type3.ntlm.data);
1536 flags |= NTLM_FLAG_SESSIONKEY;
1539 struct ntlm_buf sessionkey;
1540 unsigned char ntlmv2[16];
1541 struct ntlm_targetinfo ti;
1543 /* verify infotarget */
1545 ret = heim_ntlm_decode_targetinfo(&type2.targetinfo, 1, &ti);
1547 _gss_ntlm_delete_sec_context(minor_status,
1548 context_handle, NULL);
1549 *minor_status = ret;
1550 return GSS_S_FAILURE;
1553 if (ti.domainname && strcmp(ti.domainname, name->domain) != 0) {
1554 _gss_ntlm_delete_sec_context(minor_status,
1555 context_handle, NULL);
1556 *minor_status = EINVAL;
1557 return GSS_S_FAILURE;
1560 ret = heim_ntlm_calculate_ntlm2(ctx->client->key.data,
1561 ctx->client->key.length,
1569 _gss_ntlm_delete_sec_context(minor_status,
1570 context_handle, NULL);
1571 *minor_status = ret;
1572 return GSS_S_FAILURE;
1575 ret = heim_ntlm_build_ntlm1_master(ntlmv2, sizeof(ntlmv2),
1578 memset(ntlmv2, 0, sizeof(ntlmv2));
1580 _gss_ntlm_delete_sec_context(minor_status,
1581 context_handle, NULL);
1582 *minor_status = ret;
1583 return GSS_S_FAILURE;
1586 flags |= NTLM_FLAG_NTLM2_SESSION |
1589 if (type3.flags & NTLM_NEG_KEYEX)
1590 flags |= NTLM_FLAG_KEYEX;
1592 ret = krb5_data_copy(&ctx->sessionkey,
1593 sessionkey.data, sessionkey.length);
1594 free(sessionkey.data);
1596 _gss_ntlm_delete_sec_context(minor_status,
1597 context_handle, NULL);
1598 *minor_status = ret;
1599 return GSS_S_FAILURE;
1605 if (flags & NTLM_FLAG_NTLM2_SESSION) {
1606 _gss_ntlm_set_key(&ctx->u.v2.send, 0, (ctx->flags & NTLM_NEG_KEYEX),
1607 ctx->sessionkey.data,
1608 ctx->sessionkey.length);
1609 _gss_ntlm_set_key(&ctx->u.v2.recv, 1, (ctx->flags & NTLM_NEG_KEYEX),
1610 ctx->sessionkey.data,
1611 ctx->sessionkey.length);
1613 flags |= NTLM_FLAG_SESSION;
1614 RC4_set_key(&ctx->u.v1.crypto_recv.key,
1615 ctx->sessionkey.length,
1616 ctx->sessionkey.data);
1617 RC4_set_key(&ctx->u.v1.crypto_send.key,
1618 ctx->sessionkey.length,
1619 ctx->sessionkey.data);
1623 ret = heim_ntlm_encode_type3(&type3, &ndata);
1627 data.data = ndata.data;
1628 data.length = ndata.length;
1629 ret = krb5_store_data(response, data);
1630 heim_ntlm_free_buf(&ndata);
1631 if (ret) goto error;
1633 ret = krb5_store_int32(response, flags);
1634 if (ret) goto error;
1636 data.data = sessionkey.data;
1637 data.length = sessionkey.length;
1639 ret = krb5_store_data(response, data);
1640 if (ret) goto error;
1643 free(type3.username);
1644 heim_ntlm_free_type2(&type2);
1654 * { "GET_NTLM_UUID_LIST", NULL }
1661 static krb5_error_code
1662 kcm_op_get_ntlm_user_list(krb5_context context,
1664 kcm_operation opcode,
1665 krb5_storage *request,
1666 krb5_storage *response)
1668 struct kcm_ntlm_cred *c;
1669 krb5_error_code ret;
1671 for (c = ntlm_head; c != NULL; c = c->next) {
1672 if (!kcm_is_same_session(client, c->uid, c->session))
1675 ret = krb5_store_uint32(response, 1);
1678 ret = krb5_store_stringz(response, c->user);
1681 ret = krb5_store_stringz(response, c->domain);
1685 return krb5_store_uint32(response, 0);
1692 static struct kcm_op kcm_ops[] = {
1693 { "NOOP", kcm_op_noop },
1694 { "GET_NAME", kcm_op_get_name },
1695 { "RESOLVE", kcm_op_noop },
1696 { "GEN_NEW", kcm_op_gen_new },
1697 { "INITIALIZE", kcm_op_initialize },
1698 { "DESTROY", kcm_op_destroy },
1699 { "STORE", kcm_op_store },
1700 { "RETRIEVE", kcm_op_retrieve },
1701 { "GET_PRINCIPAL", kcm_op_get_principal },
1702 { "GET_CRED_UUID_LIST", kcm_op_get_cred_uuid_list },
1703 { "GET_CRED_BY_UUID", kcm_op_get_cred_by_uuid },
1704 { "REMOVE_CRED", kcm_op_remove_cred },
1705 { "SET_FLAGS", kcm_op_set_flags },
1706 { "CHOWN", kcm_op_chown },
1707 { "CHMOD", kcm_op_chmod },
1708 { "GET_INITIAL_TICKET", kcm_op_get_initial_ticket },
1709 { "GET_TICKET", kcm_op_get_ticket },
1710 { "MOVE_CACHE", kcm_op_move_cache },
1711 { "GET_CACHE_UUID_LIST", kcm_op_get_cache_uuid_list },
1712 { "GET_CACHE_BY_UUID", kcm_op_get_cache_by_uuid },
1713 { "GET_DEFAULT_CACHE", kcm_op_get_default_cache },
1714 { "SET_DEFAULT_CACHE", kcm_op_set_default_cache },
1715 { "GET_KDC_OFFSET", kcm_op_get_kdc_offset },
1716 { "SET_KDC_OFFSET", kcm_op_set_kdc_offset },
1717 { "ADD_NTLM_CRED", kcm_op_add_ntlm_cred },
1718 { "HAVE_USER_CRED", kcm_op_have_ntlm_cred },
1719 { "DEL_NTLM_CRED", kcm_op_del_ntlm_cred },
1720 { "DO_NTLM_AUTH", kcm_op_do_ntlm },
1721 { "GET_NTLM_USER_LIST", kcm_op_get_ntlm_user_list }
1726 kcm_op2string(kcm_operation opcode)
1728 if (opcode >= sizeof(kcm_ops)/sizeof(kcm_ops[0]))
1729 return "Unknown operation";
1731 return kcm_ops[opcode].name;
1735 kcm_dispatch(krb5_context context,
1737 krb5_data *req_data,
1738 krb5_data *resp_data)
1740 krb5_error_code ret;
1742 krb5_storage *req_sp = NULL;
1743 krb5_storage *resp_sp = NULL;
1746 resp_sp = krb5_storage_emem();
1747 if (resp_sp == NULL) {
1751 if (client->pid == -1) {
1752 kcm_log(0, "Client had invalid process number");
1753 ret = KRB5_FCC_INTERNAL;
1757 req_sp = krb5_storage_from_data(req_data);
1758 if (req_sp == NULL) {
1759 kcm_log(0, "Process %d: failed to initialize storage from data",
1765 ret = krb5_ret_uint16(req_sp, &opcode);
1767 kcm_log(0, "Process %d: didn't send a message", client->pid);
1771 if (opcode >= sizeof(kcm_ops)/sizeof(kcm_ops[0])) {
1772 kcm_log(0, "Process %d: invalid operation code %d",
1773 client->pid, opcode);
1774 ret = KRB5_FCC_INTERNAL;
1777 method = kcm_ops[opcode].method;
1778 if (method == NULL) {
1779 kcm_log(0, "Process %d: operation code %s not implemented",
1780 client->pid, kcm_op2string(opcode));
1781 ret = KRB5_FCC_INTERNAL;
1785 /* seek past place for status code */
1786 krb5_storage_seek(resp_sp, 4, SEEK_SET);
1788 ret = (*method)(context, client, opcode, req_sp, resp_sp);
1791 if (req_sp != NULL) {
1792 krb5_storage_free(req_sp);
1795 krb5_storage_seek(resp_sp, 0, SEEK_SET);
1796 krb5_store_int32(resp_sp, ret);
1798 ret = krb5_storage_to_data(resp_sp, resp_data);
1799 krb5_storage_free(resp_sp);