etc/services - sync with NetBSD-8
[minix.git] / crypto / external / bsd / heimdal / dist / kcm / protocol.c
blob483c02da8a903fc8a27776f6585e6f60ac8ac8f2
1 /* $NetBSD: protocol.c,v 1.1.1.2 2014/04/24 12:45:27 pettai Exp $ */
3 /*
4 * Copyright (c) 2005, PADL Software Pty Ltd.
5 * All rights reserved.
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
11 * are met:
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
34 * SUCH DAMAGE.
37 #include "kcm_locl.h"
38 #include <krb5/heimntlm.h>
40 static void
41 kcm_drop_default_cache(krb5_context context, kcm_client *client, char *name);
44 int
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 */
48 if (session != -1)
49 return (client->session == session);
50 else
51 #endif
52 return (client->uid == uid);
55 static krb5_error_code
56 kcm_op_noop(krb5_context context,
57 kcm_client *client,
58 kcm_operation opcode,
59 krb5_storage *request,
60 krb5_storage *response)
62 KCM_LOG_REQUEST(context, client, opcode);
64 return 0;
68 * Request:
69 * NameZ
70 * Response:
71 * NameZ
74 static krb5_error_code
75 kcm_op_get_name(krb5_context context,
76 kcm_client *client,
77 kcm_operation opcode,
78 krb5_storage *request,
79 krb5_storage *response)
82 krb5_error_code ret;
83 char *name = NULL;
84 kcm_ccache ccache;
86 ret = krb5_ret_stringz(request, &name);
87 if (ret)
88 return ret;
90 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
92 ret = kcm_ccache_resolve_client(context, client, opcode,
93 name, &ccache);
94 if (ret) {
95 free(name);
96 return ret;
99 ret = krb5_store_stringz(response, ccache->name);
100 if (ret) {
101 kcm_release_ccache(context, ccache);
102 free(name);
103 return ret;
106 free(name);
107 kcm_release_ccache(context, ccache);
108 return 0;
112 * Request:
114 * Response:
115 * NameZ
117 static krb5_error_code
118 kcm_op_gen_new(krb5_context context,
119 kcm_client *client,
120 kcm_operation opcode,
121 krb5_storage *request,
122 krb5_storage *response)
124 krb5_error_code ret;
125 char *name;
127 KCM_LOG_REQUEST(context, client, opcode);
129 name = kcm_ccache_nextid(client->pid, client->uid, client->gid);
130 if (name == NULL) {
131 return KRB5_CC_NOMEM;
134 ret = krb5_store_stringz(response, name);
135 free(name);
137 return ret;
141 * Request:
142 * NameZ
143 * Principal
145 * Response:
148 static krb5_error_code
149 kcm_op_initialize(krb5_context context,
150 kcm_client *client,
151 kcm_operation opcode,
152 krb5_storage *request,
153 krb5_storage *response)
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
163 KCM_LOG_REQUEST(context, client, opcode);
165 ret = krb5_ret_stringz(request, &name);
166 if (ret)
167 return ret;
169 ret = krb5_ret_principal(request, &principal);
170 if (ret) {
171 free(name);
172 return ret;
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;
182 ccache->client = principal;
184 free(name);
186 #if 0
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
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);
200 #endif
202 kcm_release_ccache(context, ccache);
204 return ret;
208 * Request:
209 * NameZ
211 * Response:
214 static krb5_error_code
215 kcm_op_destroy(krb5_context context,
216 kcm_client *client,
217 kcm_operation opcode,
218 krb5_storage *request,
219 krb5_storage *response)
221 krb5_error_code ret;
222 char *name;
224 ret = krb5_ret_stringz(request, &name);
225 if (ret)
226 return ret;
228 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
230 ret = kcm_ccache_destroy_client(context, client, name);
231 if (ret == 0)
232 kcm_drop_default_cache(context, client, name);
234 free(name);
236 return ret;
240 * Request:
241 * NameZ
242 * Creds
244 * Response:
247 static krb5_error_code
248 kcm_op_store(krb5_context context,
249 kcm_client *client,
250 kcm_operation opcode,
251 krb5_storage *request,
252 krb5_storage *response)
254 krb5_creds creds;
255 krb5_error_code ret;
256 kcm_ccache ccache;
257 char *name;
259 ret = krb5_ret_stringz(request, &name);
260 if (ret)
261 return ret;
263 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
265 ret = krb5_ret_creds(request, &creds);
266 if (ret) {
267 free(name);
268 return ret;
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;
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;
287 kcm_ccache_enqueue_default(context, ccache, &creds);
289 free(name);
290 kcm_release_ccache(context, ccache);
292 return 0;
296 * Request:
297 * NameZ
298 * WhichFields
299 * MatchCreds
301 * Response:
302 * Creds
305 static krb5_error_code
306 kcm_op_retrieve(krb5_context context,
307 kcm_client *client,
308 kcm_operation opcode,
309 krb5_storage *request,
310 krb5_storage *response)
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;
320 ret = krb5_ret_stringz(request, &name);
321 if (ret)
322 return ret;
324 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
326 ret = krb5_ret_uint32(request, &flags);
327 if (ret) {
328 free(name);
329 return ret;
332 ret = krb5_ret_creds_tag(request, &mcreds);
333 if (ret) {
334 free(name);
335 return ret;
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)
342 free(name);
343 krb5_free_cred_contents(context, &mcreds);
344 return KRB5_FCC_PERM;
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;
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;
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);
369 if (ret == 0)
370 free_creds = 1;
372 HEIMDAL_MUTEX_unlock(&ccache->mutex);
375 if (ret == 0) {
376 ret = krb5_store_creds(response, credp);
379 free(name);
380 krb5_free_cred_contents(context, &mcreds);
381 kcm_release_ccache(context, ccache);
383 if (free_creds)
384 krb5_free_cred_contents(context, credp);
386 return ret;
390 * Request:
391 * NameZ
393 * Response:
394 * Principal
396 static krb5_error_code
397 kcm_op_get_principal(krb5_context context,
398 kcm_client *client,
399 kcm_operation opcode,
400 krb5_storage *request,
401 krb5_storage *response)
403 krb5_error_code ret;
404 kcm_ccache ccache;
405 char *name;
407 ret = krb5_ret_stringz(request, &name);
408 if (ret)
409 return ret;
411 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
413 ret = kcm_ccache_resolve_client(context, client, opcode,
414 name, &ccache);
415 if (ret) {
416 free(name);
417 return ret;
420 if (ccache->client == NULL)
421 ret = KRB5_CC_NOTFOUND;
422 else
423 ret = krb5_store_principal(response, ccache->client);
425 free(name);
426 kcm_release_ccache(context, ccache);
428 return 0;
432 * Request:
433 * NameZ
435 * Response:
436 * UUIDs
439 static krb5_error_code
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)
446 struct kcm_creds *creds;
447 krb5_error_code ret;
448 kcm_ccache ccache;
449 char *name;
451 ret = krb5_ret_stringz(request, &name);
452 if (ret)
453 return ret;
455 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
457 ret = kcm_ccache_resolve_client(context, client, opcode,
458 name, &ccache);
459 free(name);
460 if (ret)
461 return ret;
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;
472 kcm_release_ccache(context, ccache);
474 return ret;
478 * Request:
479 * NameZ
480 * Cursor
482 * Response:
483 * Creds
485 static krb5_error_code
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)
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;
499 ret = krb5_ret_stringz(request, &name);
500 if (ret)
501 return ret;
503 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
505 ret = kcm_ccache_resolve_client(context, client, opcode,
506 name, &ccache);
507 free(name);
508 if (ret)
509 return ret;
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;
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;
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);
530 return ret;
534 * Request:
535 * NameZ
536 * WhichFields
537 * MatchCreds
539 * Response:
542 static krb5_error_code
543 kcm_op_remove_cred(krb5_context context,
544 kcm_client *client,
545 kcm_operation opcode,
546 krb5_storage *request,
547 krb5_storage *response)
549 uint32_t whichfields;
550 krb5_creds mcreds;
551 krb5_error_code ret;
552 kcm_ccache ccache;
553 char *name;
555 ret = krb5_ret_stringz(request, &name);
556 if (ret)
557 return ret;
559 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
561 ret = krb5_ret_uint32(request, &whichfields);
562 if (ret) {
563 free(name);
564 return ret;
567 ret = krb5_ret_creds_tag(request, &mcreds);
568 if (ret) {
569 free(name);
570 return ret;
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;
581 ret = kcm_ccache_remove_cred(context, ccache, whichfields, &mcreds);
583 /* XXX need to remove any events that match */
585 free(name);
586 krb5_free_cred_contents(context, &mcreds);
587 kcm_release_ccache(context, ccache);
589 return ret;
593 * Request:
594 * NameZ
595 * Flags
597 * Response:
600 static krb5_error_code
601 kcm_op_set_flags(krb5_context context,
602 kcm_client *client,
603 kcm_operation opcode,
604 krb5_storage *request,
605 krb5_storage *response)
607 uint32_t flags;
608 krb5_error_code ret;
609 kcm_ccache ccache;
610 char *name;
612 ret = krb5_ret_stringz(request, &name);
613 if (ret)
614 return ret;
616 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
618 ret = krb5_ret_uint32(request, &flags);
619 if (ret) {
620 free(name);
621 return ret;
624 ret = kcm_ccache_resolve_client(context, client, opcode,
625 name, &ccache);
626 if (ret) {
627 free(name);
628 return ret;
631 /* we don't really support any flags yet */
632 free(name);
633 kcm_release_ccache(context, ccache);
635 return 0;
639 * Request:
640 * NameZ
641 * UID
642 * GID
644 * Response:
647 static krb5_error_code
648 kcm_op_chown(krb5_context context,
649 kcm_client *client,
650 kcm_operation opcode,
651 krb5_storage *request,
652 krb5_storage *response)
654 uint32_t uid;
655 uint32_t gid;
656 krb5_error_code ret;
657 kcm_ccache ccache;
658 char *name;
660 ret = krb5_ret_stringz(request, &name);
661 if (ret)
662 return ret;
664 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
666 ret = krb5_ret_uint32(request, &uid);
667 if (ret) {
668 free(name);
669 return ret;
672 ret = krb5_ret_uint32(request, &gid);
673 if (ret) {
674 free(name);
675 return ret;
678 ret = kcm_ccache_resolve_client(context, client, opcode,
679 name, &ccache);
680 if (ret) {
681 free(name);
682 return ret;
685 ret = kcm_chown(context, client, ccache, uid, gid);
687 free(name);
688 kcm_release_ccache(context, ccache);
690 return ret;
694 * Request:
695 * NameZ
696 * Mode
698 * Response:
701 static krb5_error_code
702 kcm_op_chmod(krb5_context context,
703 kcm_client *client,
704 kcm_operation opcode,
705 krb5_storage *request,
706 krb5_storage *response)
708 uint16_t mode;
709 krb5_error_code ret;
710 kcm_ccache ccache;
711 char *name;
713 ret = krb5_ret_stringz(request, &name);
714 if (ret)
715 return ret;
717 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
719 ret = krb5_ret_uint16(request, &mode);
720 if (ret) {
721 free(name);
722 return ret;
725 ret = kcm_ccache_resolve_client(context, client, opcode,
726 name, &ccache);
727 if (ret) {
728 free(name);
729 return ret;
732 ret = kcm_chmod(context, client, ccache, mode);
734 free(name);
735 kcm_release_ccache(context, ccache);
737 return ret;
741 * Protocol extensions for moving ticket acquisition responsibility
742 * from client to KCM follow.
746 * Request:
747 * NameZ
748 * ServerPrincipalPresent
749 * ServerPrincipal OPTIONAL
750 * Key
752 * Repsonse:
755 static krb5_error_code
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)
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;
769 krb5_keyblock_zero(&key);
771 ret = krb5_ret_stringz(request, &name);
772 if (ret)
773 return ret;
775 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
777 ret = krb5_ret_int8(request, &not_tgt);
778 if (ret) {
779 free(name);
780 return ret;
783 if (not_tgt) {
784 ret = krb5_ret_principal(request, &server);
785 if (ret) {
786 free(name);
787 return ret;
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;
799 ret = kcm_ccache_resolve_client(context, client, opcode,
800 name, &ccache);
801 if (ret == 0) {
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);
816 if (ret) {
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);
825 free(name);
827 if (ret != 0) {
828 krb5_free_principal(context, server);
829 krb5_free_keyblock(context, &key);
832 kcm_release_ccache(context, ccache);
834 return ret;
838 * Request:
839 * NameZ
840 * ServerPrincipal
841 * KDCFlags
842 * EncryptionType
844 * Repsonse:
847 static krb5_error_code
848 kcm_op_get_ticket(krb5_context context,
849 kcm_client *client,
850 kcm_operation opcode,
851 krb5_storage *request,
852 krb5_storage *response)
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;
862 memset(&in, 0, sizeof(in));
864 ret = krb5_ret_stringz(request, &name);
865 if (ret)
866 return ret;
868 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
870 ret = krb5_ret_uint32(request, &flags.i);
871 if (ret) {
872 free(name);
873 return ret;
876 ret = krb5_ret_int32(request, &in.session.keytype);
877 if (ret) {
878 free(name);
879 return ret;
882 ret = krb5_ret_principal(request, &server);
883 if (ret) {
884 free(name);
885 return ret;
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;
896 HEIMDAL_MUTEX_lock(&ccache->mutex);
898 /* Fake up an internal ccache */
899 kcm_internal_ccache(context, ccache, &ccdata);
901 in.client = ccache->client;
902 in.server = server;
903 in.times.endtime = 0;
905 /* glue cc layer will store creds */
906 ret = krb5_get_credentials_with_flags(context, 0, flags,
907 &ccdata, &in, &out);
909 HEIMDAL_MUTEX_unlock(&ccache->mutex);
911 krb5_free_principal(context, server);
913 if (ret == 0)
914 krb5_free_cred_contents(context, out);
916 kcm_release_ccache(context, ccache);
917 free(name);
919 return ret;
923 * Request:
924 * OldNameZ
925 * NewNameZ
927 * Repsonse:
930 static krb5_error_code
931 kcm_op_move_cache(krb5_context context,
932 kcm_client *client,
933 kcm_operation opcode,
934 krb5_storage *request,
935 krb5_storage *response)
937 krb5_error_code ret;
938 kcm_ccache oldid, newid;
939 char *oldname, *newname;
941 ret = krb5_ret_stringz(request, &oldname);
942 if (ret)
943 return ret;
945 KCM_LOG_REQUEST_NAME(context, client, opcode, oldname);
947 ret = krb5_ret_stringz(request, &newname);
948 if (ret) {
949 free(oldname);
950 return ret;
953 /* move to ourself is simple, done! */
954 if (strcmp(oldname, newname) == 0) {
955 free(oldname);
956 free(newname);
957 return 0;
960 ret = kcm_ccache_resolve_client(context, client, opcode, oldname, &oldid);
961 if (ret) {
962 free(oldname);
963 free(newname);
964 return ret;
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);
973 if (ret) {
974 free(oldname);
975 kcm_release_ccache(context, oldid);
976 return ret;
979 HEIMDAL_MUTEX_lock(&oldid->mutex);
980 HEIMDAL_MUTEX_lock(&newid->mutex);
982 /* move content */
984 kcm_ccache_data tmp;
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);
996 #undef MOVE
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);
1006 if (ret == 0)
1007 kcm_drop_default_cache(context, client, oldname);
1009 free(oldname);
1011 return ret;
1014 static krb5_error_code
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)
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,
1028 kcm_client *client,
1029 kcm_operation opcode,
1030 krb5_storage *request,
1031 krb5_storage *response)
1033 krb5_error_code ret;
1034 kcmuuid_t uuid;
1035 ssize_t sret;
1036 kcm_ccache cache;
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);
1043 return KRB5_CC_IO;
1046 ret = kcm_ccache_resolve_by_uuid(context, uuid, &cache);
1047 if (ret)
1048 return ret;
1050 ret = kcm_access(context, client, opcode, cache);
1051 if (ret)
1052 ret = KRB5_FCC_NOFILE;
1054 if (ret == 0)
1055 ret = krb5_store_stringz(response, cache->name);
1057 kcm_release_ccache(context, cache);
1059 return ret;
1062 struct kcm_default_cache *default_caches;
1064 static krb5_error_code
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)
1071 struct kcm_default_cache *c;
1072 krb5_error_code ret;
1073 const char *name = NULL;
1074 char *n = 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)) {
1080 name = c->name;
1081 break;
1084 if (name == NULL)
1085 name = n = kcm_ccache_first_name(client);
1087 if (name == NULL) {
1088 asprintf(&n, "%d", (int)client->uid);
1089 name = n;
1091 if (name == NULL)
1092 return ENOMEM;
1093 ret = krb5_store_stringz(response, name);
1094 if (n)
1095 free(n);
1096 return ret;
1099 static void
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))
1106 continue;
1107 if (strcmp((*c)->name, name) == 0) {
1108 struct kcm_default_cache *h = *c;
1109 *c = (*c)->next;
1110 free(h->name);
1111 free(h);
1112 break;
1117 static krb5_error_code
1118 kcm_op_set_default_cache(krb5_context context,
1119 kcm_client *client,
1120 kcm_operation opcode,
1121 krb5_storage *request,
1122 krb5_storage *response)
1124 struct kcm_default_cache *c;
1125 krb5_error_code ret;
1126 char *name;
1128 ret = krb5_ret_stringz(request, &name);
1129 if (ret)
1130 return ret;
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))
1136 break;
1138 if (c == NULL) {
1139 c = malloc(sizeof(*c));
1140 if (c == NULL)
1141 return ENOMEM;
1142 c->session = client->session;
1143 c->uid = client->uid;
1144 c->name = strdup(name);
1146 c->next = default_caches;
1147 default_caches = c;
1148 } else {
1149 free(c->name);
1150 c->name = strdup(name);
1153 return 0;
1156 static krb5_error_code
1157 kcm_op_get_kdc_offset(krb5_context context,
1158 kcm_client *client,
1159 kcm_operation opcode,
1160 krb5_storage *request,
1161 krb5_storage *response)
1163 krb5_error_code ret;
1164 kcm_ccache ccache;
1165 char *name;
1167 ret = krb5_ret_stringz(request, &name);
1168 if (ret)
1169 return ret;
1171 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
1173 ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache);
1174 free(name);
1175 if (ret)
1176 return ret;
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);
1184 return ret;
1187 static krb5_error_code
1188 kcm_op_set_kdc_offset(krb5_context context,
1189 kcm_client *client,
1190 kcm_operation opcode,
1191 krb5_storage *request,
1192 krb5_storage *response)
1194 krb5_error_code ret;
1195 kcm_ccache ccache;
1196 int32_t offset;
1197 char *name;
1199 ret = krb5_ret_stringz(request, &name);
1200 if (ret)
1201 return ret;
1203 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
1205 ret = krb5_ret_int32(request, &offset);
1206 if (ret) {
1207 free(name);
1208 return ret;
1211 ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache);
1212 free(name);
1213 if (ret)
1214 return ret;
1216 HEIMDAL_MUTEX_lock(&ccache->mutex);
1217 ccache->kdc_offset = offset;
1218 HEIMDAL_MUTEX_unlock(&ccache->mutex);
1220 kcm_release_ccache(context, ccache);
1222 return ret;
1225 struct kcm_ntlm_cred {
1226 kcmuuid_t uuid;
1227 char *user;
1228 char *domain;
1229 krb5_data nthash;
1230 uid_t uid;
1231 pid_t session;
1232 struct kcm_ntlm_cred *next;
1235 static struct kcm_ntlm_cred *ntlm_head;
1237 static void
1238 free_cred(struct kcm_ntlm_cred *cred)
1240 free(cred->user);
1241 free(cred->domain);
1242 krb5_data_free(&cred->nthash);
1243 free(cred);
1248 * name
1249 * domain
1250 * ntlm hash
1252 * Reply:
1253 * uuid
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))
1265 return c;
1267 return NULL;
1270 static krb5_error_code
1271 kcm_op_add_ntlm_cred(krb5_context context,
1272 kcm_client *client,
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));
1281 if (cred == NULL)
1282 return ENOMEM;
1284 RAND_bytes(cred->uuid, sizeof(cred->uuid));
1286 ret = krb5_ret_stringz(request, &cred->user);
1287 if (ret)
1288 goto error;
1290 ret = krb5_ret_stringz(request, &cred->domain);
1291 if (ret)
1292 goto error;
1294 ret = krb5_ret_data(request, &cred->nthash);
1295 if (ret)
1296 goto error;
1298 /* search for dups */
1299 c = find_ntlm_cred(cred->user, cred->domain, client);
1300 if (c) {
1301 krb5_data hash = c->nthash;
1302 c->nthash = cred->nthash;
1303 cred->nthash = hash;
1304 free_cred(cred);
1305 cred = c;
1306 } else {
1307 cred->next = ntlm_head;
1308 ntlm_head = cred;
1311 cred->uid = client->uid;
1312 cred->session = client->session;
1314 /* write response */
1315 (void)krb5_storage_write(response, &cred->uuid, sizeof(cred->uuid));
1317 return 0;
1319 error:
1320 free_cred(cred);
1322 return ret;
1326 * { "HAVE_NTLM_CRED", NULL },
1328 * input:
1329 * name
1330 * domain
1333 static krb5_error_code
1334 kcm_op_have_ntlm_cred(krb5_context context,
1335 kcm_client *client,
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);
1345 if (ret)
1346 goto error;
1348 ret = krb5_ret_stringz(request, &domain);
1349 if (ret)
1350 goto error;
1352 if (domain[0] == '\0') {
1353 free(domain);
1354 domain = NULL;
1357 c = find_ntlm_cred(user, domain, client);
1358 if (c == NULL)
1359 ret = ENOENT;
1361 error:
1362 free(user);
1363 if (domain)
1364 free(domain);
1366 return ret;
1370 * { "DEL_NTLM_CRED", NULL },
1372 * input:
1373 * name
1374 * domain
1377 static krb5_error_code
1378 kcm_op_del_ntlm_cred(krb5_context context,
1379 kcm_client *client,
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);
1389 if (ret)
1390 goto error;
1392 ret = krb5_ret_stringz(request, &domain);
1393 if (ret)
1394 goto error;
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))
1400 c = *cp;
1401 *cp = c->next;
1403 free_cred(c);
1404 break;
1408 error:
1409 free(user);
1410 free(domain);
1412 return ret;
1416 * { "DO_NTLM_AUTH", NULL },
1418 * input:
1419 * name:string
1420 * domain:string
1421 * type2:data
1423 * reply:
1424 * type3:data
1425 * flags:int32
1426 * session-key:data
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,
1435 kcm_client *client,
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;
1445 krb5_data data;
1446 krb5_error_code ret;
1447 uint32_t flags = 0;
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);
1455 if (ret)
1456 goto error;
1458 ret = krb5_ret_stringz(request, &domain);
1459 if (ret)
1460 goto error;
1462 if (domain[0] == '\0') {
1463 free(domain);
1464 domain = NULL;
1467 c = find_ntlm_cred(user, domain, client);
1468 if (c == NULL) {
1469 ret = EINVAL;
1470 goto error;
1473 ret = krb5_ret_data(request, &data);
1474 if (ret)
1475 goto error;
1477 ndata.data = data.data;
1478 ndata.length = data.length;
1480 ret = heim_ntlm_decode_type2(&ndata, &type2);
1481 krb5_data_free(&data);
1482 if (ret)
1483 goto error;
1485 if (domain && strcmp(domain, type2.targetname) == 0) {
1486 ret = EINVAL;
1487 goto error;
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) {
1506 ret = EINVAL;
1507 goto error;
1510 ret = heim_ntlm_calculate_ntlm2_sess(nonce,
1511 type2.challenge,
1512 c->nthash.data,
1513 &type3.lm,
1514 &type3.ntlm);
1515 } else {
1516 ret = heim_ntlm_calculate_ntlm1(c->nthash.data,
1517 c->nthash.length,
1518 type2.challenge,
1519 &type3.ntlm);
1522 if (ret)
1523 goto error;
1525 ret = heim_ntlm_build_ntlm1_master(c->nthash.data,
1526 c->nthash.length,
1527 &sessionkey,
1528 &type3.sessionkey);
1529 if (ret) {
1530 if (type3.lm.data)
1531 free(type3.lm.data);
1532 if (type3.ntlm.data)
1533 free(type3.ntlm.data);
1534 goto error;
1537 free(sessionkey.data);
1538 if (ret) {
1539 if (type3.lm.data)
1540 free(type3.lm.data);
1541 if (type3.ntlm.data)
1542 free(type3.ntlm.data);
1543 goto error;
1545 flags |= NTLM_FLAG_SESSIONKEY;
1546 #if 0
1547 } else {
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);
1555 if(ret) {
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,
1571 type3.username,
1572 name->domain,
1573 type2.challenge,
1574 &type2.targetinfo,
1575 ntlmv2,
1576 &type3.ntlm);
1577 if (ret) {
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),
1585 &sessionkey,
1586 &type3.sessionkey);
1587 memset(ntlmv2, 0, sizeof(ntlmv2));
1588 if (ret) {
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 |
1596 NTLM_FLAG_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);
1604 if (ret) {
1605 _gss_ntlm_delete_sec_context(minor_status,
1606 context_handle, NULL);
1607 *minor_status = ret;
1608 return GSS_S_FAILURE;
1610 #endif
1613 #if 0
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);
1621 } else {
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);
1630 #endif
1632 ret = heim_ntlm_encode_type3(&type3, &ndata);
1633 if (ret)
1634 goto error;
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;
1651 error:
1652 free(type3.username);
1653 heim_ntlm_free_type2(&type2);
1654 free(user);
1655 if (domain)
1656 free(domain);
1658 return ret;
1663 * { "GET_NTLM_UUID_LIST", NULL }
1665 * reply:
1666 * 1 user domain
1667 * 0 [ end of list ]
1670 static krb5_error_code
1671 kcm_op_get_ntlm_user_list(krb5_context context,
1672 kcm_client *client,
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))
1682 continue;
1684 ret = krb5_store_uint32(response, 1);
1685 if (ret)
1686 return ret;
1687 ret = krb5_store_stringz(response, c->user);
1688 if (ret)
1689 return ret;
1690 ret = krb5_store_stringz(response, c->domain);
1691 if (ret)
1692 return ret;
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 }
1734 const char *
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;
1743 krb5_error_code
1744 kcm_dispatch(krb5_context context,
1745 kcm_client *client,
1746 krb5_data *req_data,
1747 krb5_data *resp_data)
1749 krb5_error_code ret;
1750 kcm_method method;
1751 krb5_storage *req_sp = NULL;
1752 krb5_storage *resp_sp = NULL;
1753 uint16_t opcode;
1755 resp_sp = krb5_storage_emem();
1756 if (resp_sp == NULL) {
1757 return ENOMEM;
1760 if (client->pid == -1) {
1761 kcm_log(0, "Client had invalid process number");
1762 ret = KRB5_FCC_INTERNAL;
1763 goto out;
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",
1769 client->pid);
1770 ret = KRB5_CC_IO;
1771 goto out;
1774 ret = krb5_ret_uint16(req_sp, &opcode);
1775 if (ret) {
1776 kcm_log(0, "Process %d: didn't send a message", client->pid);
1777 goto out;
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;
1784 goto out;
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;
1791 goto out;
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);
1799 out:
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);
1810 return ret;