1 /* $NetBSD: protocol.c,v 1.3 2023/06/19 21:41:41 christos Exp $ */
2
3 /*
4 * Copyright (c) 2005, PADL Software Pty Ltd.
5 * All rights reserved.
6 *
7 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
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.
19 *
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.
23 *
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
34 * SUCH DAMAGE.
35 */
36
37 #include "kcm_locl.h"
38 #include <krb5/heimntlm.h>
39
40 static void
41 kcm_drop_default_cache(krb5_context context, kcm_client *client, char *name);
42
43
44 int
kcm_is_same_session(kcm_client * client,uid_t uid,pid_t session)45 kcm_is_same_session(kcm_client *client, uid_t uid, pid_t session)
46 {
47 #if 0 /* XXX pppd is running in diffrent session the user */
48 if (session != -1)
49 return (client->session == session);
50 else
51 #endif
52 return (client->uid == uid);
53 }
54
55 static krb5_error_code
kcm_op_noop(krb5_context context,kcm_client * client,kcm_operation opcode,krb5_storage * request,krb5_storage * response)56 kcm_op_noop(krb5_context context,
57 kcm_client *client,
58 kcm_operation opcode,
59 krb5_storage *request,
60 krb5_storage *response)
61 {
62 KCM_LOG_REQUEST(context, client, opcode);
63
64 return 0;
65 }
66
67 /*
68 * Request:
69 * NameZ
70 * Response:
71 * NameZ
72 *
73 */
74 static krb5_error_code
kcm_op_get_name(krb5_context context,kcm_client * client,kcm_operation opcode,krb5_storage * request,krb5_storage * response)75 kcm_op_get_name(krb5_context context,
76 kcm_client *client,
77 kcm_operation opcode,
78 krb5_storage *request,
79 krb5_storage *response)
80
81 {
82 krb5_error_code ret;
83 char *name = NULL;
84 kcm_ccache ccache;
85
86 ret = krb5_ret_stringz(request, &name);
87 if (ret)
88 return ret;
89
90 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
91
92 ret = kcm_ccache_resolve_client(context, client, opcode,
93 name, &ccache);
94 if (ret) {
95 free(name);
96 return ret;
97 }
98
99 ret = krb5_store_stringz(response, ccache->name);
100 if (ret) {
101 kcm_release_ccache(context, ccache);
102 free(name);
103 return ret;
104 }
105
106 free(name);
107 kcm_release_ccache(context, ccache);
108 return 0;
109 }
110
111 /*
112 * Request:
113 *
114 * Response:
115 * NameZ
116 */
117 static krb5_error_code
kcm_op_gen_new(krb5_context context,kcm_client * client,kcm_operation opcode,krb5_storage * request,krb5_storage * response)118 kcm_op_gen_new(krb5_context context,
119 kcm_client *client,
120 kcm_operation opcode,
121 krb5_storage *request,
122 krb5_storage *response)
123 {
124 krb5_error_code ret;
125 char *name;
126
127 KCM_LOG_REQUEST(context, client, opcode);
128
129 name = kcm_ccache_nextid(client->pid, client->uid, client->gid);
130 if (name == NULL) {
131 return KRB5_CC_NOMEM;
132 }
133
134 ret = krb5_store_stringz(response, name);
135 free(name);
136
137 return ret;
138 }
139
140 /*
141 * Request:
142 * NameZ
143 * Principal
144 *
145 * Response:
146 *
147 */
148 static krb5_error_code
kcm_op_initialize(krb5_context context,kcm_client * client,kcm_operation opcode,krb5_storage * request,krb5_storage * response)149 kcm_op_initialize(krb5_context context,
150 kcm_client *client,
151 kcm_operation opcode,
152 krb5_storage *request,
153 krb5_storage *response)
154 {
155 kcm_ccache ccache;
156 krb5_principal principal;
157 krb5_error_code ret;
158 char *name;
159 #if 0
160 kcm_event event;
161 #endif
162
163 KCM_LOG_REQUEST(context, client, opcode);
164
165 ret = krb5_ret_stringz(request, &name);
166 if (ret)
167 return ret;
168
169 ret = krb5_ret_principal(request, &principal);
170 if (ret) {
171 free(name);
172 return ret;
173 }
174
175 ret = kcm_ccache_new_client(context, client, name, &ccache);
176 if (ret) {
177 free(name);
178 krb5_free_principal(context, principal);
179 return ret;
180 }
181
182 ccache->client = principal;
183
184 free(name);
185
186 #if 0
187 /*
188 * Create a new credentials cache. To mitigate DoS attacks we will
189 * expire it in 30 minutes unless it has some credentials added
190 * to it
191 */
192
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;
198
199 ret = kcm_enqueue_event_relative(context, &event);
200 #endif
201
202 kcm_release_ccache(context, ccache);
203
204 return ret;
205 }
206
207 /*
208 * Request:
209 * NameZ
210 *
211 * Response:
212 *
213 */
214 static krb5_error_code
kcm_op_destroy(krb5_context context,kcm_client * client,kcm_operation opcode,krb5_storage * request,krb5_storage * response)215 kcm_op_destroy(krb5_context context,
216 kcm_client *client,
217 kcm_operation opcode,
218 krb5_storage *request,
219 krb5_storage *response)
220 {
221 krb5_error_code ret;
222 char *name;
223
224 ret = krb5_ret_stringz(request, &name);
225 if (ret)
226 return ret;
227
228 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
229
230 ret = kcm_ccache_destroy_client(context, client, name);
231 if (ret == 0)
232 kcm_drop_default_cache(context, client, name);
233
234 free(name);
235
236 return ret;
237 }
238
239 /*
240 * Request:
241 * NameZ
242 * Creds
243 *
244 * Response:
245 *
246 */
247 static krb5_error_code
kcm_op_store(krb5_context context,kcm_client * client,kcm_operation opcode,krb5_storage * request,krb5_storage * response)248 kcm_op_store(krb5_context context,
249 kcm_client *client,
250 kcm_operation opcode,
251 krb5_storage *request,
252 krb5_storage *response)
253 {
254 krb5_creds creds;
255 krb5_error_code ret;
256 kcm_ccache ccache;
257 char *name;
258
259 ret = krb5_ret_stringz(request, &name);
260 if (ret)
261 return ret;
262
263 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
264
265 ret = krb5_ret_creds(request, &creds);
266 if (ret) {
267 free(name);
268 return ret;
269 }
270
271 ret = kcm_ccache_resolve_client(context, client, opcode,
272 name, &ccache);
273 if (ret) {
274 free(name);
275 krb5_free_cred_contents(context, &creds);
276 return ret;
277 }
278
279 ret = kcm_ccache_store_cred(context, ccache, &creds, 0);
280 if (ret) {
281 free(name);
282 krb5_free_cred_contents(context, &creds);
283 kcm_release_ccache(context, ccache);
284 return ret;
285 }
286
287 kcm_ccache_enqueue_default(context, ccache, &creds);
288
289 free(name);
290 kcm_release_ccache(context, ccache);
291
292 return 0;
293 }
294
295 /*
296 * Request:
297 * NameZ
298 * WhichFields
299 * MatchCreds
300 *
301 * Response:
302 * Creds
303 *
304 */
305 static krb5_error_code
kcm_op_retrieve(krb5_context context,kcm_client * client,kcm_operation opcode,krb5_storage * request,krb5_storage * response)306 kcm_op_retrieve(krb5_context context,
307 kcm_client *client,
308 kcm_operation opcode,
309 krb5_storage *request,
310 krb5_storage *response)
311 {
312 uint32_t flags;
313 krb5_creds mcreds;
314 krb5_error_code ret;
315 kcm_ccache ccache;
316 char *name;
317 krb5_creds *credp;
318 int free_creds = 0;
319
320 ret = krb5_ret_stringz(request, &name);
321 if (ret)
322 return ret;
323
324 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
325
326 ret = krb5_ret_uint32(request, &flags);
327 if (ret) {
328 free(name);
329 return ret;
330 }
331
332 ret = krb5_ret_creds_tag(request, &mcreds);
333 if (ret) {
334 free(name);
335 return ret;
336 }
337
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)
341 {
342 free(name);
343 krb5_free_cred_contents(context, &mcreds);
344 return KRB5_FCC_PERM;
345 }
346
347 ret = kcm_ccache_resolve_client(context, client, opcode,
348 name, &ccache);
349 if (ret) {
350 free(name);
351 krb5_free_cred_contents(context, &mcreds);
352 return ret;
353 }
354
355 ret = kcm_ccache_retrieve_cred(context, ccache, flags,
356 &mcreds, &credp);
357 if (ret && ((flags & KRB5_GC_CACHED) == 0) &&
358 !krb5_is_config_principal(context, mcreds.server)) {
359 krb5_ccache_data ccdata;
360
361 /* try and acquire */
362 HEIMDAL_MUTEX_lock(&ccache->mutex);
363
364 /* Fake up an internal ccache */
365 kcm_internal_ccache(context, ccache, &ccdata);
366
367 /* glue cc layer will store creds */
368 ret = krb5_get_credentials(context, 0, &ccdata, &mcreds, &credp);
369 if (ret == 0)
370 free_creds = 1;
371
372 HEIMDAL_MUTEX_unlock(&ccache->mutex);
373 }
374
375 if (ret == 0) {
376 ret = krb5_store_creds(response, credp);
377 }
378
379 free(name);
380 krb5_free_cred_contents(context, &mcreds);
381 kcm_release_ccache(context, ccache);
382
383 if (free_creds)
384 krb5_free_cred_contents(context, credp);
385
386 return ret;
387 }
388
389 /*
390 * Request:
391 * NameZ
392 *
393 * Response:
394 * Principal
395 */
396 static krb5_error_code
kcm_op_get_principal(krb5_context context,kcm_client * client,kcm_operation opcode,krb5_storage * request,krb5_storage * response)397 kcm_op_get_principal(krb5_context context,
398 kcm_client *client,
399 kcm_operation opcode,
400 krb5_storage *request,
401 krb5_storage *response)
402 {
403 krb5_error_code ret;
404 kcm_ccache ccache;
405 char *name;
406
407 ret = krb5_ret_stringz(request, &name);
408 if (ret)
409 return ret;
410
411 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
412
413 ret = kcm_ccache_resolve_client(context, client, opcode,
414 name, &ccache);
415 if (ret) {
416 free(name);
417 return ret;
418 }
419
420 if (ccache->client == NULL)
421 ret = KRB5_CC_NOTFOUND;
422 else
423 ret = krb5_store_principal(response, ccache->client);
424
425 free(name);
426 kcm_release_ccache(context, ccache);
427
428 return ret;
429 }
430
431 /*
432 * Request:
433 * NameZ
434 *
435 * Response:
436 * UUIDs
437 *
438 */
439 static krb5_error_code
kcm_op_get_cred_uuid_list(krb5_context context,kcm_client * client,kcm_operation opcode,krb5_storage * request,krb5_storage * response)440 kcm_op_get_cred_uuid_list(krb5_context context,
441 kcm_client *client,
442 kcm_operation opcode,
443 krb5_storage *request,
444 krb5_storage *response)
445 {
446 struct kcm_creds *creds;
447 krb5_error_code ret;
448 kcm_ccache ccache;
449 char *name;
450
451 ret = krb5_ret_stringz(request, &name);
452 if (ret)
453 return ret;
454
455 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
456
457 ret = kcm_ccache_resolve_client(context, client, opcode,
458 name, &ccache);
459 free(name);
460 if (ret)
461 return ret;
462
463 for (creds = ccache->creds ; creds ; creds = creds->next) {
464 ssize_t sret;
465 sret = krb5_storage_write(response, &creds->uuid, sizeof(creds->uuid));
466 if (sret != sizeof(creds->uuid)) {
467 ret = ENOMEM;
468 break;
469 }
470 }
471
472 kcm_release_ccache(context, ccache);
473
474 return ret;
475 }
476
477 /*
478 * Request:
479 * NameZ
480 * Cursor
481 *
482 * Response:
483 * Creds
484 */
485 static krb5_error_code
kcm_op_get_cred_by_uuid(krb5_context context,kcm_client * client,kcm_operation opcode,krb5_storage * request,krb5_storage * response)486 kcm_op_get_cred_by_uuid(krb5_context context,
487 kcm_client *client,
488 kcm_operation opcode,
489 krb5_storage *request,
490 krb5_storage *response)
491 {
492 krb5_error_code ret;
493 kcm_ccache ccache;
494 char *name;
495 struct kcm_creds *c;
496 kcmuuid_t uuid;
497 ssize_t sret;
498
499 ret = krb5_ret_stringz(request, &name);
500 if (ret)
501 return ret;
502
503 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
504
505 ret = kcm_ccache_resolve_client(context, client, opcode,
506 name, &ccache);
507 free(name);
508 if (ret)
509 return ret;
510
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);
515 return KRB5_CC_IO;
516 }
517
518 c = kcm_ccache_find_cred_uuid(context, ccache, uuid);
519 if (c == NULL) {
520 kcm_release_ccache(context, ccache);
521 return KRB5_CC_END;
522 }
523
524 HEIMDAL_MUTEX_lock(&ccache->mutex);
525 ret = krb5_store_creds(response, &c->cred);
526 HEIMDAL_MUTEX_unlock(&ccache->mutex);
527
528 kcm_release_ccache(context, ccache);
529
530 return ret;
531 }
532
533 /*
534 * Request:
535 * NameZ
536 * WhichFields
537 * MatchCreds
538 *
539 * Response:
540 *
541 */
542 static krb5_error_code
kcm_op_remove_cred(krb5_context context,kcm_client * client,kcm_operation opcode,krb5_storage * request,krb5_storage * response)543 kcm_op_remove_cred(krb5_context context,
544 kcm_client *client,
545 kcm_operation opcode,
546 krb5_storage *request,
547 krb5_storage *response)
548 {
549 uint32_t whichfields;
550 krb5_creds mcreds;
551 krb5_error_code ret;
552 kcm_ccache ccache;
553 char *name;
554
555 ret = krb5_ret_stringz(request, &name);
556 if (ret)
557 return ret;
558
559 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
560
561 ret = krb5_ret_uint32(request, &whichfields);
562 if (ret) {
563 free(name);
564 return ret;
565 }
566
567 ret = krb5_ret_creds_tag(request, &mcreds);
568 if (ret) {
569 free(name);
570 return ret;
571 }
572
573 ret = kcm_ccache_resolve_client(context, client, opcode,
574 name, &ccache);
575 if (ret) {
576 free(name);
577 krb5_free_cred_contents(context, &mcreds);
578 return ret;
579 }
580
581 ret = kcm_ccache_remove_cred(context, ccache, whichfields, &mcreds);
582
583 /* XXX need to remove any events that match */
584
585 free(name);
586 krb5_free_cred_contents(context, &mcreds);
587 kcm_release_ccache(context, ccache);
588
589 return ret;
590 }
591
592 /*
593 * Request:
594 * NameZ
595 * Flags
596 *
597 * Response:
598 *
599 */
600 static krb5_error_code
kcm_op_set_flags(krb5_context context,kcm_client * client,kcm_operation opcode,krb5_storage * request,krb5_storage * response)601 kcm_op_set_flags(krb5_context context,
602 kcm_client *client,
603 kcm_operation opcode,
604 krb5_storage *request,
605 krb5_storage *response)
606 {
607 uint32_t flags;
608 krb5_error_code ret;
609 kcm_ccache ccache;
610 char *name;
611
612 ret = krb5_ret_stringz(request, &name);
613 if (ret)
614 return ret;
615
616 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
617
618 ret = krb5_ret_uint32(request, &flags);
619 if (ret) {
620 free(name);
621 return ret;
622 }
623
624 ret = kcm_ccache_resolve_client(context, client, opcode,
625 name, &ccache);
626 if (ret) {
627 free(name);
628 return ret;
629 }
630
631 /* we don't really support any flags yet */
632 free(name);
633 kcm_release_ccache(context, ccache);
634
635 return 0;
636 }
637
638 /*
639 * Request:
640 * NameZ
641 * UID
642 * GID
643 *
644 * Response:
645 *
646 */
647 static krb5_error_code
kcm_op_chown(krb5_context context,kcm_client * client,kcm_operation opcode,krb5_storage * request,krb5_storage * response)648 kcm_op_chown(krb5_context context,
649 kcm_client *client,
650 kcm_operation opcode,
651 krb5_storage *request,
652 krb5_storage *response)
653 {
654 uint32_t uid;
655 uint32_t gid;
656 krb5_error_code ret;
657 kcm_ccache ccache;
658 char *name;
659
660 ret = krb5_ret_stringz(request, &name);
661 if (ret)
662 return ret;
663
664 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
665
666 ret = krb5_ret_uint32(request, &uid);
667 if (ret) {
668 free(name);
669 return ret;
670 }
671
672 ret = krb5_ret_uint32(request, &gid);
673 if (ret) {
674 free(name);
675 return ret;
676 }
677
678 ret = kcm_ccache_resolve_client(context, client, opcode,
679 name, &ccache);
680 if (ret) {
681 free(name);
682 return ret;
683 }
684
685 ret = kcm_chown(context, client, ccache, uid, gid);
686
687 free(name);
688 kcm_release_ccache(context, ccache);
689
690 return ret;
691 }
692
693 /*
694 * Request:
695 * NameZ
696 * Mode
697 *
698 * Response:
699 *
700 */
701 static krb5_error_code
kcm_op_chmod(krb5_context context,kcm_client * client,kcm_operation opcode,krb5_storage * request,krb5_storage * response)702 kcm_op_chmod(krb5_context context,
703 kcm_client *client,
704 kcm_operation opcode,
705 krb5_storage *request,
706 krb5_storage *response)
707 {
708 uint16_t mode;
709 krb5_error_code ret;
710 kcm_ccache ccache;
711 char *name;
712
713 ret = krb5_ret_stringz(request, &name);
714 if (ret)
715 return ret;
716
717 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
718
719 ret = krb5_ret_uint16(request, &mode);
720 if (ret) {
721 free(name);
722 return ret;
723 }
724
725 ret = kcm_ccache_resolve_client(context, client, opcode,
726 name, &ccache);
727 if (ret) {
728 free(name);
729 return ret;
730 }
731
732 ret = kcm_chmod(context, client, ccache, mode);
733
734 free(name);
735 kcm_release_ccache(context, ccache);
736
737 return ret;
738 }
739
740 /*
741 * Protocol extensions for moving ticket acquisition responsibility
742 * from client to KCM follow.
743 */
744
745 /*
746 * Request:
747 * NameZ
748 * ServerPrincipalPresent
749 * ServerPrincipal OPTIONAL
750 * Key
751 *
752 * Repsonse:
753 *
754 */
755 static krb5_error_code
kcm_op_get_initial_ticket(krb5_context context,kcm_client * client,kcm_operation opcode,krb5_storage * request,krb5_storage * response)756 kcm_op_get_initial_ticket(krb5_context context,
757 kcm_client *client,
758 kcm_operation opcode,
759 krb5_storage *request,
760 krb5_storage *response)
761 {
762 krb5_error_code ret;
763 kcm_ccache ccache;
764 char *name;
765 int8_t not_tgt = 0;
766 krb5_principal server = NULL;
767 krb5_keyblock key;
768
769 krb5_keyblock_zero(&key);
770
771 ret = krb5_ret_stringz(request, &name);
772 if (ret)
773 return ret;
774
775 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
776
777 ret = krb5_ret_int8(request, ¬_tgt);
778 if (ret) {
779 free(name);
780 return ret;
781 }
782
783 if (not_tgt) {
784 ret = krb5_ret_principal(request, &server);
785 if (ret) {
786 free(name);
787 return ret;
788 }
789 }
790
791 ret = krb5_ret_keyblock(request, &key);
792 if (ret) {
793 free(name);
794 if (server != NULL)
795 krb5_free_principal(context, server);
796 return ret;
797 }
798
799 ret = kcm_ccache_resolve_client(context, client, opcode,
800 name, &ccache);
801 if (ret == 0) {
802 HEIMDAL_MUTEX_lock(&ccache->mutex);
803
804 if (ccache->server != NULL) {
805 krb5_free_principal(context, ccache->server);
806 ccache->server = NULL;
807 }
808
809 krb5_free_keyblock(context, &ccache->key.keyblock);
810
811 ccache->server = server;
812 ccache->key.keyblock = key;
813 ccache->flags |= KCM_FLAGS_USE_CACHED_KEY;
814
815 ret = kcm_ccache_enqueue_default(context, ccache, NULL);
816 if (ret) {
817 ccache->server = NULL;
818 krb5_keyblock_zero(&ccache->key.keyblock);
819 ccache->flags &= ~(KCM_FLAGS_USE_CACHED_KEY);
820 }
821
822 HEIMDAL_MUTEX_unlock(&ccache->mutex);
823 }
824
825 free(name);
826
827 if (ret != 0) {
828 krb5_free_principal(context, server);
829 krb5_free_keyblock_contents(context, &key);
830 }
831
832 kcm_release_ccache(context, ccache);
833
834 return ret;
835 }
836
837 /*
838 * Request:
839 * NameZ
840 * ServerPrincipal
841 * KDCFlags
842 * EncryptionType
843 *
844 * Repsonse:
845 *
846 */
847 static krb5_error_code
kcm_op_get_ticket(krb5_context context,kcm_client * client,kcm_operation opcode,krb5_storage * request,krb5_storage * response)848 kcm_op_get_ticket(krb5_context context,
849 kcm_client *client,
850 kcm_operation opcode,
851 krb5_storage *request,
852 krb5_storage *response)
853 {
854 krb5_error_code ret;
855 kcm_ccache ccache;
856 char *name;
857 krb5_principal server = NULL;
858 krb5_ccache_data ccdata;
859 krb5_creds in, *out;
860 krb5_kdc_flags flags;
861
862 memset(&in, 0, sizeof(in));
863
864 ret = krb5_ret_stringz(request, &name);
865 if (ret)
866 return ret;
867
868 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
869
870 ret = krb5_ret_uint32(request, &flags.i);
871 if (ret) {
872 free(name);
873 return ret;
874 }
875
876 ret = krb5_ret_int32(request, &in.session.keytype);
877 if (ret) {
878 free(name);
879 return ret;
880 }
881
882 ret = krb5_ret_principal(request, &server);
883 if (ret) {
884 free(name);
885 return ret;
886 }
887
888 ret = kcm_ccache_resolve_client(context, client, opcode,
889 name, &ccache);
890 if (ret) {
891 krb5_free_principal(context, server);
892 free(name);
893 return ret;
894 }
895
896 HEIMDAL_MUTEX_lock(&ccache->mutex);
897
898 /* Fake up an internal ccache */
899 kcm_internal_ccache(context, ccache, &ccdata);
900
901 in.client = ccache->client;
902 in.server = server;
903 in.times.endtime = 0;
904
905 /* glue cc layer will store creds */
906 ret = krb5_get_credentials_with_flags(context, 0, flags,
907 &ccdata, &in, &out);
908
909 HEIMDAL_MUTEX_unlock(&ccache->mutex);
910
911 krb5_free_principal(context, server);
912
913 if (ret == 0)
914 krb5_free_cred_contents(context, out);
915
916 kcm_release_ccache(context, ccache);
917 free(name);
918
919 return ret;
920 }
921
922 /*
923 * Request:
924 * OldNameZ
925 * NewNameZ
926 *
927 * Repsonse:
928 *
929 */
930 static krb5_error_code
kcm_op_move_cache(krb5_context context,kcm_client * client,kcm_operation opcode,krb5_storage * request,krb5_storage * response)931 kcm_op_move_cache(krb5_context context,
932 kcm_client *client,
933 kcm_operation opcode,
934 krb5_storage *request,
935 krb5_storage *response)
936 {
937 krb5_error_code ret;
938 kcm_ccache oldid, newid;
939 char *oldname, *newname;
940
941 ret = krb5_ret_stringz(request, &oldname);
942 if (ret)
943 return ret;
944
945 KCM_LOG_REQUEST_NAME(context, client, opcode, oldname);
946
947 ret = krb5_ret_stringz(request, &newname);
948 if (ret) {
949 free(oldname);
950 return ret;
951 }
952
953 /* move to ourself is simple, done! */
954 if (strcmp(oldname, newname) == 0) {
955 free(oldname);
956 free(newname);
957 return 0;
958 }
959
960 ret = kcm_ccache_resolve_client(context, client, opcode, oldname, &oldid);
961 if (ret) {
962 free(oldname);
963 free(newname);
964 return ret;
965 }
966
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);
971 free(newname);
972
973 if (ret) {
974 free(oldname);
975 kcm_release_ccache(context, oldid);
976 return ret;
977 }
978
979 HEIMDAL_MUTEX_lock(&oldid->mutex);
980 HEIMDAL_MUTEX_lock(&newid->mutex);
981
982 /* move content */
983 {
984 kcm_ccache_data tmp;
985
986 #define MOVE(n,o,f) { tmp.f = n->f ; n->f = o->f; o->f = tmp.f; }
987
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);
996 #undef MOVE
997 }
998
999 HEIMDAL_MUTEX_unlock(&oldid->mutex);
1000 HEIMDAL_MUTEX_unlock(&newid->mutex);
1001
1002 kcm_release_ccache(context, oldid);
1003 kcm_release_ccache(context, newid);
1004
1005 ret = kcm_ccache_destroy_client(context, client, oldname);
1006 if (ret == 0)
1007 kcm_drop_default_cache(context, client, oldname);
1008
1009 free(oldname);
1010
1011 return ret;
1012 }
1013
1014 static krb5_error_code
kcm_op_get_cache_uuid_list(krb5_context context,kcm_client * client,kcm_operation opcode,krb5_storage * request,krb5_storage * response)1015 kcm_op_get_cache_uuid_list(krb5_context context,
1016 kcm_client *client,
1017 kcm_operation opcode,
1018 krb5_storage *request,
1019 krb5_storage *response)
1020 {
1021 KCM_LOG_REQUEST(context, client, opcode);
1022
1023 return kcm_ccache_get_uuids(context, client, opcode, response);
1024 }
1025
1026 static krb5_error_code
kcm_op_get_cache_by_uuid(krb5_context context,kcm_client * client,kcm_operation opcode,krb5_storage * request,krb5_storage * response)1027 kcm_op_get_cache_by_uuid(krb5_context context,
1028 kcm_client *client,
1029 kcm_operation opcode,
1030 krb5_storage *request,
1031 krb5_storage *response)
1032 {
1033 krb5_error_code ret;
1034 kcmuuid_t uuid;
1035 ssize_t sret;
1036 kcm_ccache cache;
1037
1038 KCM_LOG_REQUEST(context, client, opcode);
1039
1040 sret = krb5_storage_read(request, &uuid, sizeof(uuid));
1041 if (sret != sizeof(uuid)) {
1042 krb5_clear_error_message(context);
1043 return KRB5_CC_IO;
1044 }
1045
1046 ret = kcm_ccache_resolve_by_uuid(context, uuid, &cache);
1047 if (ret)
1048 return ret;
1049
1050 ret = kcm_access(context, client, opcode, cache);
1051 if (ret)
1052 ret = KRB5_FCC_NOFILE;
1053
1054 if (ret == 0)
1055 ret = krb5_store_stringz(response, cache->name);
1056
1057 kcm_release_ccache(context, cache);
1058
1059 return ret;
1060 }
1061
1062 struct kcm_default_cache *default_caches;
1063
1064 static krb5_error_code
kcm_op_get_default_cache(krb5_context context,kcm_client * client,kcm_operation opcode,krb5_storage * request,krb5_storage * response)1065 kcm_op_get_default_cache(krb5_context context,
1066 kcm_client *client,
1067 kcm_operation opcode,
1068 krb5_storage *request,
1069 krb5_storage *response)
1070 {
1071 struct kcm_default_cache *c;
1072 krb5_error_code ret;
1073 const char *name = NULL;
1074 char *n = NULL;
1075 int aret;
1076
1077 KCM_LOG_REQUEST(context, client, opcode);
1078
1079 for (c = default_caches; c != NULL; c = c->next) {
1080 if (kcm_is_same_session(client, c->uid, c->session)) {
1081 name = c->name;
1082 break;
1083 }
1084 }
1085 if (name == NULL)
1086 name = n = kcm_ccache_first_name(client);
1087
1088 if (name == NULL) {
1089 aret = asprintf(&n, "%d", (int)client->uid);
1090 if (aret != -1)
1091 name = n;
1092 }
1093 if (name == NULL)
1094 return ENOMEM;
1095 ret = krb5_store_stringz(response, name);
1096 if (n)
1097 free(n);
1098 return ret;
1099 }
1100
1101 static void
kcm_drop_default_cache(krb5_context context,kcm_client * client,char * name)1102 kcm_drop_default_cache(krb5_context context, kcm_client *client, char *name)
1103 {
1104 struct kcm_default_cache **c;
1105
1106 for (c = &default_caches; *c != NULL; c = &(*c)->next) {
1107 if (!kcm_is_same_session(client, (*c)->uid, (*c)->session))
1108 continue;
1109 if (strcmp((*c)->name, name) == 0) {
1110 struct kcm_default_cache *h = *c;
1111 *c = (*c)->next;
1112 free(h->name);
1113 free(h);
1114 break;
1115 }
1116 }
1117 }
1118
1119 static krb5_error_code
kcm_op_set_default_cache(krb5_context context,kcm_client * client,kcm_operation opcode,krb5_storage * request,krb5_storage * response)1120 kcm_op_set_default_cache(krb5_context context,
1121 kcm_client *client,
1122 kcm_operation opcode,
1123 krb5_storage *request,
1124 krb5_storage *response)
1125 {
1126 struct kcm_default_cache *c;
1127 krb5_error_code ret;
1128 char *name;
1129
1130 ret = krb5_ret_stringz(request, &name);
1131 if (ret)
1132 return ret;
1133
1134 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
1135
1136 for (c = default_caches; c != NULL; c = c->next) {
1137 if (kcm_is_same_session(client, c->uid, c->session))
1138 break;
1139 }
1140 if (c == NULL) {
1141 c = malloc(sizeof(*c));
1142 if (c == NULL) {
1143 free(name);
1144 return ENOMEM;
1145 }
1146 c->session = client->session;
1147 c->uid = client->uid;
1148 c->name = name;
1149
1150 c->next = default_caches;
1151 default_caches = c;
1152 } else {
1153 free(c->name);
1154 c->name = name;
1155 }
1156
1157 return 0;
1158 }
1159
1160 static krb5_error_code
kcm_op_get_kdc_offset(krb5_context context,kcm_client * client,kcm_operation opcode,krb5_storage * request,krb5_storage * response)1161 kcm_op_get_kdc_offset(krb5_context context,
1162 kcm_client *client,
1163 kcm_operation opcode,
1164 krb5_storage *request,
1165 krb5_storage *response)
1166 {
1167 krb5_error_code ret;
1168 kcm_ccache ccache;
1169 char *name;
1170
1171 ret = krb5_ret_stringz(request, &name);
1172 if (ret)
1173 return ret;
1174
1175 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
1176
1177 ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache);
1178 free(name);
1179 if (ret)
1180 return ret;
1181
1182 HEIMDAL_MUTEX_lock(&ccache->mutex);
1183 ret = krb5_store_int32(response, ccache->kdc_offset);
1184 HEIMDAL_MUTEX_unlock(&ccache->mutex);
1185
1186 kcm_release_ccache(context, ccache);
1187
1188 return ret;
1189 }
1190
1191 static krb5_error_code
kcm_op_set_kdc_offset(krb5_context context,kcm_client * client,kcm_operation opcode,krb5_storage * request,krb5_storage * response)1192 kcm_op_set_kdc_offset(krb5_context context,
1193 kcm_client *client,
1194 kcm_operation opcode,
1195 krb5_storage *request,
1196 krb5_storage *response)
1197 {
1198 krb5_error_code ret;
1199 kcm_ccache ccache;
1200 int32_t offset;
1201 char *name;
1202
1203 ret = krb5_ret_stringz(request, &name);
1204 if (ret)
1205 return ret;
1206
1207 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
1208
1209 ret = krb5_ret_int32(request, &offset);
1210 if (ret) {
1211 free(name);
1212 return ret;
1213 }
1214
1215 ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache);
1216 free(name);
1217 if (ret)
1218 return ret;
1219
1220 HEIMDAL_MUTEX_lock(&ccache->mutex);
1221 ccache->kdc_offset = offset;
1222 HEIMDAL_MUTEX_unlock(&ccache->mutex);
1223
1224 kcm_release_ccache(context, ccache);
1225
1226 return ret;
1227 }
1228
1229 struct kcm_ntlm_cred {
1230 kcmuuid_t uuid;
1231 char *user;
1232 char *domain;
1233 krb5_data nthash;
1234 uid_t uid;
1235 pid_t session;
1236 struct kcm_ntlm_cred *next;
1237 };
1238
1239 static struct kcm_ntlm_cred *ntlm_head;
1240
1241 static void
free_cred(struct kcm_ntlm_cred * cred)1242 free_cred(struct kcm_ntlm_cred *cred)
1243 {
1244 free(cred->user);
1245 free(cred->domain);
1246 krb5_data_free(&cred->nthash);
1247 free(cred);
1248 }
1249
1250
1251 /*
1252 * name
1253 * domain
1254 * ntlm hash
1255 *
1256 * Reply:
1257 * uuid
1258 */
1259
1260 static struct kcm_ntlm_cred *
find_ntlm_cred(const char * user,const char * domain,kcm_client * client)1261 find_ntlm_cred(const char *user, const char *domain, kcm_client *client)
1262 {
1263 struct kcm_ntlm_cred *c;
1264
1265 for (c = ntlm_head; c != NULL; c = c->next)
1266 if ((user[0] == '\0' || strcmp(user, c->user) == 0) &&
1267 (domain == NULL || strcmp(domain, c->domain) == 0) &&
1268 kcm_is_same_session(client, c->uid, c->session))
1269 return c;
1270
1271 return NULL;
1272 }
1273
1274 static krb5_error_code
kcm_op_add_ntlm_cred(krb5_context context,kcm_client * client,kcm_operation opcode,krb5_storage * request,krb5_storage * response)1275 kcm_op_add_ntlm_cred(krb5_context context,
1276 kcm_client *client,
1277 kcm_operation opcode,
1278 krb5_storage *request,
1279 krb5_storage *response)
1280 {
1281 struct kcm_ntlm_cred *cred, *c;
1282 krb5_error_code ret;
1283
1284 cred = calloc(1, sizeof(*cred));
1285 if (cred == NULL)
1286 return ENOMEM;
1287
1288 RAND_bytes(cred->uuid, sizeof(cred->uuid));
1289
1290 ret = krb5_ret_stringz(request, &cred->user);
1291 if (ret)
1292 goto error;
1293
1294 ret = krb5_ret_stringz(request, &cred->domain);
1295 if (ret)
1296 goto error;
1297
1298 ret = krb5_ret_data(request, &cred->nthash);
1299 if (ret)
1300 goto error;
1301
1302 /* search for dups */
1303 c = find_ntlm_cred(cred->user, cred->domain, client);
1304 if (c) {
1305 krb5_data hash = c->nthash;
1306 c->nthash = cred->nthash;
1307 cred->nthash = hash;
1308 free_cred(cred);
1309 cred = c;
1310 } else {
1311 cred->next = ntlm_head;
1312 ntlm_head = cred;
1313 }
1314
1315 cred->uid = client->uid;
1316 cred->session = client->session;
1317
1318 /* write response */
1319 (void)krb5_storage_write(response, &cred->uuid, sizeof(cred->uuid));
1320
1321 return 0;
1322
1323 error:
1324 free_cred(cred);
1325
1326 return ret;
1327 }
1328
1329 /*
1330 * { "HAVE_NTLM_CRED", NULL },
1331 *
1332 * input:
1333 * name
1334 * domain
1335 */
1336
1337 static krb5_error_code
kcm_op_have_ntlm_cred(krb5_context context,kcm_client * client,kcm_operation opcode,krb5_storage * request,krb5_storage * response)1338 kcm_op_have_ntlm_cred(krb5_context context,
1339 kcm_client *client,
1340 kcm_operation opcode,
1341 krb5_storage *request,
1342 krb5_storage *response)
1343 {
1344 struct kcm_ntlm_cred *c;
1345 char *user = NULL, *domain = NULL;
1346 krb5_error_code ret;
1347
1348 ret = krb5_ret_stringz(request, &user);
1349 if (ret)
1350 goto error;
1351
1352 ret = krb5_ret_stringz(request, &domain);
1353 if (ret)
1354 goto error;
1355
1356 if (domain[0] == '\0') {
1357 free(domain);
1358 domain = NULL;
1359 }
1360
1361 c = find_ntlm_cred(user, domain, client);
1362 if (c == NULL)
1363 ret = ENOENT;
1364
1365 error:
1366 free(user);
1367 if (domain)
1368 free(domain);
1369
1370 return ret;
1371 }
1372
1373 /*
1374 * { "DEL_NTLM_CRED", NULL },
1375 *
1376 * input:
1377 * name
1378 * domain
1379 */
1380
1381 static krb5_error_code
kcm_op_del_ntlm_cred(krb5_context context,kcm_client * client,kcm_operation opcode,krb5_storage * request,krb5_storage * response)1382 kcm_op_del_ntlm_cred(krb5_context context,
1383 kcm_client *client,
1384 kcm_operation opcode,
1385 krb5_storage *request,
1386 krb5_storage *response)
1387 {
1388 struct kcm_ntlm_cred **cp, *c;
1389 char *user = NULL, *domain = NULL;
1390 krb5_error_code ret;
1391
1392 ret = krb5_ret_stringz(request, &user);
1393 if (ret)
1394 goto error;
1395
1396 ret = krb5_ret_stringz(request, &domain);
1397 if (ret)
1398 goto error;
1399
1400 for (cp = &ntlm_head; *cp != NULL; cp = &(*cp)->next) {
1401 if (strcmp(user, (*cp)->user) == 0 && strcmp(domain, (*cp)->domain) == 0 &&
1402 kcm_is_same_session(client, (*cp)->uid, (*cp)->session))
1403 {
1404 c = *cp;
1405 *cp = c->next;
1406
1407 free_cred(c);
1408 break;
1409 }
1410 }
1411
1412 error:
1413 free(user);
1414 free(domain);
1415
1416 return ret;
1417 }
1418
1419 /*
1420 * { "DO_NTLM_AUTH", NULL },
1421 *
1422 * input:
1423 * name:string
1424 * domain:string
1425 * type2:data
1426 *
1427 * reply:
1428 * type3:data
1429 * flags:int32
1430 * session-key:data
1431 */
1432
1433 #define NTLM_FLAG_SESSIONKEY 1
1434 #define NTLM_FLAG_NTLM2_SESSION 2
1435 #define NTLM_FLAG_KEYEX 4
1436
1437 static krb5_error_code
kcm_op_do_ntlm(krb5_context context,kcm_client * client,kcm_operation opcode,krb5_storage * request,krb5_storage * response)1438 kcm_op_do_ntlm(krb5_context context,
1439 kcm_client *client,
1440 kcm_operation opcode,
1441 krb5_storage *request,
1442 krb5_storage *response)
1443 {
1444 struct kcm_ntlm_cred *c;
1445 struct ntlm_type2 type2;
1446 struct ntlm_type3 type3;
1447 char *user = NULL, *domain = NULL;
1448 struct ntlm_buf ndata, sessionkey;
1449 krb5_data data;
1450 krb5_error_code ret;
1451 uint32_t flags = 0;
1452
1453 memset(&type2, 0, sizeof(type2));
1454 memset(&type3, 0, sizeof(type3));
1455 sessionkey.data = NULL;
1456 sessionkey.length = 0;
1457
1458 ret = krb5_ret_stringz(request, &user);
1459 if (ret)
1460 goto error;
1461
1462 ret = krb5_ret_stringz(request, &domain);
1463 if (ret)
1464 goto error;
1465
1466 if (domain[0] == '\0') {
1467 free(domain);
1468 domain = NULL;
1469 }
1470
1471 c = find_ntlm_cred(user, domain, client);
1472 if (c == NULL) {
1473 ret = EINVAL;
1474 goto error;
1475 }
1476
1477 ret = krb5_ret_data(request, &data);
1478 if (ret)
1479 goto error;
1480
1481 ndata.data = data.data;
1482 ndata.length = data.length;
1483
1484 ret = heim_ntlm_decode_type2(&ndata, &type2);
1485 krb5_data_free(&data);
1486 if (ret)
1487 goto error;
1488
1489 if (domain && strcmp(domain, type2.targetname) == 0) {
1490 ret = EINVAL;
1491 goto error;
1492 }
1493
1494 type3.username = c->user;
1495 type3.flags = type2.flags;
1496 type3.targetname = type2.targetname;
1497 type3.ws = rk_UNCONST("workstation");
1498
1499 /*
1500 * NTLM Version 1 if no targetinfo buffer.
1501 */
1502
1503 if (1 || type2.targetinfo.length == 0) {
1504 struct ntlm_buf tmpsesskey;
1505
1506 if (type2.flags & NTLM_NEG_NTLM2_SESSION) {
1507 unsigned char nonce[8];
1508
1509 if (RAND_bytes(nonce, sizeof(nonce)) != 1) {
1510 ret = EINVAL;
1511 goto error;
1512 }
1513
1514 ret = heim_ntlm_calculate_ntlm2_sess(nonce,
1515 type2.challenge,
1516 c->nthash.data,
1517 &type3.lm,
1518 &type3.ntlm);
1519 } else {
1520 ret = heim_ntlm_calculate_ntlm1(c->nthash.data,
1521 c->nthash.length,
1522 type2.challenge,
1523 &type3.ntlm);
1524
1525 }
1526 if (ret)
1527 goto error;
1528
1529 ret = heim_ntlm_build_ntlm1_master(c->nthash.data,
1530 c->nthash.length,
1531 &tmpsesskey,
1532 &type3.sessionkey);
1533 if (ret) {
1534 if (type3.lm.data)
1535 free(type3.lm.data);
1536 if (type3.ntlm.data)
1537 free(type3.ntlm.data);
1538 goto error;
1539 }
1540
1541 free(tmpsesskey.data);
1542 if (ret) {
1543 if (type3.lm.data)
1544 free(type3.lm.data);
1545 if (type3.ntlm.data)
1546 free(type3.ntlm.data);
1547 goto error;
1548 }
1549 flags |= NTLM_FLAG_SESSIONKEY;
1550 #if 0
1551 } else {
1552 struct ntlm_buf sessionkey;
1553 unsigned char ntlmv2[16];
1554 struct ntlm_targetinfo ti;
1555
1556 /* verify infotarget */
1557
1558 ret = heim_ntlm_decode_targetinfo(&type2.targetinfo, 1, &ti);
1559 if(ret) {
1560 _gss_ntlm_delete_sec_context(minor_status,
1561 context_handle, NULL);
1562 *minor_status = ret;
1563 return GSS_S_FAILURE;
1564 }
1565
1566 if (ti.domainname && strcmp(ti.domainname, name->domain) != 0) {
1567 _gss_ntlm_delete_sec_context(minor_status,
1568 context_handle, NULL);
1569 *minor_status = EINVAL;
1570 return GSS_S_FAILURE;
1571 }
1572
1573 ret = heim_ntlm_calculate_ntlm2(ctx->client->key.data,
1574 ctx->client->key.length,
1575 type3.username,
1576 name->domain,
1577 type2.challenge,
1578 &type2.targetinfo,
1579 ntlmv2,
1580 &type3.ntlm);
1581 if (ret) {
1582 _gss_ntlm_delete_sec_context(minor_status,
1583 context_handle, NULL);
1584 *minor_status = ret;
1585 return GSS_S_FAILURE;
1586 }
1587
1588 ret = heim_ntlm_build_ntlm1_master(ntlmv2, sizeof(ntlmv2),
1589 &sessionkey,
1590 &type3.sessionkey);
1591 memset(ntlmv2, 0, sizeof(ntlmv2));
1592 if (ret) {
1593 _gss_ntlm_delete_sec_context(minor_status,
1594 context_handle, NULL);
1595 *minor_status = ret;
1596 return GSS_S_FAILURE;
1597 }
1598
1599 flags |= NTLM_FLAG_NTLM2_SESSION |
1600 NTLM_FLAG_SESSION;
1601
1602 if (type3.flags & NTLM_NEG_KEYEX)
1603 flags |= NTLM_FLAG_KEYEX;
1604
1605 ret = krb5_data_copy(&ctx->sessionkey,
1606 sessionkey.data, sessionkey.length);
1607 free(sessionkey.data);
1608 if (ret) {
1609 _gss_ntlm_delete_sec_context(minor_status,
1610 context_handle, NULL);
1611 *minor_status = ret;
1612 return GSS_S_FAILURE;
1613 }
1614 #endif
1615 }
1616
1617 #if 0
1618 if (flags & NTLM_FLAG_NTLM2_SESSION) {
1619 _gss_ntlm_set_key(&ctx->u.v2.send, 0, (ctx->flags & NTLM_NEG_KEYEX),
1620 ctx->sessionkey.data,
1621 ctx->sessionkey.length);
1622 _gss_ntlm_set_key(&ctx->u.v2.recv, 1, (ctx->flags & NTLM_NEG_KEYEX),
1623 ctx->sessionkey.data,
1624 ctx->sessionkey.length);
1625 } else {
1626 flags |= NTLM_FLAG_SESSION;
1627 RC4_set_key(&ctx->u.v1.crypto_recv.key,
1628 ctx->sessionkey.length,
1629 ctx->sessionkey.data);
1630 RC4_set_key(&ctx->u.v1.crypto_send.key,
1631 ctx->sessionkey.length,
1632 ctx->sessionkey.data);
1633 }
1634 #endif
1635
1636 ret = heim_ntlm_encode_type3(&type3, &ndata, NULL);
1637 if (ret)
1638 goto error;
1639
1640 data.data = ndata.data;
1641 data.length = ndata.length;
1642 ret = krb5_store_data(response, data);
1643 heim_ntlm_free_buf(&ndata);
1644 if (ret) goto error;
1645
1646 ret = krb5_store_int32(response, flags);
1647 if (ret) goto error;
1648
1649 data.data = sessionkey.data;
1650 data.length = sessionkey.length;
1651
1652 ret = krb5_store_data(response, data);
1653 if (ret) goto error;
1654
1655 error:
1656 free(type3.username);
1657 heim_ntlm_free_type2(&type2);
1658 free(user);
1659 if (domain)
1660 free(domain);
1661
1662 return ret;
1663 }
1664
1665
1666 /*
1667 * { "GET_NTLM_UUID_LIST", NULL }
1668 *
1669 * reply:
1670 * 1 user domain
1671 * 0 [ end of list ]
1672 */
1673
1674 static krb5_error_code
kcm_op_get_ntlm_user_list(krb5_context context,kcm_client * client,kcm_operation opcode,krb5_storage * request,krb5_storage * response)1675 kcm_op_get_ntlm_user_list(krb5_context context,
1676 kcm_client *client,
1677 kcm_operation opcode,
1678 krb5_storage *request,
1679 krb5_storage *response)
1680 {
1681 struct kcm_ntlm_cred *c;
1682 krb5_error_code ret;
1683
1684 for (c = ntlm_head; c != NULL; c = c->next) {
1685 if (!kcm_is_same_session(client, c->uid, c->session))
1686 continue;
1687
1688 ret = krb5_store_uint32(response, 1);
1689 if (ret)
1690 return ret;
1691 ret = krb5_store_stringz(response, c->user);
1692 if (ret)
1693 return ret;
1694 ret = krb5_store_stringz(response, c->domain);
1695 if (ret)
1696 return ret;
1697 }
1698 return krb5_store_uint32(response, 0);
1699 }
1700
1701 /*
1702 *
1703 */
1704
1705 static struct kcm_op kcm_ops[] = {
1706 { "NOOP", kcm_op_noop },
1707 { "GET_NAME", kcm_op_get_name },
1708 { "RESOLVE", kcm_op_noop },
1709 { "GEN_NEW", kcm_op_gen_new },
1710 { "INITIALIZE", kcm_op_initialize },
1711 { "DESTROY", kcm_op_destroy },
1712 { "STORE", kcm_op_store },
1713 { "RETRIEVE", kcm_op_retrieve },
1714 { "GET_PRINCIPAL", kcm_op_get_principal },
1715 { "GET_CRED_UUID_LIST", kcm_op_get_cred_uuid_list },
1716 { "GET_CRED_BY_UUID", kcm_op_get_cred_by_uuid },
1717 { "REMOVE_CRED", kcm_op_remove_cred },
1718 { "SET_FLAGS", kcm_op_set_flags },
1719 { "CHOWN", kcm_op_chown },
1720 { "CHMOD", kcm_op_chmod },
1721 { "GET_INITIAL_TICKET", kcm_op_get_initial_ticket },
1722 { "GET_TICKET", kcm_op_get_ticket },
1723 { "MOVE_CACHE", kcm_op_move_cache },
1724 { "GET_CACHE_UUID_LIST", kcm_op_get_cache_uuid_list },
1725 { "GET_CACHE_BY_UUID", kcm_op_get_cache_by_uuid },
1726 { "GET_DEFAULT_CACHE", kcm_op_get_default_cache },
1727 { "SET_DEFAULT_CACHE", kcm_op_set_default_cache },
1728 { "GET_KDC_OFFSET", kcm_op_get_kdc_offset },
1729 { "SET_KDC_OFFSET", kcm_op_set_kdc_offset },
1730 { "ADD_NTLM_CRED", kcm_op_add_ntlm_cred },
1731 { "HAVE_USER_CRED", kcm_op_have_ntlm_cred },
1732 { "DEL_NTLM_CRED", kcm_op_del_ntlm_cred },
1733 { "DO_NTLM_AUTH", kcm_op_do_ntlm },
1734 { "GET_NTLM_USER_LIST", kcm_op_get_ntlm_user_list }
1735 };
1736
1737
1738 const char *
kcm_op2string(kcm_operation opcode)1739 kcm_op2string(kcm_operation opcode)
1740 {
1741 if (opcode >= sizeof(kcm_ops)/sizeof(kcm_ops[0]))
1742 return "Unknown operation";
1743
1744 return kcm_ops[opcode].name;
1745 }
1746
1747 krb5_error_code
kcm_dispatch(krb5_context context,kcm_client * client,krb5_data * req_data,krb5_data * resp_data)1748 kcm_dispatch(krb5_context context,
1749 kcm_client *client,
1750 krb5_data *req_data,
1751 krb5_data *resp_data)
1752 {
1753 krb5_error_code ret;
1754 kcm_method method;
1755 krb5_storage *req_sp = NULL;
1756 krb5_storage *resp_sp = NULL;
1757 uint16_t opcode;
1758
1759 resp_sp = krb5_storage_emem();
1760 if (resp_sp == NULL) {
1761 return ENOMEM;
1762 }
1763
1764 if (client->pid == -1) {
1765 kcm_log(0, "Client had invalid process number");
1766 ret = KRB5_FCC_INTERNAL;
1767 goto out;
1768 }
1769
1770 req_sp = krb5_storage_from_data(req_data);
1771 if (req_sp == NULL) {
1772 kcm_log(0, "Process %d: failed to initialize storage from data",
1773 client->pid);
1774 ret = KRB5_CC_IO;
1775 goto out;
1776 }
1777
1778 ret = krb5_ret_uint16(req_sp, &opcode);
1779 if (ret) {
1780 kcm_log(0, "Process %d: didn't send a message", client->pid);
1781 goto out;
1782 }
1783
1784 if (opcode >= sizeof(kcm_ops)/sizeof(kcm_ops[0])) {
1785 kcm_log(0, "Process %d: invalid operation code %d",
1786 client->pid, opcode);
1787 ret = KRB5_FCC_INTERNAL;
1788 goto out;
1789 }
1790 method = kcm_ops[opcode].method;
1791 if (method == NULL) {
1792 kcm_log(0, "Process %d: operation code %s not implemented",
1793 client->pid, kcm_op2string(opcode));
1794 ret = KRB5_FCC_INTERNAL;
1795 goto out;
1796 }
1797
1798 /* seek past place for status code */
1799 krb5_storage_seek(resp_sp, 4, SEEK_SET);
1800
1801 ret = (*method)(context, client, opcode, req_sp, resp_sp);
1802
1803 out:
1804 if (req_sp != NULL) {
1805 krb5_storage_free(req_sp);
1806 }
1807
1808 krb5_storage_seek(resp_sp, 0, SEEK_SET);
1809 krb5_store_int32(resp_sp, ret);
1810
1811 ret = krb5_storage_to_data(resp_sp, resp_data);
1812 krb5_storage_free(resp_sp);
1813
1814 return ret;
1815 }
1816
1817