passdb: Use getline(3) to read our old machine sid
[Samba.git] / third_party / heimdal / kcm / protocol.c
blobb5442e458913b380b4911a26f8f8dd2e834ac7c2
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 && krb5_principal_is_krbtgt(context, mcreds.server))
338 free(name);
339 krb5_free_cred_contents(context, &mcreds);
340 return KRB5_FCC_PERM;
343 ret = kcm_ccache_resolve_client(context, client, opcode,
344 name, &ccache);
345 if (ret) {
346 free(name);
347 krb5_free_cred_contents(context, &mcreds);
348 return ret;
351 ret = kcm_ccache_retrieve_cred(context, ccache, flags,
352 &mcreds, &credp);
353 if (ret && ((flags & KRB5_GC_CACHED) == 0) &&
354 !krb5_is_config_principal(context, mcreds.server)) {
355 krb5_ccache_data ccdata;
357 /* try and acquire */
358 HEIMDAL_MUTEX_lock(&ccache->mutex);
360 /* Fake up an internal ccache */
361 kcm_internal_ccache(context, ccache, &ccdata);
363 /* glue cc layer will store creds */
364 ret = krb5_get_credentials(context, 0, &ccdata, &mcreds, &credp);
365 if (ret == 0)
366 free_creds = 1;
368 HEIMDAL_MUTEX_unlock(&ccache->mutex);
371 if (ret == 0) {
372 ret = krb5_store_creds(response, credp);
375 free(name);
376 krb5_free_cred_contents(context, &mcreds);
377 kcm_release_ccache(context, ccache);
379 if (free_creds)
380 krb5_free_cred_contents(context, credp);
382 return ret;
386 * Request:
387 * NameZ
389 * Response:
390 * Principal
392 static krb5_error_code
393 kcm_op_get_principal(krb5_context context,
394 kcm_client *client,
395 kcm_operation opcode,
396 krb5_storage *request,
397 krb5_storage *response)
399 krb5_error_code ret;
400 kcm_ccache ccache;
401 char *name;
403 ret = krb5_ret_stringz(request, &name);
404 if (ret)
405 return ret;
407 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
409 ret = kcm_ccache_resolve_client(context, client, opcode,
410 name, &ccache);
411 if (ret) {
412 free(name);
413 return ret;
416 if (ccache->client == NULL)
417 ret = KRB5_CC_NOTFOUND;
418 else
419 ret = krb5_store_principal(response, ccache->client);
421 free(name);
422 kcm_release_ccache(context, ccache);
424 return ret;
428 * Request:
429 * NameZ
431 * Response:
432 * UUIDs
435 static krb5_error_code
436 kcm_op_get_cred_uuid_list(krb5_context context,
437 kcm_client *client,
438 kcm_operation opcode,
439 krb5_storage *request,
440 krb5_storage *response)
442 struct kcm_creds *creds;
443 krb5_error_code ret;
444 kcm_ccache ccache;
445 char *name;
447 ret = krb5_ret_stringz(request, &name);
448 if (ret)
449 return ret;
451 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
453 ret = kcm_ccache_resolve_client(context, client, opcode,
454 name, &ccache);
455 free(name);
456 if (ret)
457 return ret;
459 for (creds = ccache->creds ; creds ; creds = creds->next) {
460 ssize_t sret;
461 sret = krb5_storage_write(response, &creds->uuid, sizeof(creds->uuid));
462 if (sret != sizeof(creds->uuid)) {
463 ret = ENOMEM;
464 break;
468 kcm_release_ccache(context, ccache);
470 return ret;
474 * Request:
475 * NameZ
476 * Cursor
478 * Response:
479 * Creds
481 static krb5_error_code
482 kcm_op_get_cred_by_uuid(krb5_context context,
483 kcm_client *client,
484 kcm_operation opcode,
485 krb5_storage *request,
486 krb5_storage *response)
488 krb5_error_code ret;
489 kcm_ccache ccache;
490 char *name;
491 struct kcm_creds *c;
492 kcmuuid_t uuid;
493 ssize_t sret;
495 ret = krb5_ret_stringz(request, &name);
496 if (ret)
497 return ret;
499 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
501 ret = kcm_ccache_resolve_client(context, client, opcode,
502 name, &ccache);
503 free(name);
504 if (ret)
505 return ret;
507 sret = krb5_storage_read(request, &uuid, sizeof(uuid));
508 if (sret != sizeof(uuid)) {
509 kcm_release_ccache(context, ccache);
510 krb5_clear_error_message(context);
511 return KRB5_CC_IO;
514 c = kcm_ccache_find_cred_uuid(context, ccache, uuid);
515 if (c == NULL) {
516 kcm_release_ccache(context, ccache);
517 return KRB5_CC_END;
520 HEIMDAL_MUTEX_lock(&ccache->mutex);
521 ret = krb5_store_creds(response, &c->cred);
522 HEIMDAL_MUTEX_unlock(&ccache->mutex);
524 kcm_release_ccache(context, ccache);
526 return ret;
530 * Request:
531 * NameZ
532 * WhichFields
533 * MatchCreds
535 * Response:
538 static krb5_error_code
539 kcm_op_remove_cred(krb5_context context,
540 kcm_client *client,
541 kcm_operation opcode,
542 krb5_storage *request,
543 krb5_storage *response)
545 uint32_t whichfields;
546 krb5_creds mcreds;
547 krb5_error_code ret;
548 kcm_ccache ccache;
549 char *name;
551 ret = krb5_ret_stringz(request, &name);
552 if (ret)
553 return ret;
555 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
557 ret = krb5_ret_uint32(request, &whichfields);
558 if (ret) {
559 free(name);
560 return ret;
563 ret = krb5_ret_creds_tag(request, &mcreds);
564 if (ret) {
565 free(name);
566 return ret;
569 ret = kcm_ccache_resolve_client(context, client, opcode,
570 name, &ccache);
571 if (ret) {
572 free(name);
573 krb5_free_cred_contents(context, &mcreds);
574 return ret;
577 ret = kcm_ccache_remove_cred(context, ccache, whichfields, &mcreds);
579 /* XXX need to remove any events that match */
581 free(name);
582 krb5_free_cred_contents(context, &mcreds);
583 kcm_release_ccache(context, ccache);
585 return ret;
589 * Request:
590 * NameZ
591 * Flags
593 * Response:
596 static krb5_error_code
597 kcm_op_set_flags(krb5_context context,
598 kcm_client *client,
599 kcm_operation opcode,
600 krb5_storage *request,
601 krb5_storage *response)
603 uint32_t flags;
604 krb5_error_code ret;
605 kcm_ccache ccache;
606 char *name;
608 ret = krb5_ret_stringz(request, &name);
609 if (ret)
610 return ret;
612 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
614 ret = krb5_ret_uint32(request, &flags);
615 if (ret) {
616 free(name);
617 return ret;
620 ret = kcm_ccache_resolve_client(context, client, opcode,
621 name, &ccache);
622 if (ret) {
623 free(name);
624 return ret;
627 /* we don't really support any flags yet */
628 free(name);
629 kcm_release_ccache(context, ccache);
631 return 0;
635 * Request:
636 * NameZ
637 * UID
638 * GID
640 * Response:
643 static krb5_error_code
644 kcm_op_chown(krb5_context context,
645 kcm_client *client,
646 kcm_operation opcode,
647 krb5_storage *request,
648 krb5_storage *response)
650 uint32_t uid;
651 uint32_t gid;
652 krb5_error_code ret;
653 kcm_ccache ccache;
654 char *name;
656 ret = krb5_ret_stringz(request, &name);
657 if (ret)
658 return ret;
660 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
662 ret = krb5_ret_uint32(request, &uid);
663 if (ret) {
664 free(name);
665 return ret;
668 ret = krb5_ret_uint32(request, &gid);
669 if (ret) {
670 free(name);
671 return ret;
674 ret = kcm_ccache_resolve_client(context, client, opcode,
675 name, &ccache);
676 if (ret) {
677 free(name);
678 return ret;
681 ret = kcm_chown(context, client, ccache, uid, gid);
683 free(name);
684 kcm_release_ccache(context, ccache);
686 return ret;
690 * Request:
691 * NameZ
692 * Mode
694 * Response:
697 static krb5_error_code
698 kcm_op_chmod(krb5_context context,
699 kcm_client *client,
700 kcm_operation opcode,
701 krb5_storage *request,
702 krb5_storage *response)
704 uint16_t mode;
705 krb5_error_code ret;
706 kcm_ccache ccache;
707 char *name;
709 ret = krb5_ret_stringz(request, &name);
710 if (ret)
711 return ret;
713 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
715 ret = krb5_ret_uint16(request, &mode);
716 if (ret) {
717 free(name);
718 return ret;
721 ret = kcm_ccache_resolve_client(context, client, opcode,
722 name, &ccache);
723 if (ret) {
724 free(name);
725 return ret;
728 ret = kcm_chmod(context, client, ccache, mode);
730 free(name);
731 kcm_release_ccache(context, ccache);
733 return ret;
737 * Protocol extensions for moving ticket acquisition responsibility
738 * from client to KCM follow.
742 * Request:
743 * NameZ
744 * ServerPrincipalPresent
745 * ServerPrincipal OPTIONAL
746 * Key
748 * Repsonse:
751 static krb5_error_code
752 kcm_op_get_initial_ticket(krb5_context context,
753 kcm_client *client,
754 kcm_operation opcode,
755 krb5_storage *request,
756 krb5_storage *response)
758 krb5_error_code ret;
759 kcm_ccache ccache;
760 char *name;
761 int8_t not_tgt = 0;
762 krb5_principal server = NULL;
763 krb5_keyblock key;
765 krb5_keyblock_zero(&key);
767 ret = krb5_ret_stringz(request, &name);
768 if (ret)
769 return ret;
771 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
773 ret = krb5_ret_int8(request, &not_tgt);
774 if (ret) {
775 free(name);
776 return ret;
779 if (not_tgt) {
780 ret = krb5_ret_principal(request, &server);
781 if (ret) {
782 free(name);
783 return ret;
787 ret = krb5_ret_keyblock(request, &key);
788 if (ret) {
789 free(name);
790 if (server != NULL)
791 krb5_free_principal(context, server);
792 return ret;
795 ret = kcm_ccache_resolve_client(context, client, opcode,
796 name, &ccache);
797 if (ret == 0) {
798 HEIMDAL_MUTEX_lock(&ccache->mutex);
800 if (ccache->server != NULL) {
801 krb5_free_principal(context, ccache->server);
802 ccache->server = NULL;
805 krb5_free_keyblock(context, &ccache->key.keyblock);
807 ccache->server = server;
808 ccache->key.keyblock = key;
809 ccache->flags |= KCM_FLAGS_USE_CACHED_KEY;
811 ret = kcm_ccache_enqueue_default(context, ccache, NULL);
812 if (ret) {
813 ccache->server = NULL;
814 krb5_keyblock_zero(&ccache->key.keyblock);
815 ccache->flags &= ~(KCM_FLAGS_USE_CACHED_KEY);
818 HEIMDAL_MUTEX_unlock(&ccache->mutex);
821 free(name);
823 if (ret != 0) {
824 krb5_free_principal(context, server);
825 krb5_free_keyblock_contents(context, &key);
828 kcm_release_ccache(context, ccache);
830 return ret;
834 * Request:
835 * NameZ
836 * ServerPrincipal
837 * KDCFlags
838 * EncryptionType
840 * Repsonse:
843 static krb5_error_code
844 kcm_op_get_ticket(krb5_context context,
845 kcm_client *client,
846 kcm_operation opcode,
847 krb5_storage *request,
848 krb5_storage *response)
850 krb5_error_code ret;
851 kcm_ccache ccache;
852 char *name;
853 krb5_principal server = NULL;
854 krb5_ccache_data ccdata;
855 krb5_creds in, *out;
856 krb5_kdc_flags flags;
858 memset(&in, 0, sizeof(in));
860 ret = krb5_ret_stringz(request, &name);
861 if (ret)
862 return ret;
864 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
866 ret = krb5_ret_uint32(request, &flags.i);
867 if (ret) {
868 free(name);
869 return ret;
872 ret = krb5_ret_int32(request, &in.session.keytype);
873 if (ret) {
874 free(name);
875 return ret;
878 ret = krb5_ret_principal(request, &server);
879 if (ret) {
880 free(name);
881 return ret;
884 ret = kcm_ccache_resolve_client(context, client, opcode,
885 name, &ccache);
886 if (ret) {
887 krb5_free_principal(context, server);
888 free(name);
889 return ret;
892 HEIMDAL_MUTEX_lock(&ccache->mutex);
894 /* Fake up an internal ccache */
895 kcm_internal_ccache(context, ccache, &ccdata);
897 in.client = ccache->client;
898 in.server = server;
899 in.times.endtime = 0;
901 /* glue cc layer will store creds */
902 ret = krb5_get_credentials_with_flags(context, 0, flags,
903 &ccdata, &in, &out);
905 HEIMDAL_MUTEX_unlock(&ccache->mutex);
907 krb5_free_principal(context, server);
909 if (ret == 0)
910 krb5_free_cred_contents(context, out);
912 kcm_release_ccache(context, ccache);
913 free(name);
915 return ret;
919 * Request:
920 * OldNameZ
921 * NewNameZ
923 * Repsonse:
926 static krb5_error_code
927 kcm_op_move_cache(krb5_context context,
928 kcm_client *client,
929 kcm_operation opcode,
930 krb5_storage *request,
931 krb5_storage *response)
933 krb5_error_code ret;
934 kcm_ccache oldid, newid;
935 char *oldname, *newname;
937 ret = krb5_ret_stringz(request, &oldname);
938 if (ret)
939 return ret;
941 KCM_LOG_REQUEST_NAME(context, client, opcode, oldname);
943 ret = krb5_ret_stringz(request, &newname);
944 if (ret) {
945 free(oldname);
946 return ret;
949 /* move to ourself is simple, done! */
950 if (strcmp(oldname, newname) == 0) {
951 free(oldname);
952 free(newname);
953 return 0;
956 ret = kcm_ccache_resolve_client(context, client, opcode, oldname, &oldid);
957 if (ret) {
958 free(oldname);
959 free(newname);
960 return ret;
963 /* Check if new credential cache exists, if not create one. */
964 ret = kcm_ccache_resolve_client(context, client, opcode, newname, &newid);
965 if (ret == KRB5_FCC_NOFILE)
966 ret = kcm_ccache_new_client(context, client, newname, &newid);
967 free(newname);
969 if (ret) {
970 free(oldname);
971 kcm_release_ccache(context, oldid);
972 return ret;
975 HEIMDAL_MUTEX_lock(&oldid->mutex);
976 HEIMDAL_MUTEX_lock(&newid->mutex);
978 /* move content */
980 kcm_ccache_data tmp;
982 #define MOVE(n,o,f) { tmp.f = n->f ; n->f = o->f; o->f = tmp.f; }
984 MOVE(newid, oldid, flags);
985 MOVE(newid, oldid, client);
986 MOVE(newid, oldid, server);
987 MOVE(newid, oldid, creds);
988 MOVE(newid, oldid, tkt_life);
989 MOVE(newid, oldid, renew_life);
990 MOVE(newid, oldid, key);
991 MOVE(newid, oldid, kdc_offset);
992 #undef MOVE
995 HEIMDAL_MUTEX_unlock(&oldid->mutex);
996 HEIMDAL_MUTEX_unlock(&newid->mutex);
998 kcm_release_ccache(context, oldid);
999 kcm_release_ccache(context, newid);
1001 ret = kcm_ccache_destroy_client(context, client, oldname);
1002 if (ret == 0)
1003 kcm_drop_default_cache(context, client, oldname);
1005 free(oldname);
1007 return ret;
1010 static krb5_error_code
1011 kcm_op_get_cache_uuid_list(krb5_context context,
1012 kcm_client *client,
1013 kcm_operation opcode,
1014 krb5_storage *request,
1015 krb5_storage *response)
1017 KCM_LOG_REQUEST(context, client, opcode);
1019 return kcm_ccache_get_uuids(context, client, opcode, response);
1022 static krb5_error_code
1023 kcm_op_get_cache_by_uuid(krb5_context context,
1024 kcm_client *client,
1025 kcm_operation opcode,
1026 krb5_storage *request,
1027 krb5_storage *response)
1029 krb5_error_code ret;
1030 kcmuuid_t uuid;
1031 ssize_t sret;
1032 kcm_ccache cache;
1034 KCM_LOG_REQUEST(context, client, opcode);
1036 sret = krb5_storage_read(request, &uuid, sizeof(uuid));
1037 if (sret != sizeof(uuid)) {
1038 krb5_clear_error_message(context);
1039 return KRB5_CC_IO;
1042 ret = kcm_ccache_resolve_by_uuid(context, uuid, &cache);
1043 if (ret)
1044 return ret;
1046 ret = kcm_access(context, client, opcode, cache);
1047 if (ret)
1048 ret = KRB5_FCC_NOFILE;
1050 if (ret == 0)
1051 ret = krb5_store_stringz(response, cache->name);
1053 kcm_release_ccache(context, cache);
1055 return ret;
1058 struct kcm_default_cache *default_caches;
1060 static krb5_error_code
1061 kcm_op_get_default_cache(krb5_context context,
1062 kcm_client *client,
1063 kcm_operation opcode,
1064 krb5_storage *request,
1065 krb5_storage *response)
1067 struct kcm_default_cache *c;
1068 krb5_error_code ret;
1069 const char *name = NULL;
1070 char *n = NULL;
1071 int aret;
1073 KCM_LOG_REQUEST(context, client, opcode);
1075 for (c = default_caches; c != NULL; c = c->next) {
1076 if (kcm_is_same_session(client, c->uid, c->session)) {
1077 name = c->name;
1078 break;
1081 if (name == NULL)
1082 name = n = kcm_ccache_first_name(client);
1084 if (name == NULL) {
1085 aret = asprintf(&n, "%d", (int)client->uid);
1086 if (aret != -1)
1087 name = n;
1089 if (name == NULL)
1090 return ENOMEM;
1091 ret = krb5_store_stringz(response, name);
1092 if (n)
1093 free(n);
1094 return ret;
1097 static void
1098 kcm_drop_default_cache(krb5_context context, kcm_client *client, char *name)
1100 struct kcm_default_cache **c;
1102 for (c = &default_caches; *c != NULL; c = &(*c)->next) {
1103 if (!kcm_is_same_session(client, (*c)->uid, (*c)->session))
1104 continue;
1105 if (strcmp((*c)->name, name) == 0) {
1106 struct kcm_default_cache *h = *c;
1107 *c = (*c)->next;
1108 free(h->name);
1109 free(h);
1110 break;
1115 static krb5_error_code
1116 kcm_op_set_default_cache(krb5_context context,
1117 kcm_client *client,
1118 kcm_operation opcode,
1119 krb5_storage *request,
1120 krb5_storage *response)
1122 struct kcm_default_cache *c;
1123 krb5_error_code ret;
1124 char *name;
1126 ret = krb5_ret_stringz(request, &name);
1127 if (ret)
1128 return ret;
1130 KCM_LOG_REQUEST_NAME(context, client, opcode, name);
1132 for (c = default_caches; c != NULL; c = c->next) {
1133 if (kcm_is_same_session(client, c->uid, c->session))
1134 break;
1136 if (c == NULL) {
1137 c = malloc(sizeof(*c));
1138 if (c == NULL) {
1139 free(name);
1140 return ENOMEM;
1142 c->session = client->session;
1143 c->uid = client->uid;
1144 c->name = name;
1146 c->next = default_caches;
1147 default_caches = c;
1148 } else {
1149 free(c->name);
1150 c->name = 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 flags |= NTLM_FLAG_SESSIONKEY;
1539 #if 0
1540 } else {
1541 struct ntlm_buf sessionkey;
1542 unsigned char ntlmv2[16];
1543 struct ntlm_targetinfo ti;
1545 /* verify infotarget */
1547 ret = heim_ntlm_decode_targetinfo(&type2.targetinfo, 1, &ti);
1548 if(ret) {
1549 _gss_ntlm_delete_sec_context(minor_status,
1550 context_handle, NULL);
1551 *minor_status = ret;
1552 return GSS_S_FAILURE;
1555 if (ti.domainname && strcmp(ti.domainname, name->domain) != 0) {
1556 _gss_ntlm_delete_sec_context(minor_status,
1557 context_handle, NULL);
1558 *minor_status = EINVAL;
1559 return GSS_S_FAILURE;
1562 ret = heim_ntlm_calculate_ntlm2(ctx->client->key.data,
1563 ctx->client->key.length,
1564 type3.username,
1565 name->domain,
1566 type2.challenge,
1567 &type2.targetinfo,
1568 ntlmv2,
1569 &type3.ntlm);
1570 if (ret) {
1571 _gss_ntlm_delete_sec_context(minor_status,
1572 context_handle, NULL);
1573 *minor_status = ret;
1574 return GSS_S_FAILURE;
1577 ret = heim_ntlm_build_ntlm1_master(ntlmv2, sizeof(ntlmv2),
1578 &sessionkey,
1579 &type3.sessionkey);
1580 memset(ntlmv2, 0, sizeof(ntlmv2));
1581 if (ret) {
1582 _gss_ntlm_delete_sec_context(minor_status,
1583 context_handle, NULL);
1584 *minor_status = ret;
1585 return GSS_S_FAILURE;
1588 flags |= NTLM_FLAG_NTLM2_SESSION |
1589 NTLM_FLAG_SESSION;
1591 if (type3.flags & NTLM_NEG_KEYEX)
1592 flags |= NTLM_FLAG_KEYEX;
1594 ret = krb5_data_copy(&ctx->sessionkey,
1595 sessionkey.data, sessionkey.length);
1596 free(sessionkey.data);
1597 if (ret) {
1598 _gss_ntlm_delete_sec_context(minor_status,
1599 context_handle, NULL);
1600 *minor_status = ret;
1601 return GSS_S_FAILURE;
1603 #endif
1606 #if 0
1607 if (flags & NTLM_FLAG_NTLM2_SESSION) {
1608 _gss_ntlm_set_key(&ctx->u.v2.send, 0, (ctx->flags & NTLM_NEG_KEYEX),
1609 ctx->sessionkey.data,
1610 ctx->sessionkey.length);
1611 _gss_ntlm_set_key(&ctx->u.v2.recv, 1, (ctx->flags & NTLM_NEG_KEYEX),
1612 ctx->sessionkey.data,
1613 ctx->sessionkey.length);
1614 } else {
1615 flags |= NTLM_FLAG_SESSION;
1616 RC4_set_key(&ctx->u.v1.crypto_recv.key,
1617 ctx->sessionkey.length,
1618 ctx->sessionkey.data);
1619 RC4_set_key(&ctx->u.v1.crypto_send.key,
1620 ctx->sessionkey.length,
1621 ctx->sessionkey.data);
1623 #endif
1625 ret = heim_ntlm_encode_type3(&type3, &ndata, NULL);
1626 if (ret)
1627 goto error;
1629 data.data = ndata.data;
1630 data.length = ndata.length;
1631 ret = krb5_store_data(response, data);
1632 heim_ntlm_free_buf(&ndata);
1633 if (ret) goto error;
1635 ret = krb5_store_int32(response, flags);
1636 if (ret) goto error;
1638 data.data = sessionkey.data;
1639 data.length = sessionkey.length;
1641 ret = krb5_store_data(response, data);
1642 if (ret) goto error;
1644 error:
1645 free(type3.username);
1646 heim_ntlm_free_type2(&type2);
1647 free(user);
1648 if (domain)
1649 free(domain);
1651 return ret;
1656 * { "GET_NTLM_UUID_LIST", NULL }
1658 * reply:
1659 * 1 user domain
1660 * 0 [ end of list ]
1663 static krb5_error_code
1664 kcm_op_get_ntlm_user_list(krb5_context context,
1665 kcm_client *client,
1666 kcm_operation opcode,
1667 krb5_storage *request,
1668 krb5_storage *response)
1670 struct kcm_ntlm_cred *c;
1671 krb5_error_code ret;
1673 for (c = ntlm_head; c != NULL; c = c->next) {
1674 if (!kcm_is_same_session(client, c->uid, c->session))
1675 continue;
1677 ret = krb5_store_uint32(response, 1);
1678 if (ret)
1679 return ret;
1680 ret = krb5_store_stringz(response, c->user);
1681 if (ret)
1682 return ret;
1683 ret = krb5_store_stringz(response, c->domain);
1684 if (ret)
1685 return ret;
1687 return krb5_store_uint32(response, 0);
1694 static struct kcm_op kcm_ops[] = {
1695 { "NOOP", kcm_op_noop },
1696 { "GET_NAME", kcm_op_get_name },
1697 { "RESOLVE", kcm_op_noop },
1698 { "GEN_NEW", kcm_op_gen_new },
1699 { "INITIALIZE", kcm_op_initialize },
1700 { "DESTROY", kcm_op_destroy },
1701 { "STORE", kcm_op_store },
1702 { "RETRIEVE", kcm_op_retrieve },
1703 { "GET_PRINCIPAL", kcm_op_get_principal },
1704 { "GET_CRED_UUID_LIST", kcm_op_get_cred_uuid_list },
1705 { "GET_CRED_BY_UUID", kcm_op_get_cred_by_uuid },
1706 { "REMOVE_CRED", kcm_op_remove_cred },
1707 { "SET_FLAGS", kcm_op_set_flags },
1708 { "CHOWN", kcm_op_chown },
1709 { "CHMOD", kcm_op_chmod },
1710 { "GET_INITIAL_TICKET", kcm_op_get_initial_ticket },
1711 { "GET_TICKET", kcm_op_get_ticket },
1712 { "MOVE_CACHE", kcm_op_move_cache },
1713 { "GET_CACHE_UUID_LIST", kcm_op_get_cache_uuid_list },
1714 { "GET_CACHE_BY_UUID", kcm_op_get_cache_by_uuid },
1715 { "GET_DEFAULT_CACHE", kcm_op_get_default_cache },
1716 { "SET_DEFAULT_CACHE", kcm_op_set_default_cache },
1717 { "GET_KDC_OFFSET", kcm_op_get_kdc_offset },
1718 { "SET_KDC_OFFSET", kcm_op_set_kdc_offset },
1719 { "ADD_NTLM_CRED", kcm_op_add_ntlm_cred },
1720 { "HAVE_USER_CRED", kcm_op_have_ntlm_cred },
1721 { "DEL_NTLM_CRED", kcm_op_del_ntlm_cred },
1722 { "DO_NTLM_AUTH", kcm_op_do_ntlm },
1723 { "GET_NTLM_USER_LIST", kcm_op_get_ntlm_user_list }
1727 const char *
1728 kcm_op2string(kcm_operation opcode)
1730 if (opcode >= sizeof(kcm_ops)/sizeof(kcm_ops[0]))
1731 return "Unknown operation";
1733 return kcm_ops[opcode].name;
1736 krb5_error_code
1737 kcm_dispatch(krb5_context context,
1738 kcm_client *client,
1739 krb5_data *req_data,
1740 krb5_data *resp_data)
1742 krb5_error_code ret;
1743 kcm_method method;
1744 krb5_storage *req_sp = NULL;
1745 krb5_storage *resp_sp = NULL;
1746 uint16_t opcode;
1748 krb5_data_zero(resp_data);
1749 resp_sp = krb5_storage_emem();
1750 if (resp_sp == NULL) {
1751 return ENOMEM;
1754 if (client->pid == -1) {
1755 kcm_log(0, "Client had invalid process number");
1756 ret = KRB5_FCC_INTERNAL;
1757 goto out;
1760 req_sp = krb5_storage_from_data(req_data);
1761 if (req_sp == NULL) {
1762 kcm_log(0, "Process %d: failed to initialize storage from data",
1763 client->pid);
1764 ret = KRB5_CC_IO;
1765 goto out;
1768 ret = krb5_ret_uint16(req_sp, &opcode);
1769 if (ret) {
1770 kcm_log(0, "Process %d: didn't send a message", client->pid);
1771 goto out;
1774 if (opcode >= sizeof(kcm_ops)/sizeof(kcm_ops[0])) {
1775 kcm_log(0, "Process %d: invalid operation code %d",
1776 client->pid, opcode);
1777 ret = KRB5_FCC_INTERNAL;
1778 goto out;
1780 method = kcm_ops[opcode].method;
1781 if (method == NULL) {
1782 kcm_log(0, "Process %d: operation code %s not implemented",
1783 client->pid, kcm_op2string(opcode));
1784 ret = KRB5_FCC_INTERNAL;
1785 goto out;
1788 /* seek past place for status code */
1789 krb5_storage_seek(resp_sp, 4, SEEK_SET);
1791 ret = (*method)(context, client, opcode, req_sp, resp_sp);
1793 out:
1794 if (req_sp != NULL) {
1795 krb5_storage_free(req_sp);
1798 if (resp_sp) {
1799 krb5_error_code ret2;
1801 krb5_storage_seek(resp_sp, 0, SEEK_SET);
1802 ret2 = krb5_store_int32(resp_sp, ret);
1803 if (ret2 == 0)
1804 ret2 = krb5_storage_to_data(resp_sp, resp_data);
1805 krb5_storage_free(resp_sp);
1806 if (ret2)
1807 ret = ret2;
1810 return ret;