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