kadmind: check for KADM5_PRIV_GET when op GET
[heimdal.git] / kcm / protocol.c
blobbb73a01ee28a52666ba8ce79ace4f9ac7b469a94
1 /*
2 * Copyright (c) 2005, PADL Software Pty Ltd.
3 * All rights reserved.
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
9 * are met:
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
32 * SUCH DAMAGE.
35 #include "kcm_locl.h"
36 #include <heimntlm.h>
38 static void
39 kcm_drop_default_cache(krb5_context context, kcm_client *client, char *name);
42 int
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 */
46 if (session != -1)
47 return (client->session == session);
48 else
49 #endif
50 return (client->uid == uid);
53 static krb5_error_code
54 kcm_op_noop(krb5_context context,
55 kcm_client *client,
56 kcm_operation opcode,
57 krb5_storage *request,
58 krb5_storage *response)
60 KCM_LOG_REQUEST(context, client, opcode);
62 return 0;
66 * Request:
67 * NameZ
68 * Response:
69 * NameZ
72 static krb5_error_code
73 kcm_op_get_name(krb5_context context,
74 kcm_client *client,
75 kcm_operation opcode,
76 krb5_storage *request,
77 krb5_storage *response)
80 krb5_error_code ret;
81 char *name = NULL;
82 kcm_ccache ccache;
84 ret = krb5_ret_stringz(request, &name);
85 if (ret)
86 return ret;
88 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
90 ret = kcm_ccache_resolve_client(context, client, opcode,
91 name, &ccache);
92 if (ret) {
93 free(name);
94 return ret;
97 ret = krb5_store_stringz(response, ccache->name);
98 if (ret) {
99 kcm_release_ccache(context, ccache);
100 free(name);
101 return ret;
104 free(name);
105 kcm_release_ccache(context, ccache);
106 return 0;
110 * Request:
112 * Response:
113 * NameZ
115 static krb5_error_code
116 kcm_op_gen_new(krb5_context context,
117 kcm_client *client,
118 kcm_operation opcode,
119 krb5_storage *request,
120 krb5_storage *response)
122 krb5_error_code ret;
123 char *name;
125 KCM_LOG_REQUEST(context, client, opcode);
127 name = kcm_ccache_nextid(client->pid, client->uid, client->gid);
128 if (name == NULL) {
129 return KRB5_CC_NOMEM;
132 ret = krb5_store_stringz(response, name);
133 free(name);
135 return ret;
139 * Request:
140 * NameZ
141 * Principal
143 * Response:
146 static krb5_error_code
147 kcm_op_initialize(krb5_context context,
148 kcm_client *client,
149 kcm_operation opcode,
150 krb5_storage *request,
151 krb5_storage *response)
153 kcm_ccache ccache;
154 krb5_principal principal;
155 krb5_error_code ret;
156 char *name;
157 #if 0
158 kcm_event event;
159 #endif
161 KCM_LOG_REQUEST(context, client, opcode);
163 ret = krb5_ret_stringz(request, &name);
164 if (ret)
165 return ret;
167 ret = krb5_ret_principal(request, &principal);
168 if (ret) {
169 free(name);
170 return ret;
173 ret = kcm_ccache_new_client(context, client, name, &ccache);
174 if (ret) {
175 free(name);
176 krb5_free_principal(context, principal);
177 return ret;
180 ccache->client = principal;
182 free(name);
184 #if 0
186 * Create a new credentials cache. To mitigate DoS attacks we will
187 * expire it in 30 minutes unless it has some credentials added
188 * to it
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);
198 #endif
200 kcm_release_ccache(context, ccache);
202 return ret;
206 * Request:
207 * NameZ
209 * Response:
212 static krb5_error_code
213 kcm_op_destroy(krb5_context context,
214 kcm_client *client,
215 kcm_operation opcode,
216 krb5_storage *request,
217 krb5_storage *response)
219 krb5_error_code ret;
220 char *name;
222 ret = krb5_ret_stringz(request, &name);
223 if (ret)
224 return ret;
226 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
228 ret = kcm_ccache_destroy_client(context, client, name);
229 if (ret == 0)
230 kcm_drop_default_cache(context, client, name);
232 free(name);
234 return ret;
238 * Request:
239 * NameZ
240 * Creds
242 * Response:
245 static krb5_error_code
246 kcm_op_store(krb5_context context,
247 kcm_client *client,
248 kcm_operation opcode,
249 krb5_storage *request,
250 krb5_storage *response)
252 krb5_creds creds;
253 krb5_error_code ret;
254 kcm_ccache ccache;
255 char *name;
257 ret = krb5_ret_stringz(request, &name);
258 if (ret)
259 return ret;
261 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
263 ret = krb5_ret_creds(request, &creds);
264 if (ret) {
265 free(name);
266 return ret;
269 ret = kcm_ccache_resolve_client(context, client, opcode,
270 name, &ccache);
271 if (ret) {
272 free(name);
273 krb5_free_cred_contents(context, &creds);
274 return ret;
277 ret = kcm_ccache_store_cred(context, ccache, &creds, 0);
278 if (ret) {
279 free(name);
280 krb5_free_cred_contents(context, &creds);
281 kcm_release_ccache(context, ccache);
282 return ret;
285 kcm_ccache_enqueue_default(context, ccache, &creds);
287 free(name);
288 kcm_release_ccache(context, ccache);
290 return 0;
294 * Request:
295 * NameZ
296 * WhichFields
297 * MatchCreds
299 * Response:
300 * Creds
303 static krb5_error_code
304 kcm_op_retrieve(krb5_context context,
305 kcm_client *client,
306 kcm_operation opcode,
307 krb5_storage *request,
308 krb5_storage *response)
310 uint32_t flags;
311 krb5_creds mcreds;
312 krb5_error_code ret;
313 kcm_ccache ccache;
314 char *name;
315 krb5_creds *credp;
316 int free_creds = 0;
318 ret = krb5_ret_stringz(request, &name);
319 if (ret)
320 return ret;
322 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
324 ret = krb5_ret_uint32(request, &flags);
325 if (ret) {
326 free(name);
327 return ret;
330 ret = krb5_ret_creds_tag(request, &mcreds);
331 if (ret) {
332 free(name);
333 return ret;
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)
340 free(name);
341 krb5_free_cred_contents(context, &mcreds);
342 return KRB5_FCC_PERM;
345 ret = kcm_ccache_resolve_client(context, client, opcode,
346 name, &ccache);
347 if (ret) {
348 free(name);
349 krb5_free_cred_contents(context, &mcreds);
350 return ret;
353 ret = kcm_ccache_retrieve_cred(context, ccache, flags,
354 &mcreds, &credp);
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);
367 if (ret == 0)
368 free_creds = 1;
370 HEIMDAL_MUTEX_unlock(&ccache->mutex);
373 if (ret == 0) {
374 ret = krb5_store_creds(response, credp);
377 free(name);
378 krb5_free_cred_contents(context, &mcreds);
379 kcm_release_ccache(context, ccache);
381 if (free_creds)
382 krb5_free_cred_contents(context, credp);
384 return ret;
388 * Request:
389 * NameZ
391 * Response:
392 * Principal
394 static krb5_error_code
395 kcm_op_get_principal(krb5_context context,
396 kcm_client *client,
397 kcm_operation opcode,
398 krb5_storage *request,
399 krb5_storage *response)
401 krb5_error_code ret;
402 kcm_ccache ccache;
403 char *name;
405 ret = krb5_ret_stringz(request, &name);
406 if (ret)
407 return ret;
409 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
411 ret = kcm_ccache_resolve_client(context, client, opcode,
412 name, &ccache);
413 if (ret) {
414 free(name);
415 return ret;
418 if (ccache->client == NULL)
419 ret = KRB5_CC_NOTFOUND;
420 else
421 ret = krb5_store_principal(response, ccache->client);
423 free(name);
424 kcm_release_ccache(context, ccache);
426 return 0;
430 * Request:
431 * NameZ
433 * Response:
434 * UUIDs
437 static krb5_error_code
438 kcm_op_get_cred_uuid_list(krb5_context context,
439 kcm_client *client,
440 kcm_operation opcode,
441 krb5_storage *request,
442 krb5_storage *response)
444 struct kcm_creds *creds;
445 krb5_error_code ret;
446 kcm_ccache ccache;
447 char *name;
449 ret = krb5_ret_stringz(request, &name);
450 if (ret)
451 return ret;
453 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
455 ret = kcm_ccache_resolve_client(context, client, opcode,
456 name, &ccache);
457 free(name);
458 if (ret)
459 return ret;
461 for (creds = ccache->creds ; creds ; creds = creds->next) {
462 ssize_t sret;
463 sret = krb5_storage_write(response, &creds->uuid, sizeof(creds->uuid));
464 if (sret != sizeof(creds->uuid)) {
465 ret = ENOMEM;
466 break;
470 kcm_release_ccache(context, ccache);
472 return ret;
476 * Request:
477 * NameZ
478 * Cursor
480 * Response:
481 * Creds
483 static krb5_error_code
484 kcm_op_get_cred_by_uuid(krb5_context context,
485 kcm_client *client,
486 kcm_operation opcode,
487 krb5_storage *request,
488 krb5_storage *response)
490 krb5_error_code ret;
491 kcm_ccache ccache;
492 char *name;
493 struct kcm_creds *c;
494 kcmuuid_t uuid;
495 ssize_t sret;
497 ret = krb5_ret_stringz(request, &name);
498 if (ret)
499 return ret;
501 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
503 ret = kcm_ccache_resolve_client(context, client, opcode,
504 name, &ccache);
505 free(name);
506 if (ret)
507 return ret;
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);
513 return KRB5_CC_IO;
516 c = kcm_ccache_find_cred_uuid(context, ccache, uuid);
517 if (c == NULL) {
518 kcm_release_ccache(context, ccache);
519 return KRB5_CC_END;
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);
528 return ret;
532 * Request:
533 * NameZ
534 * WhichFields
535 * MatchCreds
537 * Response:
540 static krb5_error_code
541 kcm_op_remove_cred(krb5_context context,
542 kcm_client *client,
543 kcm_operation opcode,
544 krb5_storage *request,
545 krb5_storage *response)
547 uint32_t whichfields;
548 krb5_creds mcreds;
549 krb5_error_code ret;
550 kcm_ccache ccache;
551 char *name;
553 ret = krb5_ret_stringz(request, &name);
554 if (ret)
555 return ret;
557 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
559 ret = krb5_ret_uint32(request, &whichfields);
560 if (ret) {
561 free(name);
562 return ret;
565 ret = krb5_ret_creds_tag(request, &mcreds);
566 if (ret) {
567 free(name);
568 return ret;
571 ret = kcm_ccache_resolve_client(context, client, opcode,
572 name, &ccache);
573 if (ret) {
574 free(name);
575 krb5_free_cred_contents(context, &mcreds);
576 return ret;
579 ret = kcm_ccache_remove_cred(context, ccache, whichfields, &mcreds);
581 /* XXX need to remove any events that match */
583 free(name);
584 krb5_free_cred_contents(context, &mcreds);
585 kcm_release_ccache(context, ccache);
587 return ret;
591 * Request:
592 * NameZ
593 * Flags
595 * Response:
598 static krb5_error_code
599 kcm_op_set_flags(krb5_context context,
600 kcm_client *client,
601 kcm_operation opcode,
602 krb5_storage *request,
603 krb5_storage *response)
605 uint32_t flags;
606 krb5_error_code ret;
607 kcm_ccache ccache;
608 char *name;
610 ret = krb5_ret_stringz(request, &name);
611 if (ret)
612 return ret;
614 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
616 ret = krb5_ret_uint32(request, &flags);
617 if (ret) {
618 free(name);
619 return ret;
622 ret = kcm_ccache_resolve_client(context, client, opcode,
623 name, &ccache);
624 if (ret) {
625 free(name);
626 return ret;
629 /* we don't really support any flags yet */
630 free(name);
631 kcm_release_ccache(context, ccache);
633 return 0;
637 * Request:
638 * NameZ
639 * UID
640 * GID
642 * Response:
645 static krb5_error_code
646 kcm_op_chown(krb5_context context,
647 kcm_client *client,
648 kcm_operation opcode,
649 krb5_storage *request,
650 krb5_storage *response)
652 uint32_t uid;
653 uint32_t gid;
654 krb5_error_code ret;
655 kcm_ccache ccache;
656 char *name;
658 ret = krb5_ret_stringz(request, &name);
659 if (ret)
660 return ret;
662 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
664 ret = krb5_ret_uint32(request, &uid);
665 if (ret) {
666 free(name);
667 return ret;
670 ret = krb5_ret_uint32(request, &gid);
671 if (ret) {
672 free(name);
673 return ret;
676 ret = kcm_ccache_resolve_client(context, client, opcode,
677 name, &ccache);
678 if (ret) {
679 free(name);
680 return ret;
683 ret = kcm_chown(context, client, ccache, uid, gid);
685 free(name);
686 kcm_release_ccache(context, ccache);
688 return ret;
692 * Request:
693 * NameZ
694 * Mode
696 * Response:
699 static krb5_error_code
700 kcm_op_chmod(krb5_context context,
701 kcm_client *client,
702 kcm_operation opcode,
703 krb5_storage *request,
704 krb5_storage *response)
706 uint16_t mode;
707 krb5_error_code ret;
708 kcm_ccache ccache;
709 char *name;
711 ret = krb5_ret_stringz(request, &name);
712 if (ret)
713 return ret;
715 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
717 ret = krb5_ret_uint16(request, &mode);
718 if (ret) {
719 free(name);
720 return ret;
723 ret = kcm_ccache_resolve_client(context, client, opcode,
724 name, &ccache);
725 if (ret) {
726 free(name);
727 return ret;
730 ret = kcm_chmod(context, client, ccache, mode);
732 free(name);
733 kcm_release_ccache(context, ccache);
735 return ret;
739 * Protocol extensions for moving ticket acquisition responsibility
740 * from client to KCM follow.
744 * Request:
745 * NameZ
746 * ServerPrincipalPresent
747 * ServerPrincipal OPTIONAL
748 * Key
750 * Repsonse:
753 static krb5_error_code
754 kcm_op_get_initial_ticket(krb5_context context,
755 kcm_client *client,
756 kcm_operation opcode,
757 krb5_storage *request,
758 krb5_storage *response)
760 krb5_error_code ret;
761 kcm_ccache ccache;
762 char *name;
763 int8_t not_tgt = 0;
764 krb5_principal server = NULL;
765 krb5_keyblock key;
767 krb5_keyblock_zero(&key);
769 ret = krb5_ret_stringz(request, &name);
770 if (ret)
771 return ret;
773 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
775 ret = krb5_ret_int8(request, &not_tgt);
776 if (ret) {
777 free(name);
778 return ret;
781 if (not_tgt) {
782 ret = krb5_ret_principal(request, &server);
783 if (ret) {
784 free(name);
785 return ret;
789 ret = krb5_ret_keyblock(request, &key);
790 if (ret) {
791 free(name);
792 if (server != NULL)
793 krb5_free_principal(context, server);
794 return ret;
797 ret = kcm_ccache_resolve_client(context, client, opcode,
798 name, &ccache);
799 if (ret == 0) {
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);
814 if (ret) {
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);
823 free(name);
825 if (ret != 0) {
826 krb5_free_principal(context, server);
827 krb5_free_keyblock(context, &key);
830 kcm_release_ccache(context, ccache);
832 return ret;
836 * Request:
837 * NameZ
838 * ServerPrincipal
839 * KDCFlags
840 * EncryptionType
842 * Repsonse:
845 static krb5_error_code
846 kcm_op_get_ticket(krb5_context context,
847 kcm_client *client,
848 kcm_operation opcode,
849 krb5_storage *request,
850 krb5_storage *response)
852 krb5_error_code ret;
853 kcm_ccache ccache;
854 char *name;
855 krb5_principal server = NULL;
856 krb5_ccache_data ccdata;
857 krb5_creds in, *out;
858 krb5_kdc_flags flags;
860 memset(&in, 0, sizeof(in));
862 ret = krb5_ret_stringz(request, &name);
863 if (ret)
864 return ret;
866 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
868 ret = krb5_ret_uint32(request, &flags.i);
869 if (ret) {
870 free(name);
871 return ret;
874 ret = krb5_ret_int32(request, &in.session.keytype);
875 if (ret) {
876 free(name);
877 return ret;
880 ret = krb5_ret_principal(request, &server);
881 if (ret) {
882 free(name);
883 return ret;
886 ret = kcm_ccache_resolve_client(context, client, opcode,
887 name, &ccache);
888 if (ret) {
889 krb5_free_principal(context, server);
890 free(name);
891 return ret;
894 HEIMDAL_MUTEX_lock(&ccache->mutex);
896 /* Fake up an internal ccache */
897 kcm_internal_ccache(context, ccache, &ccdata);
899 in.client = ccache->client;
900 in.server = server;
901 in.times.endtime = 0;
903 /* glue cc layer will store creds */
904 ret = krb5_get_credentials_with_flags(context, 0, flags,
905 &ccdata, &in, &out);
907 HEIMDAL_MUTEX_unlock(&ccache->mutex);
909 krb5_free_principal(context, server);
911 if (ret == 0)
912 krb5_free_cred_contents(context, out);
914 kcm_release_ccache(context, ccache);
915 free(name);
917 return ret;
921 * Request:
922 * OldNameZ
923 * NewNameZ
925 * Repsonse:
928 static krb5_error_code
929 kcm_op_move_cache(krb5_context context,
930 kcm_client *client,
931 kcm_operation opcode,
932 krb5_storage *request,
933 krb5_storage *response)
935 krb5_error_code ret;
936 kcm_ccache oldid, newid;
937 char *oldname, *newname;
939 ret = krb5_ret_stringz(request, &oldname);
940 if (ret)
941 return ret;
943 KCM_LOG_REQUEST_NAME(context, client, opcode, oldname);
945 ret = krb5_ret_stringz(request, &newname);
946 if (ret) {
947 free(oldname);
948 return ret;
951 /* move to ourself is simple, done! */
952 if (strcmp(oldname, newname) == 0) {
953 free(oldname);
954 free(newname);
955 return 0;
958 ret = kcm_ccache_resolve_client(context, client, opcode, oldname, &oldid);
959 if (ret) {
960 free(oldname);
961 free(newname);
962 return ret;
965 /* Check if new credential cache exists, if not create one. */
966 ret = kcm_ccache_resolve_client(context, client, opcode, newname, &newid);
967 if (ret == KRB5_FCC_NOFILE)
968 ret = kcm_ccache_new_client(context, client, newname, &newid);
969 free(newname);
971 if (ret) {
972 free(oldname);
973 kcm_release_ccache(context, oldid);
974 return ret;
977 HEIMDAL_MUTEX_lock(&oldid->mutex);
978 HEIMDAL_MUTEX_lock(&newid->mutex);
980 /* move content */
982 kcm_ccache_data tmp;
984 #define MOVE(n,o,f) { tmp.f = n->f ; n->f = o->f; o->f = tmp.f; }
986 MOVE(newid, oldid, flags);
987 MOVE(newid, oldid, client);
988 MOVE(newid, oldid, server);
989 MOVE(newid, oldid, creds);
990 MOVE(newid, oldid, tkt_life);
991 MOVE(newid, oldid, renew_life);
992 MOVE(newid, oldid, key);
993 MOVE(newid, oldid, kdc_offset);
994 #undef MOVE
997 HEIMDAL_MUTEX_unlock(&oldid->mutex);
998 HEIMDAL_MUTEX_unlock(&newid->mutex);
1000 kcm_release_ccache(context, oldid);
1001 kcm_release_ccache(context, newid);
1003 ret = kcm_ccache_destroy_client(context, client, oldname);
1004 if (ret == 0)
1005 kcm_drop_default_cache(context, client, oldname);
1007 free(oldname);
1009 return ret;
1012 static krb5_error_code
1013 kcm_op_get_cache_uuid_list(krb5_context context,
1014 kcm_client *client,
1015 kcm_operation opcode,
1016 krb5_storage *request,
1017 krb5_storage *response)
1019 KCM_LOG_REQUEST(context, client, opcode);
1021 return kcm_ccache_get_uuids(context, client, opcode, response);
1024 static krb5_error_code
1025 kcm_op_get_cache_by_uuid(krb5_context context,
1026 kcm_client *client,
1027 kcm_operation opcode,
1028 krb5_storage *request,
1029 krb5_storage *response)
1031 krb5_error_code ret;
1032 kcmuuid_t uuid;
1033 ssize_t sret;
1034 kcm_ccache cache;
1036 KCM_LOG_REQUEST(context, client, opcode);
1038 sret = krb5_storage_read(request, &uuid, sizeof(uuid));
1039 if (sret != sizeof(uuid)) {
1040 krb5_clear_error_message(context);
1041 return KRB5_CC_IO;
1044 ret = kcm_ccache_resolve_by_uuid(context, uuid, &cache);
1045 if (ret)
1046 return ret;
1048 ret = kcm_access(context, client, opcode, cache);
1049 if (ret)
1050 ret = KRB5_FCC_NOFILE;
1052 if (ret == 0)
1053 ret = krb5_store_stringz(response, cache->name);
1055 kcm_release_ccache(context, cache);
1057 return ret;
1060 struct kcm_default_cache *default_caches;
1062 static krb5_error_code
1063 kcm_op_get_default_cache(krb5_context context,
1064 kcm_client *client,
1065 kcm_operation opcode,
1066 krb5_storage *request,
1067 krb5_storage *response)
1069 struct kcm_default_cache *c;
1070 krb5_error_code ret;
1071 const char *name = NULL;
1072 char *n = NULL;
1073 int aret;
1075 KCM_LOG_REQUEST(context, client, opcode);
1077 for (c = default_caches; c != NULL; c = c->next) {
1078 if (kcm_is_same_session(client, c->uid, c->session)) {
1079 name = c->name;
1080 break;
1083 if (name == NULL)
1084 name = n = kcm_ccache_first_name(client);
1086 if (name == NULL) {
1087 aret = asprintf(&n, "%d", (int)client->uid);
1088 if (aret != -1)
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 tmpsesskey;
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 &tmpsesskey,
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(tmpsesskey.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, NULL);
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;