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