1178825Sdfr/*
2178825Sdfr * Copyright (c) 2005, PADL Software Pty Ltd.
3178825Sdfr * All rights reserved.
4178825Sdfr *
5233294Sstas * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
6233294Sstas *
7178825Sdfr * Redistribution and use in source and binary forms, with or without
8178825Sdfr * modification, are permitted provided that the following conditions
9178825Sdfr * are met:
10178825Sdfr *
11178825Sdfr * 1. Redistributions of source code must retain the above copyright
12178825Sdfr *    notice, this list of conditions and the following disclaimer.
13178825Sdfr *
14178825Sdfr * 2. Redistributions in binary form must reproduce the above copyright
15178825Sdfr *    notice, this list of conditions and the following disclaimer in the
16178825Sdfr *    documentation and/or other materials provided with the distribution.
17178825Sdfr *
18178825Sdfr * 3. Neither the name of PADL Software nor the names of its contributors
19178825Sdfr *    may be used to endorse or promote products derived from this software
20178825Sdfr *    without specific prior written permission.
21178825Sdfr *
22178825Sdfr * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
23178825Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24178825Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25178825Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
26178825Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27178825Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28178825Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29178825Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30178825Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31178825Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32178825Sdfr * SUCH DAMAGE.
33178825Sdfr */
34178825Sdfr
35178825Sdfr#include "kcm_locl.h"
36233294Sstas#include <heimntlm.h>
37178825Sdfr
38233294Sstasstatic void
39233294Sstaskcm_drop_default_cache(krb5_context context, kcm_client *client, char *name);
40178825Sdfr
41233294Sstas
42233294Sstasint
43233294Sstaskcm_is_same_session(kcm_client *client, uid_t uid, pid_t session)
44233294Sstas{
45233294Sstas#if 0 /* XXX pppd is running in diffrent session the user */
46233294Sstas    if (session != -1)
47233294Sstas	return (client->session == session);
48233294Sstas    else
49233294Sstas#endif
50233294Sstas	return  (client->uid == uid);
51233294Sstas}
52233294Sstas
53178825Sdfrstatic krb5_error_code
54178825Sdfrkcm_op_noop(krb5_context context,
55178825Sdfr	    kcm_client *client,
56178825Sdfr	    kcm_operation opcode,
57178825Sdfr	    krb5_storage *request,
58178825Sdfr	    krb5_storage *response)
59178825Sdfr{
60178825Sdfr    KCM_LOG_REQUEST(context, client, opcode);
61178825Sdfr
62233294Sstas    return 0;
63178825Sdfr}
64178825Sdfr
65178825Sdfr/*
66178825Sdfr * Request:
67178825Sdfr *	NameZ
68178825Sdfr * Response:
69178825Sdfr *	NameZ
70178825Sdfr *
71178825Sdfr */
72178825Sdfrstatic krb5_error_code
73178825Sdfrkcm_op_get_name(krb5_context context,
74178825Sdfr		kcm_client *client,
75178825Sdfr		kcm_operation opcode,
76178825Sdfr		krb5_storage *request,
77178825Sdfr		krb5_storage *response)
78178825Sdfr
79178825Sdfr{
80178825Sdfr    krb5_error_code ret;
81178825Sdfr    char *name = NULL;
82178825Sdfr    kcm_ccache ccache;
83178825Sdfr
84178825Sdfr    ret = krb5_ret_stringz(request, &name);
85178825Sdfr    if (ret)
86178825Sdfr	return ret;
87178825Sdfr
88178825Sdfr    KCM_LOG_REQUEST_NAME(context, client, opcode, name);
89178825Sdfr
90178825Sdfr    ret = kcm_ccache_resolve_client(context, client, opcode,
91178825Sdfr				    name, &ccache);
92178825Sdfr    if (ret) {
93178825Sdfr	free(name);
94178825Sdfr	return ret;
95178825Sdfr    }
96178825Sdfr
97178825Sdfr    ret = krb5_store_stringz(response, ccache->name);
98178825Sdfr    if (ret) {
99233294Sstas	kcm_release_ccache(context, ccache);
100178825Sdfr	free(name);
101178825Sdfr	return ret;
102178825Sdfr    }
103178825Sdfr
104178825Sdfr    free(name);
105233294Sstas    kcm_release_ccache(context, ccache);
106178825Sdfr    return 0;
107178825Sdfr}
108178825Sdfr
109178825Sdfr/*
110178825Sdfr * Request:
111233294Sstas *
112178825Sdfr * Response:
113178825Sdfr *	NameZ
114178825Sdfr */
115178825Sdfrstatic krb5_error_code
116178825Sdfrkcm_op_gen_new(krb5_context context,
117178825Sdfr	       kcm_client *client,
118178825Sdfr	       kcm_operation opcode,
119178825Sdfr	       krb5_storage *request,
120178825Sdfr	       krb5_storage *response)
121178825Sdfr{
122178825Sdfr    krb5_error_code ret;
123178825Sdfr    char *name;
124178825Sdfr
125178825Sdfr    KCM_LOG_REQUEST(context, client, opcode);
126178825Sdfr
127178825Sdfr    name = kcm_ccache_nextid(client->pid, client->uid, client->gid);
128178825Sdfr    if (name == NULL) {
129178825Sdfr	return KRB5_CC_NOMEM;
130178825Sdfr    }
131178825Sdfr
132178825Sdfr    ret = krb5_store_stringz(response, name);
133178825Sdfr    free(name);
134178825Sdfr
135178825Sdfr    return ret;
136178825Sdfr}
137178825Sdfr
138178825Sdfr/*
139178825Sdfr * Request:
140178825Sdfr *	NameZ
141178825Sdfr *	Principal
142233294Sstas *
143178825Sdfr * Response:
144233294Sstas *
145178825Sdfr */
146178825Sdfrstatic krb5_error_code
147178825Sdfrkcm_op_initialize(krb5_context context,
148178825Sdfr		  kcm_client *client,
149178825Sdfr		  kcm_operation opcode,
150178825Sdfr		  krb5_storage *request,
151178825Sdfr		  krb5_storage *response)
152178825Sdfr{
153178825Sdfr    kcm_ccache ccache;
154178825Sdfr    krb5_principal principal;
155178825Sdfr    krb5_error_code ret;
156178825Sdfr    char *name;
157178825Sdfr#if 0
158178825Sdfr    kcm_event event;
159178825Sdfr#endif
160178825Sdfr
161178825Sdfr    KCM_LOG_REQUEST(context, client, opcode);
162178825Sdfr
163178825Sdfr    ret = krb5_ret_stringz(request, &name);
164178825Sdfr    if (ret)
165178825Sdfr	return ret;
166178825Sdfr
167178825Sdfr    ret = krb5_ret_principal(request, &principal);
168178825Sdfr    if (ret) {
169178825Sdfr	free(name);
170178825Sdfr	return ret;
171178825Sdfr    }
172178825Sdfr
173178825Sdfr    ret = kcm_ccache_new_client(context, client, name, &ccache);
174178825Sdfr    if (ret) {
175178825Sdfr	free(name);
176178825Sdfr	krb5_free_principal(context, principal);
177178825Sdfr	return ret;
178178825Sdfr    }
179178825Sdfr
180178825Sdfr    ccache->client = principal;
181178825Sdfr
182178825Sdfr    free(name);
183178825Sdfr
184178825Sdfr#if 0
185178825Sdfr    /*
186178825Sdfr     * Create a new credentials cache. To mitigate DoS attacks we will
187178825Sdfr     * expire it in 30 minutes unless it has some credentials added
188178825Sdfr     * to it
189178825Sdfr     */
190178825Sdfr
191178825Sdfr    event.fire_time = 30 * 60;
192178825Sdfr    event.expire_time = 0;
193178825Sdfr    event.backoff_time = 0;
194178825Sdfr    event.action = KCM_EVENT_DESTROY_EMPTY_CACHE;
195178825Sdfr    event.ccache = ccache;
196178825Sdfr
197178825Sdfr    ret = kcm_enqueue_event_relative(context, &event);
198178825Sdfr#endif
199178825Sdfr
200233294Sstas    kcm_release_ccache(context, ccache);
201178825Sdfr
202178825Sdfr    return ret;
203178825Sdfr}
204178825Sdfr
205178825Sdfr/*
206178825Sdfr * Request:
207178825Sdfr *	NameZ
208233294Sstas *
209178825Sdfr * Response:
210233294Sstas *
211178825Sdfr */
212178825Sdfrstatic krb5_error_code
213178825Sdfrkcm_op_destroy(krb5_context context,
214178825Sdfr	       kcm_client *client,
215178825Sdfr	       kcm_operation opcode,
216178825Sdfr	       krb5_storage *request,
217178825Sdfr	       krb5_storage *response)
218178825Sdfr{
219178825Sdfr    krb5_error_code ret;
220178825Sdfr    char *name;
221178825Sdfr
222178825Sdfr    ret = krb5_ret_stringz(request, &name);
223178825Sdfr    if (ret)
224178825Sdfr	return ret;
225178825Sdfr
226178825Sdfr    KCM_LOG_REQUEST_NAME(context, client, opcode, name);
227178825Sdfr
228178825Sdfr    ret = kcm_ccache_destroy_client(context, client, name);
229233294Sstas    if (ret == 0)
230233294Sstas	kcm_drop_default_cache(context, client, name);
231178825Sdfr
232178825Sdfr    free(name);
233178825Sdfr
234178825Sdfr    return ret;
235178825Sdfr}
236178825Sdfr
237178825Sdfr/*
238178825Sdfr * Request:
239178825Sdfr *	NameZ
240178825Sdfr *	Creds
241233294Sstas *
242178825Sdfr * Response:
243233294Sstas *
244178825Sdfr */
245178825Sdfrstatic krb5_error_code
246178825Sdfrkcm_op_store(krb5_context context,
247178825Sdfr	     kcm_client *client,
248178825Sdfr	     kcm_operation opcode,
249178825Sdfr	     krb5_storage *request,
250178825Sdfr	     krb5_storage *response)
251178825Sdfr{
252178825Sdfr    krb5_creds creds;
253178825Sdfr    krb5_error_code ret;
254178825Sdfr    kcm_ccache ccache;
255178825Sdfr    char *name;
256178825Sdfr
257178825Sdfr    ret = krb5_ret_stringz(request, &name);
258178825Sdfr    if (ret)
259178825Sdfr	return ret;
260178825Sdfr
261178825Sdfr    KCM_LOG_REQUEST_NAME(context, client, opcode, name);
262178825Sdfr
263178825Sdfr    ret = krb5_ret_creds(request, &creds);
264178825Sdfr    if (ret) {
265178825Sdfr	free(name);
266178825Sdfr	return ret;
267178825Sdfr    }
268178825Sdfr
269178825Sdfr    ret = kcm_ccache_resolve_client(context, client, opcode,
270178825Sdfr				    name, &ccache);
271178825Sdfr    if (ret) {
272178825Sdfr	free(name);
273178825Sdfr	krb5_free_cred_contents(context, &creds);
274178825Sdfr	return ret;
275178825Sdfr    }
276178825Sdfr
277178825Sdfr    ret = kcm_ccache_store_cred(context, ccache, &creds, 0);
278178825Sdfr    if (ret) {
279178825Sdfr	free(name);
280178825Sdfr	krb5_free_cred_contents(context, &creds);
281233294Sstas	kcm_release_ccache(context, ccache);
282178825Sdfr	return ret;
283178825Sdfr    }
284178825Sdfr
285178825Sdfr    kcm_ccache_enqueue_default(context, ccache, &creds);
286178825Sdfr
287178825Sdfr    free(name);
288233294Sstas    kcm_release_ccache(context, ccache);
289178825Sdfr
290178825Sdfr    return 0;
291178825Sdfr}
292178825Sdfr
293178825Sdfr/*
294178825Sdfr * Request:
295178825Sdfr *	NameZ
296178825Sdfr *	WhichFields
297178825Sdfr *	MatchCreds
298178825Sdfr *
299178825Sdfr * Response:
300178825Sdfr *	Creds
301233294Sstas *
302178825Sdfr */
303178825Sdfrstatic krb5_error_code
304178825Sdfrkcm_op_retrieve(krb5_context context,
305178825Sdfr		kcm_client *client,
306178825Sdfr		kcm_operation opcode,
307178825Sdfr		krb5_storage *request,
308178825Sdfr		krb5_storage *response)
309178825Sdfr{
310178825Sdfr    uint32_t flags;
311178825Sdfr    krb5_creds mcreds;
312178825Sdfr    krb5_error_code ret;
313178825Sdfr    kcm_ccache ccache;
314178825Sdfr    char *name;
315178825Sdfr    krb5_creds *credp;
316178825Sdfr    int free_creds = 0;
317178825Sdfr
318178825Sdfr    ret = krb5_ret_stringz(request, &name);
319178825Sdfr    if (ret)
320178825Sdfr	return ret;
321178825Sdfr
322178825Sdfr    KCM_LOG_REQUEST_NAME(context, client, opcode, name);
323178825Sdfr
324178825Sdfr    ret = krb5_ret_uint32(request, &flags);
325178825Sdfr    if (ret) {
326178825Sdfr	free(name);
327178825Sdfr	return ret;
328178825Sdfr    }
329178825Sdfr
330178825Sdfr    ret = krb5_ret_creds_tag(request, &mcreds);
331178825Sdfr    if (ret) {
332178825Sdfr	free(name);
333178825Sdfr	return ret;
334178825Sdfr    }
335178825Sdfr
336178825Sdfr    if (disallow_getting_krbtgt &&
337178825Sdfr	mcreds.server->name.name_string.len == 2 &&
338178825Sdfr	strcmp(mcreds.server->name.name_string.val[0], KRB5_TGS_NAME) == 0)
339178825Sdfr    {
340178825Sdfr	free(name);
341178825Sdfr	krb5_free_cred_contents(context, &mcreds);
342178825Sdfr	return KRB5_FCC_PERM;
343178825Sdfr    }
344178825Sdfr
345178825Sdfr    ret = kcm_ccache_resolve_client(context, client, opcode,
346178825Sdfr				    name, &ccache);
347178825Sdfr    if (ret) {
348178825Sdfr	free(name);
349178825Sdfr	krb5_free_cred_contents(context, &mcreds);
350178825Sdfr	return ret;
351178825Sdfr    }
352178825Sdfr
353178825Sdfr    ret = kcm_ccache_retrieve_cred(context, ccache, flags,
354178825Sdfr				   &mcreds, &credp);
355233294Sstas    if (ret && ((flags & KRB5_GC_CACHED) == 0) &&
356233294Sstas	!krb5_is_config_principal(context, mcreds.server)) {
357178825Sdfr	krb5_ccache_data ccdata;
358178825Sdfr
359178825Sdfr	/* try and acquire */
360178825Sdfr	HEIMDAL_MUTEX_lock(&ccache->mutex);
361178825Sdfr
362178825Sdfr	/* Fake up an internal ccache */
363178825Sdfr	kcm_internal_ccache(context, ccache, &ccdata);
364178825Sdfr
365178825Sdfr	/* glue cc layer will store creds */
366178825Sdfr	ret = krb5_get_credentials(context, 0, &ccdata, &mcreds, &credp);
367178825Sdfr	if (ret == 0)
368178825Sdfr	    free_creds = 1;
369178825Sdfr
370178825Sdfr	HEIMDAL_MUTEX_unlock(&ccache->mutex);
371178825Sdfr    }
372178825Sdfr
373178825Sdfr    if (ret == 0) {
374178825Sdfr	ret = krb5_store_creds(response, credp);
375178825Sdfr    }
376178825Sdfr
377178825Sdfr    free(name);
378178825Sdfr    krb5_free_cred_contents(context, &mcreds);
379233294Sstas    kcm_release_ccache(context, ccache);
380178825Sdfr
381178825Sdfr    if (free_creds)
382178825Sdfr	krb5_free_cred_contents(context, credp);
383178825Sdfr
384178825Sdfr    return ret;
385178825Sdfr}
386178825Sdfr
387178825Sdfr/*
388178825Sdfr * Request:
389178825Sdfr *	NameZ
390178825Sdfr *
391178825Sdfr * Response:
392178825Sdfr *	Principal
393178825Sdfr */
394178825Sdfrstatic krb5_error_code
395178825Sdfrkcm_op_get_principal(krb5_context context,
396178825Sdfr		     kcm_client *client,
397178825Sdfr		     kcm_operation opcode,
398178825Sdfr		     krb5_storage *request,
399178825Sdfr		     krb5_storage *response)
400178825Sdfr{
401178825Sdfr    krb5_error_code ret;
402178825Sdfr    kcm_ccache ccache;
403178825Sdfr    char *name;
404178825Sdfr
405178825Sdfr    ret = krb5_ret_stringz(request, &name);
406178825Sdfr    if (ret)
407178825Sdfr	return ret;
408178825Sdfr
409178825Sdfr    KCM_LOG_REQUEST_NAME(context, client, opcode, name);
410178825Sdfr
411178825Sdfr    ret = kcm_ccache_resolve_client(context, client, opcode,
412178825Sdfr				    name, &ccache);
413178825Sdfr    if (ret) {
414178825Sdfr	free(name);
415178825Sdfr	return ret;
416178825Sdfr    }
417178825Sdfr
418178825Sdfr    if (ccache->client == NULL)
419178825Sdfr	ret = KRB5_CC_NOTFOUND;
420178825Sdfr    else
421178825Sdfr	ret = krb5_store_principal(response, ccache->client);
422178825Sdfr
423178825Sdfr    free(name);
424233294Sstas    kcm_release_ccache(context, ccache);
425178825Sdfr
426178825Sdfr    return 0;
427178825Sdfr}
428178825Sdfr
429178825Sdfr/*
430178825Sdfr * Request:
431178825Sdfr *	NameZ
432178825Sdfr *
433178825Sdfr * Response:
434233294Sstas *	UUIDs
435233294Sstas *
436178825Sdfr */
437178825Sdfrstatic krb5_error_code
438233294Sstaskcm_op_get_cred_uuid_list(krb5_context context,
439233294Sstas			  kcm_client *client,
440233294Sstas			  kcm_operation opcode,
441233294Sstas			  krb5_storage *request,
442233294Sstas			  krb5_storage *response)
443178825Sdfr{
444233294Sstas    struct kcm_creds *creds;
445178825Sdfr    krb5_error_code ret;
446178825Sdfr    kcm_ccache ccache;
447178825Sdfr    char *name;
448178825Sdfr
449178825Sdfr    ret = krb5_ret_stringz(request, &name);
450178825Sdfr    if (ret)
451178825Sdfr	return ret;
452178825Sdfr
453178825Sdfr    KCM_LOG_REQUEST_NAME(context, client, opcode, name);
454178825Sdfr
455178825Sdfr    ret = kcm_ccache_resolve_client(context, client, opcode,
456178825Sdfr				    name, &ccache);
457233294Sstas    free(name);
458233294Sstas    if (ret)
459178825Sdfr	return ret;
460178825Sdfr
461233294Sstas    for (creds = ccache->creds ; creds ; creds = creds->next) {
462233294Sstas	ssize_t sret;
463233294Sstas	sret = krb5_storage_write(response, &creds->uuid, sizeof(creds->uuid));
464233294Sstas	if (sret != sizeof(creds->uuid)) {
465233294Sstas	    ret = ENOMEM;
466233294Sstas	    break;
467233294Sstas	}
468178825Sdfr    }
469178825Sdfr
470233294Sstas    kcm_release_ccache(context, ccache);
471178825Sdfr
472178825Sdfr    return ret;
473178825Sdfr}
474178825Sdfr
475178825Sdfr/*
476178825Sdfr * Request:
477178825Sdfr *	NameZ
478178825Sdfr *	Cursor
479178825Sdfr *
480178825Sdfr * Response:
481178825Sdfr *	Creds
482178825Sdfr */
483178825Sdfrstatic krb5_error_code
484233294Sstaskcm_op_get_cred_by_uuid(krb5_context context,
485233294Sstas			kcm_client *client,
486233294Sstas			kcm_operation opcode,
487233294Sstas			krb5_storage *request,
488233294Sstas			krb5_storage *response)
489178825Sdfr{
490178825Sdfr    krb5_error_code ret;
491178825Sdfr    kcm_ccache ccache;
492178825Sdfr    char *name;
493233294Sstas    struct kcm_creds *c;
494233294Sstas    kcmuuid_t uuid;
495233294Sstas    ssize_t sret;
496178825Sdfr
497178825Sdfr    ret = krb5_ret_stringz(request, &name);
498178825Sdfr    if (ret)
499178825Sdfr	return ret;
500178825Sdfr
501178825Sdfr    KCM_LOG_REQUEST_NAME(context, client, opcode, name);
502178825Sdfr
503178825Sdfr    ret = kcm_ccache_resolve_client(context, client, opcode,
504178825Sdfr				    name, &ccache);
505178825Sdfr    free(name);
506178825Sdfr    if (ret)
507178825Sdfr	return ret;
508178825Sdfr
509233294Sstas    sret = krb5_storage_read(request, &uuid, sizeof(uuid));
510233294Sstas    if (sret != sizeof(uuid)) {
511233294Sstas	kcm_release_ccache(context, ccache);
512233294Sstas	krb5_clear_error_message(context);
513233294Sstas	return KRB5_CC_IO;
514178825Sdfr    }
515178825Sdfr
516233294Sstas    c = kcm_ccache_find_cred_uuid(context, ccache, uuid);
517233294Sstas    if (c == NULL) {
518233294Sstas	kcm_release_ccache(context, ccache);
519233294Sstas	return KRB5_CC_END;
520178825Sdfr    }
521178825Sdfr
522233294Sstas    HEIMDAL_MUTEX_lock(&ccache->mutex);
523233294Sstas    ret = krb5_store_creds(response, &c->cred);
524233294Sstas    HEIMDAL_MUTEX_unlock(&ccache->mutex);
525178825Sdfr
526233294Sstas    kcm_release_ccache(context, ccache);
527178825Sdfr
528178825Sdfr    return ret;
529178825Sdfr}
530178825Sdfr
531178825Sdfr/*
532178825Sdfr * Request:
533178825Sdfr *	NameZ
534178825Sdfr *	WhichFields
535178825Sdfr *	MatchCreds
536178825Sdfr *
537178825Sdfr * Response:
538233294Sstas *
539178825Sdfr */
540178825Sdfrstatic krb5_error_code
541178825Sdfrkcm_op_remove_cred(krb5_context context,
542178825Sdfr		   kcm_client *client,
543178825Sdfr		   kcm_operation opcode,
544178825Sdfr		   krb5_storage *request,
545178825Sdfr		   krb5_storage *response)
546178825Sdfr{
547178825Sdfr    uint32_t whichfields;
548178825Sdfr    krb5_creds mcreds;
549178825Sdfr    krb5_error_code ret;
550178825Sdfr    kcm_ccache ccache;
551178825Sdfr    char *name;
552178825Sdfr
553178825Sdfr    ret = krb5_ret_stringz(request, &name);
554178825Sdfr    if (ret)
555178825Sdfr	return ret;
556178825Sdfr
557178825Sdfr    KCM_LOG_REQUEST_NAME(context, client, opcode, name);
558178825Sdfr
559178825Sdfr    ret = krb5_ret_uint32(request, &whichfields);
560178825Sdfr    if (ret) {
561178825Sdfr	free(name);
562178825Sdfr	return ret;
563178825Sdfr    }
564178825Sdfr
565178825Sdfr    ret = krb5_ret_creds_tag(request, &mcreds);
566178825Sdfr    if (ret) {
567178825Sdfr	free(name);
568178825Sdfr	return ret;
569178825Sdfr    }
570178825Sdfr
571178825Sdfr    ret = kcm_ccache_resolve_client(context, client, opcode,
572178825Sdfr				    name, &ccache);
573178825Sdfr    if (ret) {
574178825Sdfr	free(name);
575178825Sdfr	krb5_free_cred_contents(context, &mcreds);
576178825Sdfr	return ret;
577178825Sdfr    }
578178825Sdfr
579178825Sdfr    ret = kcm_ccache_remove_cred(context, ccache, whichfields, &mcreds);
580178825Sdfr
581178825Sdfr    /* XXX need to remove any events that match */
582178825Sdfr
583178825Sdfr    free(name);
584178825Sdfr    krb5_free_cred_contents(context, &mcreds);
585233294Sstas    kcm_release_ccache(context, ccache);
586178825Sdfr
587178825Sdfr    return ret;
588178825Sdfr}
589178825Sdfr
590178825Sdfr/*
591178825Sdfr * Request:
592178825Sdfr *	NameZ
593178825Sdfr *	Flags
594178825Sdfr *
595178825Sdfr * Response:
596233294Sstas *
597178825Sdfr */
598178825Sdfrstatic krb5_error_code
599178825Sdfrkcm_op_set_flags(krb5_context context,
600178825Sdfr		 kcm_client *client,
601178825Sdfr		 kcm_operation opcode,
602178825Sdfr		 krb5_storage *request,
603178825Sdfr		 krb5_storage *response)
604178825Sdfr{
605178825Sdfr    uint32_t flags;
606178825Sdfr    krb5_error_code ret;
607178825Sdfr    kcm_ccache ccache;
608178825Sdfr    char *name;
609178825Sdfr
610178825Sdfr    ret = krb5_ret_stringz(request, &name);
611178825Sdfr    if (ret)
612178825Sdfr	return ret;
613178825Sdfr
614178825Sdfr    KCM_LOG_REQUEST_NAME(context, client, opcode, name);
615178825Sdfr
616178825Sdfr    ret = krb5_ret_uint32(request, &flags);
617178825Sdfr    if (ret) {
618178825Sdfr	free(name);
619178825Sdfr	return ret;
620178825Sdfr    }
621178825Sdfr
622178825Sdfr    ret = kcm_ccache_resolve_client(context, client, opcode,
623178825Sdfr				    name, &ccache);
624178825Sdfr    if (ret) {
625178825Sdfr	free(name);
626178825Sdfr	return ret;
627178825Sdfr    }
628178825Sdfr
629178825Sdfr    /* we don't really support any flags yet */
630178825Sdfr    free(name);
631233294Sstas    kcm_release_ccache(context, ccache);
632178825Sdfr
633178825Sdfr    return 0;
634178825Sdfr}
635178825Sdfr
636178825Sdfr/*
637178825Sdfr * Request:
638178825Sdfr *	NameZ
639178825Sdfr *	UID
640178825Sdfr *	GID
641178825Sdfr *
642178825Sdfr * Response:
643233294Sstas *
644178825Sdfr */
645178825Sdfrstatic krb5_error_code
646178825Sdfrkcm_op_chown(krb5_context context,
647178825Sdfr	     kcm_client *client,
648178825Sdfr	     kcm_operation opcode,
649178825Sdfr	     krb5_storage *request,
650178825Sdfr	     krb5_storage *response)
651178825Sdfr{
652178825Sdfr    uint32_t uid;
653178825Sdfr    uint32_t gid;
654178825Sdfr    krb5_error_code ret;
655178825Sdfr    kcm_ccache ccache;
656178825Sdfr    char *name;
657178825Sdfr
658178825Sdfr    ret = krb5_ret_stringz(request, &name);
659178825Sdfr    if (ret)
660178825Sdfr	return ret;
661178825Sdfr
662178825Sdfr    KCM_LOG_REQUEST_NAME(context, client, opcode, name);
663178825Sdfr
664178825Sdfr    ret = krb5_ret_uint32(request, &uid);
665178825Sdfr    if (ret) {
666178825Sdfr	free(name);
667178825Sdfr	return ret;
668178825Sdfr    }
669178825Sdfr
670178825Sdfr    ret = krb5_ret_uint32(request, &gid);
671178825Sdfr    if (ret) {
672178825Sdfr	free(name);
673178825Sdfr	return ret;
674178825Sdfr    }
675178825Sdfr
676178825Sdfr    ret = kcm_ccache_resolve_client(context, client, opcode,
677178825Sdfr				    name, &ccache);
678178825Sdfr    if (ret) {
679178825Sdfr	free(name);
680178825Sdfr	return ret;
681178825Sdfr    }
682178825Sdfr
683178825Sdfr    ret = kcm_chown(context, client, ccache, uid, gid);
684178825Sdfr
685178825Sdfr    free(name);
686233294Sstas    kcm_release_ccache(context, ccache);
687178825Sdfr
688178825Sdfr    return ret;
689178825Sdfr}
690178825Sdfr
691178825Sdfr/*
692178825Sdfr * Request:
693178825Sdfr *	NameZ
694178825Sdfr *	Mode
695178825Sdfr *
696178825Sdfr * Response:
697233294Sstas *
698178825Sdfr */
699178825Sdfrstatic krb5_error_code
700178825Sdfrkcm_op_chmod(krb5_context context,
701178825Sdfr	     kcm_client *client,
702178825Sdfr	     kcm_operation opcode,
703178825Sdfr	     krb5_storage *request,
704178825Sdfr	     krb5_storage *response)
705178825Sdfr{
706178825Sdfr    uint16_t mode;
707178825Sdfr    krb5_error_code ret;
708178825Sdfr    kcm_ccache ccache;
709178825Sdfr    char *name;
710178825Sdfr
711178825Sdfr    ret = krb5_ret_stringz(request, &name);
712178825Sdfr    if (ret)
713178825Sdfr	return ret;
714178825Sdfr
715178825Sdfr    KCM_LOG_REQUEST_NAME(context, client, opcode, name);
716178825Sdfr
717178825Sdfr    ret = krb5_ret_uint16(request, &mode);
718178825Sdfr    if (ret) {
719178825Sdfr	free(name);
720178825Sdfr	return ret;
721178825Sdfr    }
722178825Sdfr
723178825Sdfr    ret = kcm_ccache_resolve_client(context, client, opcode,
724178825Sdfr				    name, &ccache);
725178825Sdfr    if (ret) {
726178825Sdfr	free(name);
727178825Sdfr	return ret;
728178825Sdfr    }
729178825Sdfr
730178825Sdfr    ret = kcm_chmod(context, client, ccache, mode);
731178825Sdfr
732178825Sdfr    free(name);
733233294Sstas    kcm_release_ccache(context, ccache);
734178825Sdfr
735178825Sdfr    return ret;
736178825Sdfr}
737178825Sdfr
738178825Sdfr/*
739178825Sdfr * Protocol extensions for moving ticket acquisition responsibility
740178825Sdfr * from client to KCM follow.
741178825Sdfr */
742178825Sdfr
743178825Sdfr/*
744178825Sdfr * Request:
745178825Sdfr *	NameZ
746178825Sdfr *	ServerPrincipalPresent
747178825Sdfr *	ServerPrincipal OPTIONAL
748178825Sdfr *	Key
749178825Sdfr *
750178825Sdfr * Repsonse:
751178825Sdfr *
752178825Sdfr */
753178825Sdfrstatic krb5_error_code
754178825Sdfrkcm_op_get_initial_ticket(krb5_context context,
755178825Sdfr			  kcm_client *client,
756178825Sdfr			  kcm_operation opcode,
757178825Sdfr			  krb5_storage *request,
758178825Sdfr			  krb5_storage *response)
759178825Sdfr{
760178825Sdfr    krb5_error_code ret;
761178825Sdfr    kcm_ccache ccache;
762178825Sdfr    char *name;
763178825Sdfr    int8_t not_tgt = 0;
764178825Sdfr    krb5_principal server = NULL;
765178825Sdfr    krb5_keyblock key;
766178825Sdfr
767178825Sdfr    krb5_keyblock_zero(&key);
768178825Sdfr
769178825Sdfr    ret = krb5_ret_stringz(request, &name);
770178825Sdfr    if (ret)
771178825Sdfr	return ret;
772178825Sdfr
773178825Sdfr    KCM_LOG_REQUEST_NAME(context, client, opcode, name);
774178825Sdfr
775178825Sdfr    ret = krb5_ret_int8(request, &not_tgt);
776178825Sdfr    if (ret) {
777178825Sdfr	free(name);
778178825Sdfr	return ret;
779178825Sdfr    }
780178825Sdfr
781178825Sdfr    if (not_tgt) {
782178825Sdfr	ret = krb5_ret_principal(request, &server);
783178825Sdfr	if (ret) {
784178825Sdfr	    free(name);
785178825Sdfr	    return ret;
786178825Sdfr	}
787178825Sdfr    }
788178825Sdfr
789178825Sdfr    ret = krb5_ret_keyblock(request, &key);
790178825Sdfr    if (ret) {
791178825Sdfr	free(name);
792178825Sdfr	if (server != NULL)
793178825Sdfr	    krb5_free_principal(context, server);
794178825Sdfr	return ret;
795178825Sdfr    }
796178825Sdfr
797178825Sdfr    ret = kcm_ccache_resolve_client(context, client, opcode,
798178825Sdfr				    name, &ccache);
799178825Sdfr    if (ret == 0) {
800178825Sdfr	HEIMDAL_MUTEX_lock(&ccache->mutex);
801178825Sdfr
802178825Sdfr	if (ccache->server != NULL) {
803178825Sdfr	    krb5_free_principal(context, ccache->server);
804178825Sdfr	    ccache->server = NULL;
805178825Sdfr	}
806178825Sdfr
807178825Sdfr	krb5_free_keyblock(context, &ccache->key.keyblock);
808178825Sdfr
809178825Sdfr	ccache->server = server;
810178825Sdfr	ccache->key.keyblock = key;
811178825Sdfr    	ccache->flags |= KCM_FLAGS_USE_CACHED_KEY;
812178825Sdfr
813178825Sdfr	ret = kcm_ccache_enqueue_default(context, ccache, NULL);
814178825Sdfr	if (ret) {
815178825Sdfr	    ccache->server = NULL;
816178825Sdfr	    krb5_keyblock_zero(&ccache->key.keyblock);
817178825Sdfr	    ccache->flags &= ~(KCM_FLAGS_USE_CACHED_KEY);
818178825Sdfr	}
819178825Sdfr
820178825Sdfr	HEIMDAL_MUTEX_unlock(&ccache->mutex);
821178825Sdfr    }
822178825Sdfr
823178825Sdfr    free(name);
824178825Sdfr
825178825Sdfr    if (ret != 0) {
826178825Sdfr	krb5_free_principal(context, server);
827178825Sdfr	krb5_free_keyblock(context, &key);
828178825Sdfr    }
829178825Sdfr
830233294Sstas    kcm_release_ccache(context, ccache);
831178825Sdfr
832178825Sdfr    return ret;
833178825Sdfr}
834178825Sdfr
835178825Sdfr/*
836178825Sdfr * Request:
837178825Sdfr *	NameZ
838178825Sdfr *	ServerPrincipal
839178825Sdfr *	KDCFlags
840178825Sdfr *	EncryptionType
841178825Sdfr *
842178825Sdfr * Repsonse:
843178825Sdfr *
844178825Sdfr */
845178825Sdfrstatic krb5_error_code
846178825Sdfrkcm_op_get_ticket(krb5_context context,
847178825Sdfr		  kcm_client *client,
848178825Sdfr		  kcm_operation opcode,
849178825Sdfr		  krb5_storage *request,
850178825Sdfr		  krb5_storage *response)
851178825Sdfr{
852178825Sdfr    krb5_error_code ret;
853178825Sdfr    kcm_ccache ccache;
854178825Sdfr    char *name;
855178825Sdfr    krb5_principal server = NULL;
856178825Sdfr    krb5_ccache_data ccdata;
857178825Sdfr    krb5_creds in, *out;
858178825Sdfr    krb5_kdc_flags flags;
859178825Sdfr
860178825Sdfr    memset(&in, 0, sizeof(in));
861178825Sdfr
862178825Sdfr    ret = krb5_ret_stringz(request, &name);
863178825Sdfr    if (ret)
864178825Sdfr	return ret;
865178825Sdfr
866178825Sdfr    KCM_LOG_REQUEST_NAME(context, client, opcode, name);
867178825Sdfr
868178825Sdfr    ret = krb5_ret_uint32(request, &flags.i);
869178825Sdfr    if (ret) {
870178825Sdfr	free(name);
871178825Sdfr	return ret;
872178825Sdfr    }
873178825Sdfr
874178825Sdfr    ret = krb5_ret_int32(request, &in.session.keytype);
875178825Sdfr    if (ret) {
876178825Sdfr	free(name);
877178825Sdfr	return ret;
878178825Sdfr    }
879178825Sdfr
880178825Sdfr    ret = krb5_ret_principal(request, &server);
881178825Sdfr    if (ret) {
882178825Sdfr	free(name);
883178825Sdfr	return ret;
884178825Sdfr    }
885178825Sdfr
886178825Sdfr    ret = kcm_ccache_resolve_client(context, client, opcode,
887178825Sdfr				    name, &ccache);
888178825Sdfr    if (ret) {
889178825Sdfr	krb5_free_principal(context, server);
890178825Sdfr	free(name);
891178825Sdfr	return ret;
892178825Sdfr    }
893233294Sstas
894178825Sdfr    HEIMDAL_MUTEX_lock(&ccache->mutex);
895178825Sdfr
896178825Sdfr    /* Fake up an internal ccache */
897178825Sdfr    kcm_internal_ccache(context, ccache, &ccdata);
898178825Sdfr
899178825Sdfr    in.client = ccache->client;
900178825Sdfr    in.server = server;
901178825Sdfr    in.times.endtime = 0;
902178825Sdfr
903178825Sdfr    /* glue cc layer will store creds */
904178825Sdfr    ret = krb5_get_credentials_with_flags(context, 0, flags,
905178825Sdfr					  &ccdata, &in, &out);
906178825Sdfr
907178825Sdfr    HEIMDAL_MUTEX_unlock(&ccache->mutex);
908178825Sdfr
909233294Sstas    krb5_free_principal(context, server);
910233294Sstas
911178825Sdfr    if (ret == 0)
912178825Sdfr	krb5_free_cred_contents(context, out);
913178825Sdfr
914233294Sstas    kcm_release_ccache(context, ccache);
915178825Sdfr    free(name);
916178825Sdfr
917178825Sdfr    return ret;
918178825Sdfr}
919178825Sdfr
920233294Sstas/*
921233294Sstas * Request:
922233294Sstas *	OldNameZ
923233294Sstas *	NewNameZ
924233294Sstas *
925233294Sstas * Repsonse:
926233294Sstas *
927233294Sstas */
928233294Sstasstatic krb5_error_code
929233294Sstaskcm_op_move_cache(krb5_context context,
930233294Sstas		  kcm_client *client,
931233294Sstas		  kcm_operation opcode,
932233294Sstas		  krb5_storage *request,
933233294Sstas		  krb5_storage *response)
934233294Sstas{
935233294Sstas    krb5_error_code ret;
936233294Sstas    kcm_ccache oldid, newid;
937233294Sstas    char *oldname, *newname;
938233294Sstas
939233294Sstas    ret = krb5_ret_stringz(request, &oldname);
940233294Sstas    if (ret)
941233294Sstas	return ret;
942233294Sstas
943233294Sstas    KCM_LOG_REQUEST_NAME(context, client, opcode, oldname);
944233294Sstas
945233294Sstas    ret = krb5_ret_stringz(request, &newname);
946233294Sstas    if (ret) {
947233294Sstas	free(oldname);
948233294Sstas	return ret;
949233294Sstas    }
950233294Sstas
951233294Sstas    /* move to ourself is simple, done! */
952233294Sstas    if (strcmp(oldname, newname) == 0) {
953233294Sstas	free(oldname);
954233294Sstas	free(newname);
955233294Sstas	return 0;
956233294Sstas    }
957233294Sstas
958233294Sstas    ret = kcm_ccache_resolve_client(context, client, opcode, oldname, &oldid);
959233294Sstas    if (ret) {
960233294Sstas	free(oldname);
961233294Sstas	free(newname);
962233294Sstas	return ret;
963233294Sstas    }
964233294Sstas
965233294Sstas    /* Check if new credential cache exists, if not create one. */
966233294Sstas    ret = kcm_ccache_resolve_client(context, client, opcode, newname, &newid);
967233294Sstas    if (ret == KRB5_FCC_NOFILE)
968233294Sstas	ret = kcm_ccache_new_client(context, client, newname, &newid);
969233294Sstas    free(newname);
970233294Sstas
971233294Sstas    if (ret) {
972233294Sstas	free(oldname);
973233294Sstas	kcm_release_ccache(context, oldid);
974233294Sstas	return ret;
975233294Sstas    }
976233294Sstas
977233294Sstas    HEIMDAL_MUTEX_lock(&oldid->mutex);
978233294Sstas    HEIMDAL_MUTEX_lock(&newid->mutex);
979233294Sstas
980233294Sstas    /* move content */
981233294Sstas    {
982233294Sstas	kcm_ccache_data tmp;
983233294Sstas
984233294Sstas#define MOVE(n,o,f) { tmp.f = n->f ; n->f = o->f; o->f = tmp.f; }
985233294Sstas
986233294Sstas	MOVE(newid, oldid, flags);
987233294Sstas	MOVE(newid, oldid, client);
988233294Sstas	MOVE(newid, oldid, server);
989233294Sstas	MOVE(newid, oldid, creds);
990233294Sstas	MOVE(newid, oldid, tkt_life);
991233294Sstas	MOVE(newid, oldid, renew_life);
992233294Sstas	MOVE(newid, oldid, key);
993233294Sstas	MOVE(newid, oldid, kdc_offset);
994233294Sstas#undef MOVE
995233294Sstas    }
996233294Sstas
997233294Sstas    HEIMDAL_MUTEX_unlock(&oldid->mutex);
998233294Sstas    HEIMDAL_MUTEX_unlock(&newid->mutex);
999233294Sstas
1000233294Sstas    kcm_release_ccache(context, oldid);
1001233294Sstas    kcm_release_ccache(context, newid);
1002233294Sstas
1003233294Sstas    ret = kcm_ccache_destroy_client(context, client, oldname);
1004233294Sstas    if (ret == 0)
1005233294Sstas	kcm_drop_default_cache(context, client, oldname);
1006233294Sstas
1007233294Sstas    free(oldname);
1008233294Sstas
1009233294Sstas    return ret;
1010233294Sstas}
1011233294Sstas
1012233294Sstasstatic krb5_error_code
1013233294Sstaskcm_op_get_cache_uuid_list(krb5_context context,
1014233294Sstas			   kcm_client *client,
1015233294Sstas			   kcm_operation opcode,
1016233294Sstas			   krb5_storage *request,
1017233294Sstas			   krb5_storage *response)
1018233294Sstas{
1019233294Sstas    KCM_LOG_REQUEST(context, client, opcode);
1020233294Sstas
1021233294Sstas    return kcm_ccache_get_uuids(context, client, opcode, response);
1022233294Sstas}
1023233294Sstas
1024233294Sstasstatic krb5_error_code
1025233294Sstaskcm_op_get_cache_by_uuid(krb5_context context,
1026233294Sstas			 kcm_client *client,
1027233294Sstas			 kcm_operation opcode,
1028233294Sstas			 krb5_storage *request,
1029233294Sstas			 krb5_storage *response)
1030233294Sstas{
1031233294Sstas    krb5_error_code ret;
1032233294Sstas    kcmuuid_t uuid;
1033233294Sstas    ssize_t sret;
1034233294Sstas    kcm_ccache cache;
1035233294Sstas
1036233294Sstas    KCM_LOG_REQUEST(context, client, opcode);
1037233294Sstas
1038233294Sstas    sret = krb5_storage_read(request, &uuid, sizeof(uuid));
1039233294Sstas    if (sret != sizeof(uuid)) {
1040233294Sstas	krb5_clear_error_message(context);
1041233294Sstas	return KRB5_CC_IO;
1042233294Sstas    }
1043233294Sstas
1044233294Sstas    ret = kcm_ccache_resolve_by_uuid(context, uuid, &cache);
1045233294Sstas    if (ret)
1046233294Sstas	return ret;
1047233294Sstas
1048233294Sstas    ret = kcm_access(context, client, opcode, cache);
1049233294Sstas    if (ret)
1050233294Sstas	ret = KRB5_FCC_NOFILE;
1051233294Sstas
1052233294Sstas    if (ret == 0)
1053233294Sstas	ret = krb5_store_stringz(response, cache->name);
1054233294Sstas
1055233294Sstas    kcm_release_ccache(context, cache);
1056233294Sstas
1057233294Sstas    return ret;
1058233294Sstas}
1059233294Sstas
1060233294Sstasstruct kcm_default_cache *default_caches;
1061233294Sstas
1062233294Sstasstatic krb5_error_code
1063233294Sstaskcm_op_get_default_cache(krb5_context context,
1064233294Sstas			 kcm_client *client,
1065233294Sstas			 kcm_operation opcode,
1066233294Sstas			 krb5_storage *request,
1067233294Sstas			 krb5_storage *response)
1068233294Sstas{
1069233294Sstas    struct kcm_default_cache *c;
1070233294Sstas    krb5_error_code ret;
1071233294Sstas    const char *name = NULL;
1072233294Sstas    char *n = NULL;
1073233294Sstas
1074233294Sstas    KCM_LOG_REQUEST(context, client, opcode);
1075233294Sstas
1076233294Sstas    for (c = default_caches; c != NULL; c = c->next) {
1077233294Sstas	if (kcm_is_same_session(client, c->uid, c->session)) {
1078233294Sstas	    name = c->name;
1079233294Sstas	    break;
1080233294Sstas	}
1081233294Sstas    }
1082233294Sstas    if (name == NULL)
1083233294Sstas	name = n = kcm_ccache_first_name(client);
1084233294Sstas
1085233294Sstas    if (name == NULL) {
1086233294Sstas	asprintf(&n, "%d", (int)client->uid);
1087233294Sstas	name = n;
1088233294Sstas    }
1089233294Sstas    if (name == NULL)
1090233294Sstas	return ENOMEM;
1091233294Sstas    ret = krb5_store_stringz(response, name);
1092233294Sstas    if (n)
1093233294Sstas	free(n);
1094233294Sstas    return ret;
1095233294Sstas}
1096233294Sstas
1097233294Sstasstatic void
1098233294Sstaskcm_drop_default_cache(krb5_context context, kcm_client *client, char *name)
1099233294Sstas{
1100233294Sstas    struct kcm_default_cache **c;
1101233294Sstas
1102233294Sstas    for (c = &default_caches; *c != NULL; c = &(*c)->next) {
1103233294Sstas	if (!kcm_is_same_session(client, (*c)->uid, (*c)->session))
1104233294Sstas	    continue;
1105233294Sstas	if (strcmp((*c)->name, name) == 0) {
1106233294Sstas	    struct kcm_default_cache *h = *c;
1107233294Sstas	    *c = (*c)->next;
1108233294Sstas	    free(h->name);
1109233294Sstas	    free(h);
1110233294Sstas	    break;
1111233294Sstas	}
1112233294Sstas    }
1113233294Sstas}
1114233294Sstas
1115233294Sstasstatic krb5_error_code
1116233294Sstaskcm_op_set_default_cache(krb5_context context,
1117233294Sstas			 kcm_client *client,
1118233294Sstas			 kcm_operation opcode,
1119233294Sstas			 krb5_storage *request,
1120233294Sstas			 krb5_storage *response)
1121233294Sstas{
1122233294Sstas    struct kcm_default_cache *c;
1123233294Sstas    krb5_error_code ret;
1124233294Sstas    char *name;
1125233294Sstas
1126233294Sstas    ret = krb5_ret_stringz(request, &name);
1127233294Sstas    if (ret)
1128233294Sstas	return ret;
1129233294Sstas
1130233294Sstas    KCM_LOG_REQUEST_NAME(context, client, opcode, name);
1131233294Sstas
1132233294Sstas    for (c = default_caches; c != NULL; c = c->next) {
1133233294Sstas	if (kcm_is_same_session(client, c->uid, c->session))
1134233294Sstas	    break;
1135233294Sstas    }
1136233294Sstas    if (c == NULL) {
1137233294Sstas	c = malloc(sizeof(*c));
1138233294Sstas	if (c == NULL)
1139233294Sstas	    return ENOMEM;
1140233294Sstas	c->session = client->session;
1141233294Sstas	c->uid = client->uid;
1142233294Sstas	c->name = strdup(name);
1143233294Sstas
1144233294Sstas	c->next = default_caches;
1145233294Sstas	default_caches = c;
1146233294Sstas    } else {
1147233294Sstas	free(c->name);
1148233294Sstas	c->name = strdup(name);
1149233294Sstas    }
1150233294Sstas
1151233294Sstas    return 0;
1152233294Sstas}
1153233294Sstas
1154233294Sstasstatic krb5_error_code
1155233294Sstaskcm_op_get_kdc_offset(krb5_context context,
1156233294Sstas		      kcm_client *client,
1157233294Sstas		      kcm_operation opcode,
1158233294Sstas		      krb5_storage *request,
1159233294Sstas		      krb5_storage *response)
1160233294Sstas{
1161233294Sstas    krb5_error_code ret;
1162233294Sstas    kcm_ccache ccache;
1163233294Sstas    char *name;
1164233294Sstas
1165233294Sstas    ret = krb5_ret_stringz(request, &name);
1166233294Sstas    if (ret)
1167233294Sstas	return ret;
1168233294Sstas
1169233294Sstas    KCM_LOG_REQUEST_NAME(context, client, opcode, name);
1170233294Sstas
1171233294Sstas    ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache);
1172233294Sstas    free(name);
1173233294Sstas    if (ret)
1174233294Sstas	return ret;
1175233294Sstas
1176233294Sstas    HEIMDAL_MUTEX_lock(&ccache->mutex);
1177233294Sstas    ret = krb5_store_int32(response, ccache->kdc_offset);
1178233294Sstas    HEIMDAL_MUTEX_unlock(&ccache->mutex);
1179233294Sstas
1180233294Sstas    kcm_release_ccache(context, ccache);
1181233294Sstas
1182233294Sstas    return ret;
1183233294Sstas}
1184233294Sstas
1185233294Sstasstatic krb5_error_code
1186233294Sstaskcm_op_set_kdc_offset(krb5_context context,
1187233294Sstas		      kcm_client *client,
1188233294Sstas		      kcm_operation opcode,
1189233294Sstas		      krb5_storage *request,
1190233294Sstas		      krb5_storage *response)
1191233294Sstas{
1192233294Sstas    krb5_error_code ret;
1193233294Sstas    kcm_ccache ccache;
1194233294Sstas    int32_t offset;
1195233294Sstas    char *name;
1196233294Sstas
1197233294Sstas    ret = krb5_ret_stringz(request, &name);
1198233294Sstas    if (ret)
1199233294Sstas	return ret;
1200233294Sstas
1201233294Sstas    KCM_LOG_REQUEST_NAME(context, client, opcode, name);
1202233294Sstas
1203233294Sstas    ret = krb5_ret_int32(request, &offset);
1204233294Sstas    if (ret) {
1205233294Sstas	free(name);
1206233294Sstas	return ret;
1207233294Sstas    }
1208233294Sstas
1209233294Sstas    ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache);
1210233294Sstas    free(name);
1211233294Sstas    if (ret)
1212233294Sstas	return ret;
1213233294Sstas
1214233294Sstas    HEIMDAL_MUTEX_lock(&ccache->mutex);
1215233294Sstas    ccache->kdc_offset = offset;
1216233294Sstas    HEIMDAL_MUTEX_unlock(&ccache->mutex);
1217233294Sstas
1218233294Sstas    kcm_release_ccache(context, ccache);
1219233294Sstas
1220233294Sstas    return ret;
1221233294Sstas}
1222233294Sstas
1223233294Sstasstruct kcm_ntlm_cred {
1224233294Sstas    kcmuuid_t uuid;
1225233294Sstas    char *user;
1226233294Sstas    char *domain;
1227233294Sstas    krb5_data nthash;
1228233294Sstas    uid_t uid;
1229233294Sstas    pid_t session;
1230233294Sstas    struct kcm_ntlm_cred *next;
1231233294Sstas};
1232233294Sstas
1233233294Sstasstatic struct kcm_ntlm_cred *ntlm_head;
1234233294Sstas
1235233294Sstasstatic void
1236233294Sstasfree_cred(struct kcm_ntlm_cred *cred)
1237233294Sstas{
1238233294Sstas    free(cred->user);
1239233294Sstas    free(cred->domain);
1240233294Sstas    krb5_data_free(&cred->nthash);
1241233294Sstas    free(cred);
1242233294Sstas}
1243233294Sstas
1244233294Sstas
1245233294Sstas/*
1246233294Sstas * name
1247233294Sstas * domain
1248233294Sstas * ntlm hash
1249233294Sstas *
1250233294Sstas * Reply:
1251233294Sstas *   uuid
1252233294Sstas */
1253233294Sstas
1254233294Sstasstatic struct kcm_ntlm_cred *
1255233294Sstasfind_ntlm_cred(const char *user, const char *domain, kcm_client *client)
1256233294Sstas{
1257233294Sstas    struct kcm_ntlm_cred *c;
1258233294Sstas
1259233294Sstas    for (c = ntlm_head; c != NULL; c = c->next)
1260233294Sstas	if ((user[0] == '\0' || strcmp(user, c->user) == 0) &&
1261233294Sstas	    (domain == NULL || strcmp(domain, c->domain) == 0) &&
1262233294Sstas	    kcm_is_same_session(client, c->uid, c->session))
1263233294Sstas	    return c;
1264233294Sstas
1265233294Sstas    return NULL;
1266233294Sstas}
1267233294Sstas
1268233294Sstasstatic krb5_error_code
1269233294Sstaskcm_op_add_ntlm_cred(krb5_context context,
1270233294Sstas		     kcm_client *client,
1271233294Sstas		     kcm_operation opcode,
1272233294Sstas		     krb5_storage *request,
1273233294Sstas		     krb5_storage *response)
1274233294Sstas{
1275233294Sstas    struct kcm_ntlm_cred *cred, *c;
1276233294Sstas    krb5_error_code ret;
1277233294Sstas
1278233294Sstas    cred = calloc(1, sizeof(*cred));
1279233294Sstas    if (cred == NULL)
1280233294Sstas	return ENOMEM;
1281233294Sstas
1282233294Sstas    RAND_bytes(cred->uuid, sizeof(cred->uuid));
1283233294Sstas
1284233294Sstas    ret = krb5_ret_stringz(request, &cred->user);
1285233294Sstas    if (ret)
1286233294Sstas	goto error;
1287233294Sstas
1288233294Sstas    ret = krb5_ret_stringz(request, &cred->domain);
1289233294Sstas    if (ret)
1290233294Sstas	goto error;
1291233294Sstas
1292233294Sstas    ret = krb5_ret_data(request, &cred->nthash);
1293233294Sstas    if (ret)
1294233294Sstas	goto error;
1295233294Sstas
1296233294Sstas    /* search for dups */
1297233294Sstas    c = find_ntlm_cred(cred->user, cred->domain, client);
1298233294Sstas    if (c) {
1299233294Sstas	krb5_data hash = c->nthash;
1300233294Sstas	c->nthash = cred->nthash;
1301233294Sstas	cred->nthash = hash;
1302233294Sstas	free_cred(cred);
1303233294Sstas	cred = c;
1304233294Sstas    } else {
1305233294Sstas	cred->next = ntlm_head;
1306233294Sstas	ntlm_head = cred;
1307233294Sstas    }
1308233294Sstas
1309233294Sstas    cred->uid = client->uid;
1310233294Sstas    cred->session = client->session;
1311233294Sstas
1312233294Sstas    /* write response */
1313233294Sstas    (void)krb5_storage_write(response, &cred->uuid, sizeof(cred->uuid));
1314233294Sstas
1315233294Sstas    return 0;
1316233294Sstas
1317233294Sstas error:
1318233294Sstas    free_cred(cred);
1319233294Sstas
1320233294Sstas    return ret;
1321233294Sstas}
1322233294Sstas
1323233294Sstas/*
1324233294Sstas * { "HAVE_NTLM_CRED",		NULL },
1325233294Sstas *
1326233294Sstas * input:
1327233294Sstas *  name
1328233294Sstas *  domain
1329233294Sstas */
1330233294Sstas
1331233294Sstasstatic krb5_error_code
1332233294Sstaskcm_op_have_ntlm_cred(krb5_context context,
1333233294Sstas		     kcm_client *client,
1334233294Sstas		     kcm_operation opcode,
1335233294Sstas		     krb5_storage *request,
1336233294Sstas		     krb5_storage *response)
1337233294Sstas{
1338233294Sstas    struct kcm_ntlm_cred *c;
1339233294Sstas    char *user = NULL, *domain = NULL;
1340233294Sstas    krb5_error_code ret;
1341233294Sstas
1342233294Sstas    ret = krb5_ret_stringz(request, &user);
1343233294Sstas    if (ret)
1344233294Sstas	goto error;
1345233294Sstas
1346233294Sstas    ret = krb5_ret_stringz(request, &domain);
1347233294Sstas    if (ret)
1348233294Sstas	goto error;
1349233294Sstas
1350233294Sstas    if (domain[0] == '\0') {
1351233294Sstas	free(domain);
1352233294Sstas	domain = NULL;
1353233294Sstas    }
1354233294Sstas
1355233294Sstas    c = find_ntlm_cred(user, domain, client);
1356233294Sstas    if (c == NULL)
1357233294Sstas	ret = ENOENT;
1358233294Sstas
1359233294Sstas error:
1360233294Sstas    free(user);
1361233294Sstas    if (domain)
1362233294Sstas	free(domain);
1363233294Sstas
1364233294Sstas    return ret;
1365233294Sstas}
1366233294Sstas
1367233294Sstas/*
1368233294Sstas * { "DEL_NTLM_CRED",		NULL },
1369233294Sstas *
1370233294Sstas * input:
1371233294Sstas *  name
1372233294Sstas *  domain
1373233294Sstas */
1374233294Sstas
1375233294Sstasstatic krb5_error_code
1376233294Sstaskcm_op_del_ntlm_cred(krb5_context context,
1377233294Sstas		     kcm_client *client,
1378233294Sstas		     kcm_operation opcode,
1379233294Sstas		     krb5_storage *request,
1380233294Sstas		     krb5_storage *response)
1381233294Sstas{
1382233294Sstas    struct kcm_ntlm_cred **cp, *c;
1383233294Sstas    char *user = NULL, *domain = NULL;
1384233294Sstas    krb5_error_code ret;
1385233294Sstas
1386233294Sstas    ret = krb5_ret_stringz(request, &user);
1387233294Sstas    if (ret)
1388233294Sstas	goto error;
1389233294Sstas
1390233294Sstas    ret = krb5_ret_stringz(request, &domain);
1391233294Sstas    if (ret)
1392233294Sstas	goto error;
1393233294Sstas
1394233294Sstas    for (cp = &ntlm_head; *cp != NULL; cp = &(*cp)->next) {
1395233294Sstas	if (strcmp(user, (*cp)->user) == 0 && strcmp(domain, (*cp)->domain) == 0 &&
1396233294Sstas	    kcm_is_same_session(client, (*cp)->uid, (*cp)->session))
1397233294Sstas	{
1398233294Sstas	    c = *cp;
1399233294Sstas	    *cp = c->next;
1400233294Sstas
1401233294Sstas	    free_cred(c);
1402233294Sstas	    break;
1403233294Sstas	}
1404233294Sstas    }
1405233294Sstas
1406233294Sstas error:
1407233294Sstas    free(user);
1408233294Sstas    free(domain);
1409233294Sstas
1410233294Sstas    return ret;
1411233294Sstas}
1412233294Sstas
1413233294Sstas/*
1414233294Sstas * { "DO_NTLM_AUTH",		NULL },
1415233294Sstas *
1416233294Sstas * input:
1417233294Sstas *  name:string
1418233294Sstas *  domain:string
1419233294Sstas *  type2:data
1420233294Sstas *
1421233294Sstas * reply:
1422233294Sstas *  type3:data
1423233294Sstas *  flags:int32
1424233294Sstas *  session-key:data
1425233294Sstas */
1426233294Sstas
1427233294Sstas#define NTLM_FLAG_SESSIONKEY 1
1428233294Sstas#define NTLM_FLAG_NTLM2_SESSION 2
1429233294Sstas#define NTLM_FLAG_KEYEX 4
1430233294Sstas
1431233294Sstasstatic krb5_error_code
1432233294Sstaskcm_op_do_ntlm(krb5_context context,
1433233294Sstas	       kcm_client *client,
1434233294Sstas	       kcm_operation opcode,
1435233294Sstas	       krb5_storage *request,
1436233294Sstas	       krb5_storage *response)
1437233294Sstas{
1438233294Sstas    struct kcm_ntlm_cred *c;
1439233294Sstas    struct ntlm_type2 type2;
1440233294Sstas    struct ntlm_type3 type3;
1441233294Sstas    char *user = NULL, *domain = NULL;
1442233294Sstas    struct ntlm_buf ndata, sessionkey;
1443233294Sstas    krb5_data data;
1444233294Sstas    krb5_error_code ret;
1445233294Sstas    uint32_t flags = 0;
1446233294Sstas
1447233294Sstas    memset(&type2, 0, sizeof(type2));
1448233294Sstas    memset(&type3, 0, sizeof(type3));
1449233294Sstas    sessionkey.data = NULL;
1450233294Sstas    sessionkey.length = 0;
1451233294Sstas
1452233294Sstas    ret = krb5_ret_stringz(request, &user);
1453233294Sstas    if (ret)
1454233294Sstas	goto error;
1455233294Sstas
1456233294Sstas    ret = krb5_ret_stringz(request, &domain);
1457233294Sstas    if (ret)
1458233294Sstas	goto error;
1459233294Sstas
1460233294Sstas    if (domain[0] == '\0') {
1461233294Sstas	free(domain);
1462233294Sstas	domain = NULL;
1463233294Sstas    }
1464233294Sstas
1465233294Sstas    c = find_ntlm_cred(user, domain, client);
1466233294Sstas    if (c == NULL) {
1467233294Sstas	ret = EINVAL;
1468233294Sstas	goto error;
1469233294Sstas    }
1470233294Sstas
1471233294Sstas    ret = krb5_ret_data(request, &data);
1472233294Sstas    if (ret)
1473233294Sstas	goto error;
1474233294Sstas
1475233294Sstas    ndata.data = data.data;
1476233294Sstas    ndata.length = data.length;
1477233294Sstas
1478233294Sstas    ret = heim_ntlm_decode_type2(&ndata, &type2);
1479233294Sstas    krb5_data_free(&data);
1480233294Sstas    if (ret)
1481233294Sstas	goto error;
1482233294Sstas
1483233294Sstas    if (domain && strcmp(domain, type2.targetname) == 0) {
1484233294Sstas	ret = EINVAL;
1485233294Sstas	goto error;
1486233294Sstas    }
1487233294Sstas
1488233294Sstas    type3.username = c->user;
1489233294Sstas    type3.flags = type2.flags;
1490233294Sstas    type3.targetname = type2.targetname;
1491233294Sstas    type3.ws = rk_UNCONST("workstation");
1492233294Sstas
1493233294Sstas    /*
1494233294Sstas     * NTLM Version 1 if no targetinfo buffer.
1495233294Sstas     */
1496233294Sstas
1497233294Sstas    if (1 || type2.targetinfo.length == 0) {
1498233294Sstas	struct ntlm_buf sessionkey;
1499233294Sstas
1500233294Sstas	if (type2.flags & NTLM_NEG_NTLM2_SESSION) {
1501233294Sstas	    unsigned char nonce[8];
1502233294Sstas
1503233294Sstas	    if (RAND_bytes(nonce, sizeof(nonce)) != 1) {
1504233294Sstas		ret = EINVAL;
1505233294Sstas		goto error;
1506233294Sstas	    }
1507233294Sstas
1508233294Sstas	    ret = heim_ntlm_calculate_ntlm2_sess(nonce,
1509233294Sstas						 type2.challenge,
1510233294Sstas						 c->nthash.data,
1511233294Sstas						 &type3.lm,
1512233294Sstas						 &type3.ntlm);
1513233294Sstas	} else {
1514233294Sstas	    ret = heim_ntlm_calculate_ntlm1(c->nthash.data,
1515233294Sstas					    c->nthash.length,
1516233294Sstas					    type2.challenge,
1517233294Sstas					    &type3.ntlm);
1518233294Sstas
1519233294Sstas	}
1520233294Sstas	if (ret)
1521233294Sstas	    goto error;
1522233294Sstas
1523233294Sstas	ret = heim_ntlm_build_ntlm1_master(c->nthash.data,
1524233294Sstas					   c->nthash.length,
1525233294Sstas					   &sessionkey,
1526233294Sstas					   &type3.sessionkey);
1527233294Sstas	if (ret) {
1528233294Sstas	    if (type3.lm.data)
1529233294Sstas		free(type3.lm.data);
1530233294Sstas	    if (type3.ntlm.data)
1531233294Sstas		free(type3.ntlm.data);
1532233294Sstas	    goto error;
1533233294Sstas	}
1534233294Sstas
1535233294Sstas	free(sessionkey.data);
1536233294Sstas	if (ret) {
1537233294Sstas	    if (type3.lm.data)
1538233294Sstas		free(type3.lm.data);
1539233294Sstas	    if (type3.ntlm.data)
1540233294Sstas		free(type3.ntlm.data);
1541233294Sstas	    goto error;
1542233294Sstas	}
1543233294Sstas	flags |= NTLM_FLAG_SESSIONKEY;
1544233294Sstas#if 0
1545233294Sstas    } else {
1546233294Sstas	struct ntlm_buf sessionkey;
1547233294Sstas	unsigned char ntlmv2[16];
1548233294Sstas	struct ntlm_targetinfo ti;
1549233294Sstas
1550233294Sstas	/* verify infotarget */
1551233294Sstas
1552233294Sstas	ret = heim_ntlm_decode_targetinfo(&type2.targetinfo, 1, &ti);
1553233294Sstas	if(ret) {
1554233294Sstas	    _gss_ntlm_delete_sec_context(minor_status,
1555233294Sstas					 context_handle, NULL);
1556233294Sstas	    *minor_status = ret;
1557233294Sstas	    return GSS_S_FAILURE;
1558233294Sstas	}
1559233294Sstas
1560233294Sstas	if (ti.domainname && strcmp(ti.domainname, name->domain) != 0) {
1561233294Sstas	    _gss_ntlm_delete_sec_context(minor_status,
1562233294Sstas					 context_handle, NULL);
1563233294Sstas	    *minor_status = EINVAL;
1564233294Sstas	    return GSS_S_FAILURE;
1565233294Sstas	}
1566233294Sstas
1567233294Sstas	ret = heim_ntlm_calculate_ntlm2(ctx->client->key.data,
1568233294Sstas					ctx->client->key.length,
1569233294Sstas					type3.username,
1570233294Sstas					name->domain,
1571233294Sstas					type2.challenge,
1572233294Sstas					&type2.targetinfo,
1573233294Sstas					ntlmv2,
1574233294Sstas					&type3.ntlm);
1575233294Sstas	if (ret) {
1576233294Sstas	    _gss_ntlm_delete_sec_context(minor_status,
1577233294Sstas					 context_handle, NULL);
1578233294Sstas	    *minor_status = ret;
1579233294Sstas	    return GSS_S_FAILURE;
1580233294Sstas	}
1581233294Sstas
1582233294Sstas	ret = heim_ntlm_build_ntlm1_master(ntlmv2, sizeof(ntlmv2),
1583233294Sstas					   &sessionkey,
1584233294Sstas					   &type3.sessionkey);
1585233294Sstas	memset(ntlmv2, 0, sizeof(ntlmv2));
1586233294Sstas	if (ret) {
1587233294Sstas	    _gss_ntlm_delete_sec_context(minor_status,
1588233294Sstas					 context_handle, NULL);
1589233294Sstas	    *minor_status = ret;
1590233294Sstas	    return GSS_S_FAILURE;
1591233294Sstas	}
1592233294Sstas
1593233294Sstas	flags |= NTLM_FLAG_NTLM2_SESSION |
1594233294Sstas	         NTLM_FLAG_SESSION;
1595233294Sstas
1596233294Sstas	if (type3.flags & NTLM_NEG_KEYEX)
1597233294Sstas	    flags |= NTLM_FLAG_KEYEX;
1598233294Sstas
1599233294Sstas	ret = krb5_data_copy(&ctx->sessionkey,
1600233294Sstas			     sessionkey.data, sessionkey.length);
1601233294Sstas	free(sessionkey.data);
1602233294Sstas	if (ret) {
1603233294Sstas	    _gss_ntlm_delete_sec_context(minor_status,
1604233294Sstas					 context_handle, NULL);
1605233294Sstas	    *minor_status = ret;
1606233294Sstas	    return GSS_S_FAILURE;
1607233294Sstas	}
1608233294Sstas#endif
1609233294Sstas    }
1610233294Sstas
1611233294Sstas#if 0
1612233294Sstas    if (flags & NTLM_FLAG_NTLM2_SESSION) {
1613233294Sstas	_gss_ntlm_set_key(&ctx->u.v2.send, 0, (ctx->flags & NTLM_NEG_KEYEX),
1614233294Sstas			  ctx->sessionkey.data,
1615233294Sstas			  ctx->sessionkey.length);
1616233294Sstas	_gss_ntlm_set_key(&ctx->u.v2.recv, 1, (ctx->flags & NTLM_NEG_KEYEX),
1617233294Sstas			  ctx->sessionkey.data,
1618233294Sstas			  ctx->sessionkey.length);
1619233294Sstas    } else {
1620233294Sstas	flags |= NTLM_FLAG_SESSION;
1621233294Sstas	RC4_set_key(&ctx->u.v1.crypto_recv.key,
1622233294Sstas		    ctx->sessionkey.length,
1623233294Sstas		    ctx->sessionkey.data);
1624233294Sstas	RC4_set_key(&ctx->u.v1.crypto_send.key,
1625233294Sstas		    ctx->sessionkey.length,
1626233294Sstas		    ctx->sessionkey.data);
1627233294Sstas    }
1628233294Sstas#endif
1629233294Sstas
1630233294Sstas    ret = heim_ntlm_encode_type3(&type3, &ndata);
1631233294Sstas    if (ret)
1632233294Sstas	goto error;
1633233294Sstas
1634233294Sstas    data.data = ndata.data;
1635233294Sstas    data.length = ndata.length;
1636233294Sstas    ret = krb5_store_data(response, data);
1637233294Sstas    heim_ntlm_free_buf(&ndata);
1638233294Sstas    if (ret) goto error;
1639233294Sstas
1640233294Sstas    ret = krb5_store_int32(response, flags);
1641233294Sstas    if (ret) goto error;
1642233294Sstas
1643233294Sstas    data.data = sessionkey.data;
1644233294Sstas    data.length = sessionkey.length;
1645233294Sstas
1646233294Sstas    ret = krb5_store_data(response, data);
1647233294Sstas    if (ret) goto error;
1648233294Sstas
1649233294Sstas error:
1650233294Sstas    free(type3.username);
1651233294Sstas    heim_ntlm_free_type2(&type2);
1652233294Sstas    free(user);
1653233294Sstas    if (domain)
1654233294Sstas	free(domain);
1655233294Sstas
1656233294Sstas    return ret;
1657233294Sstas}
1658233294Sstas
1659233294Sstas
1660233294Sstas/*
1661233294Sstas * { "GET_NTLM_UUID_LIST",	NULL }
1662233294Sstas *
1663233294Sstas * reply:
1664233294Sstas *   1 user domain
1665233294Sstas *   0 [ end of list ]
1666233294Sstas */
1667233294Sstas
1668233294Sstasstatic krb5_error_code
1669233294Sstaskcm_op_get_ntlm_user_list(krb5_context context,
1670233294Sstas			  kcm_client *client,
1671233294Sstas			  kcm_operation opcode,
1672233294Sstas			  krb5_storage *request,
1673233294Sstas			  krb5_storage *response)
1674233294Sstas{
1675233294Sstas    struct kcm_ntlm_cred *c;
1676233294Sstas    krb5_error_code ret;
1677233294Sstas
1678233294Sstas    for (c = ntlm_head; c != NULL; c = c->next) {
1679233294Sstas	if (!kcm_is_same_session(client, c->uid, c->session))
1680233294Sstas	    continue;
1681233294Sstas
1682233294Sstas	ret = krb5_store_uint32(response, 1);
1683233294Sstas	if (ret)
1684233294Sstas	    return ret;
1685233294Sstas	ret = krb5_store_stringz(response, c->user);
1686233294Sstas	if (ret)
1687233294Sstas	    return ret;
1688233294Sstas	ret = krb5_store_stringz(response, c->domain);
1689233294Sstas	if (ret)
1690233294Sstas	    return ret;
1691233294Sstas    }
1692233294Sstas    return krb5_store_uint32(response, 0);
1693233294Sstas}
1694233294Sstas
1695233294Sstas/*
1696233294Sstas *
1697233294Sstas */
1698233294Sstas
1699178825Sdfrstatic struct kcm_op kcm_ops[] = {
1700178825Sdfr    { "NOOP", 			kcm_op_noop },
1701178825Sdfr    { "GET_NAME",		kcm_op_get_name },
1702178825Sdfr    { "RESOLVE",		kcm_op_noop },
1703178825Sdfr    { "GEN_NEW", 		kcm_op_gen_new },
1704178825Sdfr    { "INITIALIZE",		kcm_op_initialize },
1705178825Sdfr    { "DESTROY",		kcm_op_destroy },
1706178825Sdfr    { "STORE",			kcm_op_store },
1707178825Sdfr    { "RETRIEVE",		kcm_op_retrieve },
1708178825Sdfr    { "GET_PRINCIPAL",		kcm_op_get_principal },
1709233294Sstas    { "GET_CRED_UUID_LIST",	kcm_op_get_cred_uuid_list },
1710233294Sstas    { "GET_CRED_BY_UUID",	kcm_op_get_cred_by_uuid },
1711178825Sdfr    { "REMOVE_CRED",		kcm_op_remove_cred },
1712178825Sdfr    { "SET_FLAGS",		kcm_op_set_flags },
1713178825Sdfr    { "CHOWN",			kcm_op_chown },
1714178825Sdfr    { "CHMOD",			kcm_op_chmod },
1715178825Sdfr    { "GET_INITIAL_TICKET",	kcm_op_get_initial_ticket },
1716233294Sstas    { "GET_TICKET",		kcm_op_get_ticket },
1717233294Sstas    { "MOVE_CACHE",		kcm_op_move_cache },
1718233294Sstas    { "GET_CACHE_UUID_LIST",	kcm_op_get_cache_uuid_list },
1719233294Sstas    { "GET_CACHE_BY_UUID",	kcm_op_get_cache_by_uuid },
1720233294Sstas    { "GET_DEFAULT_CACHE",      kcm_op_get_default_cache },
1721233294Sstas    { "SET_DEFAULT_CACHE",      kcm_op_set_default_cache },
1722233294Sstas    { "GET_KDC_OFFSET",      	kcm_op_get_kdc_offset },
1723233294Sstas    { "SET_KDC_OFFSET",      	kcm_op_set_kdc_offset },
1724233294Sstas    { "ADD_NTLM_CRED",		kcm_op_add_ntlm_cred },
1725233294Sstas    { "HAVE_USER_CRED",		kcm_op_have_ntlm_cred },
1726233294Sstas    { "DEL_NTLM_CRED",		kcm_op_del_ntlm_cred },
1727233294Sstas    { "DO_NTLM_AUTH",		kcm_op_do_ntlm },
1728233294Sstas    { "GET_NTLM_USER_LIST",	kcm_op_get_ntlm_user_list }
1729178825Sdfr};
1730178825Sdfr
1731178825Sdfr
1732233294Sstasconst char *
1733233294Sstaskcm_op2string(kcm_operation opcode)
1734178825Sdfr{
1735178825Sdfr    if (opcode >= sizeof(kcm_ops)/sizeof(kcm_ops[0]))
1736178825Sdfr	return "Unknown operation";
1737178825Sdfr
1738178825Sdfr    return kcm_ops[opcode].name;
1739178825Sdfr}
1740178825Sdfr
1741178825Sdfrkrb5_error_code
1742178825Sdfrkcm_dispatch(krb5_context context,
1743178825Sdfr	     kcm_client *client,
1744178825Sdfr	     krb5_data *req_data,
1745178825Sdfr	     krb5_data *resp_data)
1746178825Sdfr{
1747178825Sdfr    krb5_error_code ret;
1748178825Sdfr    kcm_method method;
1749178825Sdfr    krb5_storage *req_sp = NULL;
1750178825Sdfr    krb5_storage *resp_sp = NULL;
1751178825Sdfr    uint16_t opcode;
1752178825Sdfr
1753178825Sdfr    resp_sp = krb5_storage_emem();
1754178825Sdfr    if (resp_sp == NULL) {
1755178825Sdfr	return ENOMEM;
1756178825Sdfr    }
1757178825Sdfr
1758178825Sdfr    if (client->pid == -1) {
1759178825Sdfr	kcm_log(0, "Client had invalid process number");
1760178825Sdfr	ret = KRB5_FCC_INTERNAL;
1761178825Sdfr	goto out;
1762178825Sdfr    }
1763178825Sdfr
1764178825Sdfr    req_sp = krb5_storage_from_data(req_data);
1765178825Sdfr    if (req_sp == NULL) {
1766178825Sdfr	kcm_log(0, "Process %d: failed to initialize storage from data",
1767178825Sdfr		client->pid);
1768178825Sdfr	ret = KRB5_CC_IO;
1769178825Sdfr	goto out;
1770178825Sdfr    }
1771178825Sdfr
1772178825Sdfr    ret = krb5_ret_uint16(req_sp, &opcode);
1773178825Sdfr    if (ret) {
1774178825Sdfr	kcm_log(0, "Process %d: didn't send a message", client->pid);
1775178825Sdfr	goto out;
1776178825Sdfr    }
1777178825Sdfr
1778178825Sdfr    if (opcode >= sizeof(kcm_ops)/sizeof(kcm_ops[0])) {
1779178825Sdfr	kcm_log(0, "Process %d: invalid operation code %d",
1780178825Sdfr		client->pid, opcode);
1781178825Sdfr	ret = KRB5_FCC_INTERNAL;
1782178825Sdfr	goto out;
1783178825Sdfr    }
1784178825Sdfr    method = kcm_ops[opcode].method;
1785233294Sstas    if (method == NULL) {
1786233294Sstas	kcm_log(0, "Process %d: operation code %s not implemented",
1787233294Sstas		client->pid, kcm_op2string(opcode));
1788233294Sstas	ret = KRB5_FCC_INTERNAL;
1789233294Sstas	goto out;
1790233294Sstas    }
1791178825Sdfr
1792178825Sdfr    /* seek past place for status code */
1793178825Sdfr    krb5_storage_seek(resp_sp, 4, SEEK_SET);
1794178825Sdfr
1795178825Sdfr    ret = (*method)(context, client, opcode, req_sp, resp_sp);
1796178825Sdfr
1797178825Sdfrout:
1798178825Sdfr    if (req_sp != NULL) {
1799178825Sdfr	krb5_storage_free(req_sp);
1800178825Sdfr    }
1801178825Sdfr
1802178825Sdfr    krb5_storage_seek(resp_sp, 0, SEEK_SET);
1803178825Sdfr    krb5_store_int32(resp_sp, ret);
1804178825Sdfr
1805178825Sdfr    ret = krb5_storage_to_data(resp_sp, resp_data);
1806178825Sdfr    krb5_storage_free(resp_sp);
1807178825Sdfr
1808178825Sdfr    return ret;
1809178825Sdfr}
1810178825Sdfr
1811