protocol.c revision 233294
133965Sjdp/*
278836Sobrien * Copyright (c) 2005, PADL Software Pty Ltd.
378836Sobrien * All rights reserved.
460514Sobrien *
533965Sjdp * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
633965Sjdp *
733965Sjdp * Redistribution and use in source and binary forms, with or without
833965Sjdp * modification, are permitted provided that the following conditions
933965Sjdp * are met:
1033965Sjdp *
1133965Sjdp * 1. Redistributions of source code must retain the above copyright
1233965Sjdp *    notice, this list of conditions and the following disclaimer.
1333965Sjdp *
1433965Sjdp * 2. Redistributions in binary form must reproduce the above copyright
1533965Sjdp *    notice, this list of conditions and the following disclaimer in the
1633965Sjdp *    documentation and/or other materials provided with the distribution.
1733965Sjdp *
1833965Sjdp * 3. Neither the name of PADL Software nor the names of its contributors
1933965Sjdp *    may be used to endorse or promote products derived from this software
2060514Sobrien *    without specific prior written permission.
2160514Sobrien *
2233965Sjdp * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
2359431Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2459431Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2533965Sjdp * ARE DISCLAIMED.  IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
2633965Sjdp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2733965Sjdp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2833965Sjdp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2933965Sjdp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3033965Sjdp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3133965Sjdp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3233965Sjdp * SUCH DAMAGE.
3333965Sjdp */
3433965Sjdp
3533965Sjdp#include "kcm_locl.h"
3633965Sjdp#include <heimntlm.h>
3733965Sjdp
3833965Sjdpstatic void
3933965Sjdpkcm_drop_default_cache(krb5_context context, kcm_client *client, char *name);
4033965Sjdp
4133965Sjdp
4233965Sjdpint
4333965Sjdpkcm_is_same_session(kcm_client *client, uid_t uid, pid_t session)
4433965Sjdp{
4533965Sjdp#if 0 /* XXX pppd is running in diffrent session the user */
4633965Sjdp    if (session != -1)
4733965Sjdp	return (client->session == session);
4833965Sjdp    else
4933965Sjdp#endif
5033965Sjdp	return  (client->uid == uid);
5133965Sjdp}
5233965Sjdp
5333965Sjdpstatic krb5_error_code
5433965Sjdpkcm_op_noop(krb5_context context,
5533965Sjdp	    kcm_client *client,
5633965Sjdp	    kcm_operation opcode,
5733965Sjdp	    krb5_storage *request,
5833965Sjdp	    krb5_storage *response)
5933965Sjdp{
6033965Sjdp    KCM_LOG_REQUEST(context, client, opcode);
6133965Sjdp
6233965Sjdp    return 0;
6333965Sjdp}
6433965Sjdp
6533965Sjdp/*
6633965Sjdp * Request:
6733965Sjdp *	NameZ
6833965Sjdp * Response:
6933965Sjdp *	NameZ
7033965Sjdp *
7133965Sjdp */
7233965Sjdpstatic krb5_error_code
7333965Sjdpkcm_op_get_name(krb5_context context,
7433965Sjdp		kcm_client *client,
7533965Sjdp		kcm_operation opcode,
7633965Sjdp		krb5_storage *request,
7733965Sjdp		krb5_storage *response)
7833965Sjdp
7933965Sjdp{
8033965Sjdp    krb5_error_code ret;
8133965Sjdp    char *name = NULL;
8233965Sjdp    kcm_ccache ccache;
8333965Sjdp
8433965Sjdp    ret = krb5_ret_stringz(request, &name);
8533965Sjdp    if (ret)
8633965Sjdp	return ret;
8733965Sjdp
8833965Sjdp    KCM_LOG_REQUEST_NAME(context, client, opcode, name);
8933965Sjdp
9033965Sjdp    ret = kcm_ccache_resolve_client(context, client, opcode,
9133965Sjdp				    name, &ccache);
9233965Sjdp    if (ret) {
9333965Sjdp	free(name);
9433965Sjdp	return ret;
9533965Sjdp    }
9633965Sjdp
9733965Sjdp    ret = krb5_store_stringz(response, ccache->name);
9833965Sjdp    if (ret) {
9933965Sjdp	kcm_release_ccache(context, ccache);
10033965Sjdp	free(name);
10133965Sjdp	return ret;
10233965Sjdp    }
10333965Sjdp
10433965Sjdp    free(name);
10533965Sjdp    kcm_release_ccache(context, ccache);
10633965Sjdp    return 0;
10733965Sjdp}
10833965Sjdp
10933965Sjdp/*
11033965Sjdp * Request:
11133965Sjdp *
11233965Sjdp * Response:
11333965Sjdp *	NameZ
11433965Sjdp */
11533965Sjdpstatic krb5_error_code
11633965Sjdpkcm_op_gen_new(krb5_context context,
11733965Sjdp	       kcm_client *client,
11833965Sjdp	       kcm_operation opcode,
11933965Sjdp	       krb5_storage *request,
12033965Sjdp	       krb5_storage *response)
12133965Sjdp{
12233965Sjdp    krb5_error_code ret;
12333965Sjdp    char *name;
12433965Sjdp
12533965Sjdp    KCM_LOG_REQUEST(context, client, opcode);
12633965Sjdp
12733965Sjdp    name = kcm_ccache_nextid(client->pid, client->uid, client->gid);
12833965Sjdp    if (name == NULL) {
12933965Sjdp	return KRB5_CC_NOMEM;
13033965Sjdp    }
13133965Sjdp
13233965Sjdp    ret = krb5_store_stringz(response, name);
13333965Sjdp    free(name);
13433965Sjdp
13533965Sjdp    return ret;
13633965Sjdp}
13733965Sjdp
13833965Sjdp/*
13933965Sjdp * Request:
14033965Sjdp *	NameZ
14133965Sjdp *	Principal
14233965Sjdp *
14333965Sjdp * Response:
14433965Sjdp *
14533965Sjdp */
14633965Sjdpstatic krb5_error_code
14733965Sjdpkcm_op_initialize(krb5_context context,
14833965Sjdp		  kcm_client *client,
14933965Sjdp		  kcm_operation opcode,
15033965Sjdp		  krb5_storage *request,
15133965Sjdp		  krb5_storage *response)
15233965Sjdp{
15333965Sjdp    kcm_ccache ccache;
15433965Sjdp    krb5_principal principal;
15533965Sjdp    krb5_error_code ret;
15633965Sjdp    char *name;
15789864Sobrien#if 0
15833965Sjdp    kcm_event event;
15933965Sjdp#endif
16033965Sjdp
16133965Sjdp    KCM_LOG_REQUEST(context, client, opcode);
16233965Sjdp
16333965Sjdp    ret = krb5_ret_stringz(request, &name);
16433965Sjdp    if (ret)
16533965Sjdp	return ret;
16633965Sjdp
16733965Sjdp    ret = krb5_ret_principal(request, &principal);
16833965Sjdp    if (ret) {
16933965Sjdp	free(name);
17033965Sjdp	return ret;
17133965Sjdp    }
17233965Sjdp
17333965Sjdp    ret = kcm_ccache_new_client(context, client, name, &ccache);
17433965Sjdp    if (ret) {
17533965Sjdp	free(name);
17633965Sjdp	krb5_free_principal(context, principal);
17733965Sjdp	return ret;
17833965Sjdp    }
17933965Sjdp
18033965Sjdp    ccache->client = principal;
18133965Sjdp
18233965Sjdp    free(name);
18333965Sjdp
18433965Sjdp#if 0
18533965Sjdp    /*
18633965Sjdp     * Create a new credentials cache. To mitigate DoS attacks we will
18733965Sjdp     * expire it in 30 minutes unless it has some credentials added
18833965Sjdp     * to it
18933965Sjdp     */
19033965Sjdp
19133965Sjdp    event.fire_time = 30 * 60;
19233965Sjdp    event.expire_time = 0;
19333965Sjdp    event.backoff_time = 0;
19433965Sjdp    event.action = KCM_EVENT_DESTROY_EMPTY_CACHE;
19533965Sjdp    event.ccache = ccache;
19633965Sjdp
19733965Sjdp    ret = kcm_enqueue_event_relative(context, &event);
19833965Sjdp#endif
19933965Sjdp
20033965Sjdp    kcm_release_ccache(context, ccache);
20133965Sjdp
20233965Sjdp    return ret;
20333965Sjdp}
20433965Sjdp
20533965Sjdp/*
20633965Sjdp * Request:
20733965Sjdp *	NameZ
20833965Sjdp *
20933965Sjdp * Response:
21033965Sjdp *
21133965Sjdp */
21233965Sjdpstatic krb5_error_code
21333965Sjdpkcm_op_destroy(krb5_context context,
21433965Sjdp	       kcm_client *client,
21533965Sjdp	       kcm_operation opcode,
21633965Sjdp	       krb5_storage *request,
21733965Sjdp	       krb5_storage *response)
21833965Sjdp{
21933965Sjdp    krb5_error_code ret;
22033965Sjdp    char *name;
22133965Sjdp
22233965Sjdp    ret = krb5_ret_stringz(request, &name);
22333965Sjdp    if (ret)
22433965Sjdp	return ret;
22533965Sjdp
22633965Sjdp    KCM_LOG_REQUEST_NAME(context, client, opcode, name);
22733965Sjdp
22833965Sjdp    ret = kcm_ccache_destroy_client(context, client, name);
22933965Sjdp    if (ret == 0)
23033965Sjdp	kcm_drop_default_cache(context, client, name);
23133965Sjdp
23233965Sjdp    free(name);
23333965Sjdp
23433965Sjdp    return ret;
23533965Sjdp}
23633965Sjdp
23733965Sjdp/*
23833965Sjdp * Request:
23933965Sjdp *	NameZ
24033965Sjdp *	Creds
24133965Sjdp *
24233965Sjdp * Response:
24333965Sjdp *
24433965Sjdp */
24533965Sjdpstatic krb5_error_code
24633965Sjdpkcm_op_store(krb5_context context,
24733965Sjdp	     kcm_client *client,
24833965Sjdp	     kcm_operation opcode,
24933965Sjdp	     krb5_storage *request,
25089864Sobrien	     krb5_storage *response)
25189864Sobrien{
25289864Sobrien    krb5_creds creds;
25389864Sobrien    krb5_error_code ret;
25489864Sobrien    kcm_ccache ccache;
25533965Sjdp    char *name;
25633965Sjdp
25733965Sjdp    ret = krb5_ret_stringz(request, &name);
25833965Sjdp    if (ret)
25933965Sjdp	return ret;
26038891Sjdp
26133965Sjdp    KCM_LOG_REQUEST_NAME(context, client, opcode, name);
26238891Sjdp
26338891Sjdp    ret = krb5_ret_creds(request, &creds);
26438891Sjdp    if (ret) {
26538891Sjdp	free(name);
26660514Sobrien	return ret;
26760514Sobrien    }
26833965Sjdp
26933965Sjdp    ret = kcm_ccache_resolve_client(context, client, opcode,
27033965Sjdp				    name, &ccache);
27177307Sobrien    if (ret) {
27233965Sjdp	free(name);
27333965Sjdp	krb5_free_cred_contents(context, &creds);
27433965Sjdp	return ret;
27533965Sjdp    }
27633965Sjdp
27733965Sjdp    ret = kcm_ccache_store_cred(context, ccache, &creds, 0);
27833965Sjdp    if (ret) {
27933965Sjdp	free(name);
28033965Sjdp	krb5_free_cred_contents(context, &creds);
28133965Sjdp	kcm_release_ccache(context, ccache);
28233965Sjdp	return ret;
28333965Sjdp    }
28433965Sjdp
28533965Sjdp    kcm_ccache_enqueue_default(context, ccache, &creds);
28633965Sjdp
28733965Sjdp    free(name);
28860514Sobrien    kcm_release_ccache(context, ccache);
28933965Sjdp
29033965Sjdp    return 0;
29133965Sjdp}
29233965Sjdp
29333965Sjdp/*
29433965Sjdp * Request:
29533965Sjdp *	NameZ
29633965Sjdp *	WhichFields
29733965Sjdp *	MatchCreds
29833965Sjdp *
29933965Sjdp * Response:
30033965Sjdp *	Creds
30133965Sjdp *
30289864Sobrien */
30389864Sobrienstatic krb5_error_code
30489864Sobrienkcm_op_retrieve(krb5_context context,
30568773Sobrien		kcm_client *client,
30668773Sobrien		kcm_operation opcode,
30768773Sobrien		krb5_storage *request,
30877307Sobrien		krb5_storage *response)
30977307Sobrien{
31077307Sobrien    uint32_t flags;
31168773Sobrien    krb5_creds mcreds;
31268773Sobrien    krb5_error_code ret;
31368773Sobrien    kcm_ccache ccache;
31468773Sobrien    char *name;
31568773Sobrien    krb5_creds *credp;
31668773Sobrien    int free_creds = 0;
31768773Sobrien
31868773Sobrien    ret = krb5_ret_stringz(request, &name);
31968773Sobrien    if (ret)
32068773Sobrien	return ret;
32168773Sobrien
32268773Sobrien    KCM_LOG_REQUEST_NAME(context, client, opcode, name);
32368773Sobrien
32468773Sobrien    ret = krb5_ret_uint32(request, &flags);
32568773Sobrien    if (ret) {
32668773Sobrien	free(name);
32768773Sobrien	return ret;
32868773Sobrien    }
32968773Sobrien
33089864Sobrien    ret = krb5_ret_creds_tag(request, &mcreds);
33189864Sobrien    if (ret) {
33268773Sobrien	free(name);
33368773Sobrien	return ret;
33433965Sjdp    }
33533965Sjdp
33668773Sobrien    if (disallow_getting_krbtgt &&
33733965Sjdp	mcreds.server->name.name_string.len == 2 &&
33833965Sjdp	strcmp(mcreds.server->name.name_string.val[0], KRB5_TGS_NAME) == 0)
33933965Sjdp    {
34033965Sjdp	free(name);
34133965Sjdp	krb5_free_cred_contents(context, &mcreds);
34233965Sjdp	return KRB5_FCC_PERM;
34333965Sjdp    }
34433965Sjdp
34533965Sjdp    ret = kcm_ccache_resolve_client(context, client, opcode,
34633965Sjdp				    name, &ccache);
34733965Sjdp    if (ret) {
34833965Sjdp	free(name);
34933965Sjdp	krb5_free_cred_contents(context, &mcreds);
35033965Sjdp	return ret;
35133965Sjdp    }
35233965Sjdp
35333965Sjdp    ret = kcm_ccache_retrieve_cred(context, ccache, flags,
35433965Sjdp				   &mcreds, &credp);
35533965Sjdp    if (ret && ((flags & KRB5_GC_CACHED) == 0) &&
35633965Sjdp	!krb5_is_config_principal(context, mcreds.server)) {
35733965Sjdp	krb5_ccache_data ccdata;
35833965Sjdp
35933965Sjdp	/* try and acquire */
36033965Sjdp	HEIMDAL_MUTEX_lock(&ccache->mutex);
36133965Sjdp
36233965Sjdp	/* Fake up an internal ccache */
36333965Sjdp	kcm_internal_ccache(context, ccache, &ccdata);
36433965Sjdp
36533965Sjdp	/* glue cc layer will store creds */
36633965Sjdp	ret = krb5_get_credentials(context, 0, &ccdata, &mcreds, &credp);
36733965Sjdp	if (ret == 0)
36833965Sjdp	    free_creds = 1;
36960514Sobrien
37033965Sjdp	HEIMDAL_MUTEX_unlock(&ccache->mutex);
37133965Sjdp    }
37233965Sjdp
37333965Sjdp    if (ret == 0) {
37433965Sjdp	ret = krb5_store_creds(response, credp);
37533965Sjdp    }
37633965Sjdp
37733965Sjdp    free(name);
37833965Sjdp    krb5_free_cred_contents(context, &mcreds);
37933965Sjdp    kcm_release_ccache(context, ccache);
38033965Sjdp
38133965Sjdp    if (free_creds)
38233965Sjdp	krb5_free_cred_contents(context, credp);
38333965Sjdp
38433965Sjdp    return ret;
38533965Sjdp}
38633965Sjdp
38733965Sjdp/*
38833965Sjdp * Request:
38933965Sjdp *	NameZ
39033965Sjdp *
39133965Sjdp * Response:
39233965Sjdp *	Principal
39333965Sjdp */
39460514Sobrienstatic krb5_error_code
39533965Sjdpkcm_op_get_principal(krb5_context context,
39633965Sjdp		     kcm_client *client,
39733965Sjdp		     kcm_operation opcode,
39833965Sjdp		     krb5_storage *request,
39989864Sobrien		     krb5_storage *response)
40089864Sobrien{
40133965Sjdp    krb5_error_code ret;
40233965Sjdp    kcm_ccache ccache;
40333965Sjdp    char *name;
40433965Sjdp
40533965Sjdp    ret = krb5_ret_stringz(request, &name);
40633965Sjdp    if (ret)
40733965Sjdp	return ret;
40833965Sjdp
40960514Sobrien    KCM_LOG_REQUEST_NAME(context, client, opcode, name);
41060514Sobrien
41160514Sobrien    ret = kcm_ccache_resolve_client(context, client, opcode,
41289864Sobrien				    name, &ccache);
41389864Sobrien    if (ret) {
41489864Sobrien	free(name);
41560514Sobrien	return ret;
41660514Sobrien    }
41760514Sobrien
41833965Sjdp    if (ccache->client == NULL)
41933965Sjdp	ret = KRB5_CC_NOTFOUND;
42033965Sjdp    else
42133965Sjdp	ret = krb5_store_principal(response, ccache->client);
42233965Sjdp
42333965Sjdp    free(name);
42433965Sjdp    kcm_release_ccache(context, ccache);
42533965Sjdp
42689864Sobrien    return 0;
42777307Sobrien}
42833965Sjdp
42933965Sjdp/*
43033965Sjdp * Request:
43133965Sjdp *	NameZ
43233965Sjdp *
43333965Sjdp * Response:
43433965Sjdp *	UUIDs
43533965Sjdp *
43633965Sjdp */
43733965Sjdpstatic krb5_error_code
43833965Sjdpkcm_op_get_cred_uuid_list(krb5_context context,
43933965Sjdp			  kcm_client *client,
44033965Sjdp			  kcm_operation opcode,
44133965Sjdp			  krb5_storage *request,
44233965Sjdp			  krb5_storage *response)
44377307Sobrien{
44477307Sobrien    struct kcm_creds *creds;
44577307Sobrien    krb5_error_code ret;
44677307Sobrien    kcm_ccache ccache;
44777307Sobrien    char *name;
44877307Sobrien
44977307Sobrien    ret = krb5_ret_stringz(request, &name);
45077307Sobrien    if (ret)
45177307Sobrien	return ret;
45277307Sobrien
45377307Sobrien    KCM_LOG_REQUEST_NAME(context, client, opcode, name);
45433965Sjdp
45533965Sjdp    ret = kcm_ccache_resolve_client(context, client, opcode,
45633965Sjdp				    name, &ccache);
45733965Sjdp    free(name);
45833965Sjdp    if (ret)
45933965Sjdp	return ret;
46033965Sjdp
46133965Sjdp    for (creds = ccache->creds ; creds ; creds = creds->next) {
46233965Sjdp	ssize_t sret;
46333965Sjdp	sret = krb5_storage_write(response, &creds->uuid, sizeof(creds->uuid));
46433965Sjdp	if (sret != sizeof(creds->uuid)) {
46533965Sjdp	    ret = ENOMEM;
46633965Sjdp	    break;
46789864Sobrien	}
46833965Sjdp    }
46933965Sjdp
47033965Sjdp    kcm_release_ccache(context, ccache);
47133965Sjdp
47233965Sjdp    return ret;
47333965Sjdp}
47433965Sjdp
47533965Sjdp/*
47633965Sjdp * Request:
47733965Sjdp *	NameZ
47833965Sjdp *	Cursor
47933965Sjdp *
48033965Sjdp * Response:
48133965Sjdp *	Creds
48233965Sjdp */
48333965Sjdpstatic krb5_error_code
48433965Sjdpkcm_op_get_cred_by_uuid(krb5_context context,
48533965Sjdp			kcm_client *client,
48633965Sjdp			kcm_operation opcode,
48733965Sjdp			krb5_storage *request,
48833965Sjdp			krb5_storage *response)
48933965Sjdp{
49033965Sjdp    krb5_error_code ret;
49133965Sjdp    kcm_ccache ccache;
49233965Sjdp    char *name;
49333965Sjdp    struct kcm_creds *c;
49433965Sjdp    kcmuuid_t uuid;
49533965Sjdp    ssize_t sret;
49633965Sjdp
49733965Sjdp    ret = krb5_ret_stringz(request, &name);
49877307Sobrien    if (ret)
49977307Sobrien	return ret;
50077307Sobrien
50177307Sobrien    KCM_LOG_REQUEST_NAME(context, client, opcode, name);
50277307Sobrien
50377307Sobrien    ret = kcm_ccache_resolve_client(context, client, opcode,
50477307Sobrien				    name, &ccache);
50577307Sobrien    free(name);
50677307Sobrien    if (ret)
50777307Sobrien	return ret;
50877307Sobrien
50933965Sjdp    sret = krb5_storage_read(request, &uuid, sizeof(uuid));
51060514Sobrien    if (sret != sizeof(uuid)) {
51133965Sjdp	kcm_release_ccache(context, ccache);
51233965Sjdp	krb5_clear_error_message(context);
51333965Sjdp	return KRB5_CC_IO;
51433965Sjdp    }
51533965Sjdp
51633965Sjdp    c = kcm_ccache_find_cred_uuid(context, ccache, uuid);
51733965Sjdp    if (c == NULL) {
51833965Sjdp	kcm_release_ccache(context, ccache);
51933965Sjdp	return KRB5_CC_END;
52033965Sjdp    }
52133965Sjdp
52233965Sjdp    HEIMDAL_MUTEX_lock(&ccache->mutex);
52333965Sjdp    ret = krb5_store_creds(response, &c->cred);
52433965Sjdp    HEIMDAL_MUTEX_unlock(&ccache->mutex);
52533965Sjdp
52633965Sjdp    kcm_release_ccache(context, ccache);
52733965Sjdp
52833965Sjdp    return ret;
52933965Sjdp}
53033965Sjdp
53133965Sjdp/*
53233965Sjdp * Request:
53333965Sjdp *	NameZ
53433965Sjdp *	WhichFields
53533965Sjdp *	MatchCreds
53633965Sjdp *
53733965Sjdp * Response:
53833965Sjdp *
53933965Sjdp */
54033965Sjdpstatic krb5_error_code
54133965Sjdpkcm_op_remove_cred(krb5_context context,
54233965Sjdp		   kcm_client *client,
54333965Sjdp		   kcm_operation opcode,
54433965Sjdp		   krb5_storage *request,
54533965Sjdp		   krb5_storage *response)
54633965Sjdp{
54733965Sjdp    uint32_t whichfields;
54833965Sjdp    krb5_creds mcreds;
54960514Sobrien    krb5_error_code ret;
55033965Sjdp    kcm_ccache ccache;
55133965Sjdp    char *name;
55233965Sjdp
55333965Sjdp    ret = krb5_ret_stringz(request, &name);
55433965Sjdp    if (ret)
55533965Sjdp	return ret;
55633965Sjdp
55733965Sjdp    KCM_LOG_REQUEST_NAME(context, client, opcode, name);
55833965Sjdp
55933965Sjdp    ret = krb5_ret_uint32(request, &whichfields);
56033965Sjdp    if (ret) {
56133965Sjdp	free(name);
56233965Sjdp	return ret;
56333965Sjdp    }
56433965Sjdp
56533965Sjdp    ret = krb5_ret_creds_tag(request, &mcreds);
56633965Sjdp    if (ret) {
56733965Sjdp	free(name);
56833965Sjdp	return ret;
56933965Sjdp    }
57033965Sjdp
57133965Sjdp    ret = kcm_ccache_resolve_client(context, client, opcode,
57233965Sjdp				    name, &ccache);
57333965Sjdp    if (ret) {
57433965Sjdp	free(name);
57533965Sjdp	krb5_free_cred_contents(context, &mcreds);
57633965Sjdp	return ret;
57733965Sjdp    }
57833965Sjdp
57933965Sjdp    ret = kcm_ccache_remove_cred(context, ccache, whichfields, &mcreds);
58033965Sjdp
58133965Sjdp    /* XXX need to remove any events that match */
58233965Sjdp
58333965Sjdp    free(name);
58433965Sjdp    krb5_free_cred_contents(context, &mcreds);
58533965Sjdp    kcm_release_ccache(context, ccache);
58633965Sjdp
58733965Sjdp    return ret;
58833965Sjdp}
58933965Sjdp
59033965Sjdp/*
59133965Sjdp * Request:
59233965Sjdp *	NameZ
59333965Sjdp *	Flags
59433965Sjdp *
59533965Sjdp * Response:
59633965Sjdp *
59733965Sjdp */
59833965Sjdpstatic krb5_error_code
59933965Sjdpkcm_op_set_flags(krb5_context context,
60038891Sjdp		 kcm_client *client,
60138891Sjdp		 kcm_operation opcode,
60238891Sjdp		 krb5_storage *request,
60338891Sjdp		 krb5_storage *response)
60438891Sjdp{
60533965Sjdp    uint32_t flags;
60633965Sjdp    krb5_error_code ret;
60733965Sjdp    kcm_ccache ccache;
60833965Sjdp    char *name;
60938891Sjdp
61038891Sjdp    ret = krb5_ret_stringz(request, &name);
61138891Sjdp    if (ret)
61238891Sjdp	return ret;
61338891Sjdp
61433965Sjdp    KCM_LOG_REQUEST_NAME(context, client, opcode, name);
61533965Sjdp
61633965Sjdp    ret = krb5_ret_uint32(request, &flags);
61733965Sjdp    if (ret) {
61833965Sjdp	free(name);
61933965Sjdp	return ret;
62033965Sjdp    }
62133965Sjdp
62233965Sjdp    ret = kcm_ccache_resolve_client(context, client, opcode,
62333965Sjdp				    name, &ccache);
62433965Sjdp    if (ret) {
62533965Sjdp	free(name);
62633965Sjdp	return ret;
62733965Sjdp    }
62833965Sjdp
62933965Sjdp    /* we don't really support any flags yet */
63033965Sjdp    free(name);
63133965Sjdp    kcm_release_ccache(context, ccache);
63233965Sjdp
63333965Sjdp    return 0;
63433965Sjdp}
63533965Sjdp
63633965Sjdp/*
63733965Sjdp * Request:
63833965Sjdp *	NameZ
63933965Sjdp *	UID
64033965Sjdp *	GID
64133965Sjdp *
64233965Sjdp * Response:
64333965Sjdp *
64433965Sjdp */
64533965Sjdpstatic krb5_error_code
64633965Sjdpkcm_op_chown(krb5_context context,
64733965Sjdp	     kcm_client *client,
64833965Sjdp	     kcm_operation opcode,
64933965Sjdp	     krb5_storage *request,
65033965Sjdp	     krb5_storage *response)
65133965Sjdp{
65233965Sjdp    uint32_t uid;
65333965Sjdp    uint32_t gid;
65438891Sjdp    krb5_error_code ret;
65538891Sjdp    kcm_ccache ccache;
65638891Sjdp    char *name;
65733965Sjdp
65833965Sjdp    ret = krb5_ret_stringz(request, &name);
65933965Sjdp    if (ret)
66033965Sjdp	return ret;
66133965Sjdp
66233965Sjdp    KCM_LOG_REQUEST_NAME(context, client, opcode, name);
66333965Sjdp
66433965Sjdp    ret = krb5_ret_uint32(request, &uid);
66533965Sjdp    if (ret) {
66633965Sjdp	free(name);
66733965Sjdp	return ret;
66833965Sjdp    }
66933965Sjdp
67033965Sjdp    ret = krb5_ret_uint32(request, &gid);
67133965Sjdp    if (ret) {
67233965Sjdp	free(name);
67333965Sjdp	return ret;
67433965Sjdp    }
67533965Sjdp
67633965Sjdp    ret = kcm_ccache_resolve_client(context, client, opcode,
67733965Sjdp				    name, &ccache);
67833965Sjdp    if (ret) {
67933965Sjdp	free(name);
68033965Sjdp	return ret;
68133965Sjdp    }
68233965Sjdp
68333965Sjdp    ret = kcm_chown(context, client, ccache, uid, gid);
68433965Sjdp
68533965Sjdp    free(name);
68633965Sjdp    kcm_release_ccache(context, ccache);
68733965Sjdp
68833965Sjdp    return ret;
68933965Sjdp}
69033965Sjdp
69133965Sjdp/*
69233965Sjdp * Request:
69333965Sjdp *	NameZ
69433965Sjdp *	Mode
69533965Sjdp *
69633965Sjdp * Response:
69733965Sjdp *
69833965Sjdp */
69933965Sjdpstatic krb5_error_code
70033965Sjdpkcm_op_chmod(krb5_context context,
70133965Sjdp	     kcm_client *client,
70233965Sjdp	     kcm_operation opcode,
70333965Sjdp	     krb5_storage *request,
70433965Sjdp	     krb5_storage *response)
70533965Sjdp{
70633965Sjdp    uint16_t mode;
70733965Sjdp    krb5_error_code ret;
70833965Sjdp    kcm_ccache ccache;
70933965Sjdp    char *name;
71033965Sjdp
71133965Sjdp    ret = krb5_ret_stringz(request, &name);
71233965Sjdp    if (ret)
71333965Sjdp	return ret;
71433965Sjdp
71533965Sjdp    KCM_LOG_REQUEST_NAME(context, client, opcode, name);
71633965Sjdp
71733965Sjdp    ret = krb5_ret_uint16(request, &mode);
71833965Sjdp    if (ret) {
71933965Sjdp	free(name);
72033965Sjdp	return ret;
72133965Sjdp    }
72233965Sjdp
72333965Sjdp    ret = kcm_ccache_resolve_client(context, client, opcode,
72433965Sjdp				    name, &ccache);
72533965Sjdp    if (ret) {
72633965Sjdp	free(name);
72733965Sjdp	return ret;
72833965Sjdp    }
72933965Sjdp
73033965Sjdp    ret = kcm_chmod(context, client, ccache, mode);
73133965Sjdp
73233965Sjdp    free(name);
73333965Sjdp    kcm_release_ccache(context, ccache);
73433965Sjdp
73533965Sjdp    return ret;
73633965Sjdp}
73733965Sjdp
73833965Sjdp/*
73933965Sjdp * Protocol extensions for moving ticket acquisition responsibility
74033965Sjdp * from client to KCM follow.
74133965Sjdp */
74233965Sjdp
74333965Sjdp/*
74433965Sjdp * Request:
74533965Sjdp *	NameZ
74633965Sjdp *	ServerPrincipalPresent
74733965Sjdp *	ServerPrincipal OPTIONAL
74833965Sjdp *	Key
74933965Sjdp *
75033965Sjdp * Repsonse:
75133965Sjdp *
75233965Sjdp */
75333965Sjdpstatic krb5_error_code
75433965Sjdpkcm_op_get_initial_ticket(krb5_context context,
75533965Sjdp			  kcm_client *client,
75633965Sjdp			  kcm_operation opcode,
75733965Sjdp			  krb5_storage *request,
75833965Sjdp			  krb5_storage *response)
75933965Sjdp{
76033965Sjdp    krb5_error_code ret;
76133965Sjdp    kcm_ccache ccache;
76233965Sjdp    char *name;
76333965Sjdp    int8_t not_tgt = 0;
76433965Sjdp    krb5_principal server = NULL;
76533965Sjdp    krb5_keyblock key;
76633965Sjdp
76733965Sjdp    krb5_keyblock_zero(&key);
76833965Sjdp
76933965Sjdp    ret = krb5_ret_stringz(request, &name);
77033965Sjdp    if (ret)
77133965Sjdp	return ret;
77233965Sjdp
77333965Sjdp    KCM_LOG_REQUEST_NAME(context, client, opcode, name);
77433965Sjdp
77533965Sjdp    ret = krb5_ret_int8(request, &not_tgt);
77633965Sjdp    if (ret) {
77733965Sjdp	free(name);
77833965Sjdp	return ret;
77933965Sjdp    }
78033965Sjdp
78133965Sjdp    if (not_tgt) {
78233965Sjdp	ret = krb5_ret_principal(request, &server);
78333965Sjdp	if (ret) {
78433965Sjdp	    free(name);
78533965Sjdp	    return ret;
78633965Sjdp	}
78733965Sjdp    }
78833965Sjdp
78933965Sjdp    ret = krb5_ret_keyblock(request, &key);
79033965Sjdp    if (ret) {
79133965Sjdp	free(name);
79233965Sjdp	if (server != NULL)
79333965Sjdp	    krb5_free_principal(context, server);
79433965Sjdp	return ret;
79533965Sjdp    }
79633965Sjdp
79733965Sjdp    ret = kcm_ccache_resolve_client(context, client, opcode,
79833965Sjdp				    name, &ccache);
79933965Sjdp    if (ret == 0) {
80033965Sjdp	HEIMDAL_MUTEX_lock(&ccache->mutex);
80133965Sjdp
80233965Sjdp	if (ccache->server != NULL) {
80333965Sjdp	    krb5_free_principal(context, ccache->server);
80433965Sjdp	    ccache->server = NULL;
80533965Sjdp	}
80633965Sjdp
80733965Sjdp	krb5_free_keyblock(context, &ccache->key.keyblock);
80833965Sjdp
80933965Sjdp	ccache->server = server;
81033965Sjdp	ccache->key.keyblock = key;
81133965Sjdp    	ccache->flags |= KCM_FLAGS_USE_CACHED_KEY;
81233965Sjdp
81333965Sjdp	ret = kcm_ccache_enqueue_default(context, ccache, NULL);
81433965Sjdp	if (ret) {
81533965Sjdp	    ccache->server = NULL;
81633965Sjdp	    krb5_keyblock_zero(&ccache->key.keyblock);
81733965Sjdp	    ccache->flags &= ~(KCM_FLAGS_USE_CACHED_KEY);
81833965Sjdp	}
81933965Sjdp
82033965Sjdp	HEIMDAL_MUTEX_unlock(&ccache->mutex);
82133965Sjdp    }
82233965Sjdp
82333965Sjdp    free(name);
82433965Sjdp
82533965Sjdp    if (ret != 0) {
82633965Sjdp	krb5_free_principal(context, server);
82733965Sjdp	krb5_free_keyblock(context, &key);
82833965Sjdp    }
82933965Sjdp
83033965Sjdp    kcm_release_ccache(context, ccache);
83133965Sjdp
83233965Sjdp    return ret;
83333965Sjdp}
83433965Sjdp
83533965Sjdp/*
83633965Sjdp * Request:
83733965Sjdp *	NameZ
83833965Sjdp *	ServerPrincipal
83933965Sjdp *	KDCFlags
84033965Sjdp *	EncryptionType
84133965Sjdp *
84233965Sjdp * Repsonse:
84333965Sjdp *
84433965Sjdp */
84533965Sjdpstatic krb5_error_code
84633965Sjdpkcm_op_get_ticket(krb5_context context,
84733965Sjdp		  kcm_client *client,
84833965Sjdp		  kcm_operation opcode,
84933965Sjdp		  krb5_storage *request,
85033965Sjdp		  krb5_storage *response)
85133965Sjdp{
85233965Sjdp    krb5_error_code ret;
85333965Sjdp    kcm_ccache ccache;
85433965Sjdp    char *name;
85533965Sjdp    krb5_principal server = NULL;
85633965Sjdp    krb5_ccache_data ccdata;
85733965Sjdp    krb5_creds in, *out;
85833965Sjdp    krb5_kdc_flags flags;
85933965Sjdp
86038891Sjdp    memset(&in, 0, sizeof(in));
86133965Sjdp
86233965Sjdp    ret = krb5_ret_stringz(request, &name);
86333965Sjdp    if (ret)
86433965Sjdp	return ret;
86533965Sjdp
86633965Sjdp    KCM_LOG_REQUEST_NAME(context, client, opcode, name);
86733965Sjdp
86833965Sjdp    ret = krb5_ret_uint32(request, &flags.i);
86933965Sjdp    if (ret) {
87033965Sjdp	free(name);
87133965Sjdp	return ret;
87233965Sjdp    }
87333965Sjdp
87433965Sjdp    ret = krb5_ret_int32(request, &in.session.keytype);
87533965Sjdp    if (ret) {
87633965Sjdp	free(name);
87733965Sjdp	return ret;
87833965Sjdp    }
87933965Sjdp
88033965Sjdp    ret = krb5_ret_principal(request, &server);
88133965Sjdp    if (ret) {
88233965Sjdp	free(name);
88333965Sjdp	return ret;
88433965Sjdp    }
88533965Sjdp
88633965Sjdp    ret = kcm_ccache_resolve_client(context, client, opcode,
88733965Sjdp				    name, &ccache);
88833965Sjdp    if (ret) {
88933965Sjdp	krb5_free_principal(context, server);
89033965Sjdp	free(name);
89133965Sjdp	return ret;
89233965Sjdp    }
89333965Sjdp
89433965Sjdp    HEIMDAL_MUTEX_lock(&ccache->mutex);
89533965Sjdp
89633965Sjdp    /* Fake up an internal ccache */
89733965Sjdp    kcm_internal_ccache(context, ccache, &ccdata);
89833965Sjdp
89933965Sjdp    in.client = ccache->client;
90033965Sjdp    in.server = server;
90133965Sjdp    in.times.endtime = 0;
90233965Sjdp
90333965Sjdp    /* glue cc layer will store creds */
90433965Sjdp    ret = krb5_get_credentials_with_flags(context, 0, flags,
90533965Sjdp					  &ccdata, &in, &out);
90633965Sjdp
90733965Sjdp    HEIMDAL_MUTEX_unlock(&ccache->mutex);
90833965Sjdp
90933965Sjdp    krb5_free_principal(context, server);
91033965Sjdp
91133965Sjdp    if (ret == 0)
91233965Sjdp	krb5_free_cred_contents(context, out);
91333965Sjdp
91433965Sjdp    kcm_release_ccache(context, ccache);
91533965Sjdp    free(name);
91633965Sjdp
91733965Sjdp    return ret;
91833965Sjdp}
91933965Sjdp
92033965Sjdp/*
92133965Sjdp * Request:
92233965Sjdp *	OldNameZ
92333965Sjdp *	NewNameZ
92433965Sjdp *
92533965Sjdp * Repsonse:
92633965Sjdp *
92733965Sjdp */
92833965Sjdpstatic krb5_error_code
92933965Sjdpkcm_op_move_cache(krb5_context context,
93033965Sjdp		  kcm_client *client,
93133965Sjdp		  kcm_operation opcode,
93233965Sjdp		  krb5_storage *request,
93333965Sjdp		  krb5_storage *response)
93433965Sjdp{
93533965Sjdp    krb5_error_code ret;
93633965Sjdp    kcm_ccache oldid, newid;
93733965Sjdp    char *oldname, *newname;
93833965Sjdp
93933965Sjdp    ret = krb5_ret_stringz(request, &oldname);
94033965Sjdp    if (ret)
94133965Sjdp	return ret;
94233965Sjdp
94333965Sjdp    KCM_LOG_REQUEST_NAME(context, client, opcode, oldname);
94433965Sjdp
94533965Sjdp    ret = krb5_ret_stringz(request, &newname);
94633965Sjdp    if (ret) {
94733965Sjdp	free(oldname);
94833965Sjdp	return ret;
94933965Sjdp    }
95033965Sjdp
95133965Sjdp    /* move to ourself is simple, done! */
95233965Sjdp    if (strcmp(oldname, newname) == 0) {
95333965Sjdp	free(oldname);
95433965Sjdp	free(newname);
95589864Sobrien	return 0;
95633965Sjdp    }
95733965Sjdp
95833965Sjdp    ret = kcm_ccache_resolve_client(context, client, opcode, oldname, &oldid);
95933965Sjdp    if (ret) {
96033965Sjdp	free(oldname);
96160514Sobrien	free(newname);
96233965Sjdp	return ret;
96333965Sjdp    }
96433965Sjdp
96533965Sjdp    /* Check if new credential cache exists, if not create one. */
96633965Sjdp    ret = kcm_ccache_resolve_client(context, client, opcode, newname, &newid);
96733965Sjdp    if (ret == KRB5_FCC_NOFILE)
96833965Sjdp	ret = kcm_ccache_new_client(context, client, newname, &newid);
96933965Sjdp    free(newname);
97033965Sjdp
97133965Sjdp    if (ret) {
97260514Sobrien	free(oldname);
97333965Sjdp	kcm_release_ccache(context, oldid);
97433965Sjdp	return ret;
97533965Sjdp    }
97689864Sobrien
97789864Sobrien    HEIMDAL_MUTEX_lock(&oldid->mutex);
97889864Sobrien    HEIMDAL_MUTEX_lock(&newid->mutex);
97933965Sjdp
98033965Sjdp    /* move content */
98133965Sjdp    {
98233965Sjdp	kcm_ccache_data tmp;
98333965Sjdp
98433965Sjdp#define MOVE(n,o,f) { tmp.f = n->f ; n->f = o->f; o->f = tmp.f; }
98533965Sjdp
98633965Sjdp	MOVE(newid, oldid, flags);
98733965Sjdp	MOVE(newid, oldid, client);
98833965Sjdp	MOVE(newid, oldid, server);
98933965Sjdp	MOVE(newid, oldid, creds);
99033965Sjdp	MOVE(newid, oldid, tkt_life);
99133965Sjdp	MOVE(newid, oldid, renew_life);
99233965Sjdp	MOVE(newid, oldid, key);
99333965Sjdp	MOVE(newid, oldid, kdc_offset);
99433965Sjdp#undef MOVE
99533965Sjdp    }
99633965Sjdp
99733965Sjdp    HEIMDAL_MUTEX_unlock(&oldid->mutex);
99833965Sjdp    HEIMDAL_MUTEX_unlock(&newid->mutex);
99933965Sjdp
100033965Sjdp    kcm_release_ccache(context, oldid);
100133965Sjdp    kcm_release_ccache(context, newid);
100233965Sjdp
100333965Sjdp    ret = kcm_ccache_destroy_client(context, client, oldname);
100433965Sjdp    if (ret == 0)
100533965Sjdp	kcm_drop_default_cache(context, client, oldname);
100633965Sjdp
100733965Sjdp    free(oldname);
100833965Sjdp
100933965Sjdp    return ret;
101033965Sjdp}
101133965Sjdp
101233965Sjdpstatic krb5_error_code
101333965Sjdpkcm_op_get_cache_uuid_list(krb5_context context,
101433965Sjdp			   kcm_client *client,
101533965Sjdp			   kcm_operation opcode,
101633965Sjdp			   krb5_storage *request,
101733965Sjdp			   krb5_storage *response)
101833965Sjdp{
101933965Sjdp    KCM_LOG_REQUEST(context, client, opcode);
102033965Sjdp
102133965Sjdp    return kcm_ccache_get_uuids(context, client, opcode, response);
102233965Sjdp}
102333965Sjdp
102433965Sjdpstatic krb5_error_code
102533965Sjdpkcm_op_get_cache_by_uuid(krb5_context context,
102633965Sjdp			 kcm_client *client,
102733965Sjdp			 kcm_operation opcode,
102833965Sjdp			 krb5_storage *request,
102933965Sjdp			 krb5_storage *response)
103033965Sjdp{
103133965Sjdp    krb5_error_code ret;
103233965Sjdp    kcmuuid_t uuid;
103333965Sjdp    ssize_t sret;
103433965Sjdp    kcm_ccache cache;
103533965Sjdp
103633965Sjdp    KCM_LOG_REQUEST(context, client, opcode);
103733965Sjdp
103833965Sjdp    sret = krb5_storage_read(request, &uuid, sizeof(uuid));
103933965Sjdp    if (sret != sizeof(uuid)) {
104033965Sjdp	krb5_clear_error_message(context);
104133965Sjdp	return KRB5_CC_IO;
104233965Sjdp    }
104333965Sjdp
104433965Sjdp    ret = kcm_ccache_resolve_by_uuid(context, uuid, &cache);
104533965Sjdp    if (ret)
104633965Sjdp	return ret;
104733965Sjdp
104833965Sjdp    ret = kcm_access(context, client, opcode, cache);
104933965Sjdp    if (ret)
105033965Sjdp	ret = KRB5_FCC_NOFILE;
105133965Sjdp
105233965Sjdp    if (ret == 0)
105333965Sjdp	ret = krb5_store_stringz(response, cache->name);
105433965Sjdp
105533965Sjdp    kcm_release_ccache(context, cache);
105633965Sjdp
105733965Sjdp    return ret;
105833965Sjdp}
105933965Sjdp
106033965Sjdpstruct kcm_default_cache *default_caches;
106133965Sjdp
106233965Sjdpstatic krb5_error_code
106333965Sjdpkcm_op_get_default_cache(krb5_context context,
106433965Sjdp			 kcm_client *client,
106533965Sjdp			 kcm_operation opcode,
106633965Sjdp			 krb5_storage *request,
106733965Sjdp			 krb5_storage *response)
106833965Sjdp{
106933965Sjdp    struct kcm_default_cache *c;
107033965Sjdp    krb5_error_code ret;
107133965Sjdp    const char *name = NULL;
107233965Sjdp    char *n = NULL;
107333965Sjdp
107433965Sjdp    KCM_LOG_REQUEST(context, client, opcode);
107533965Sjdp
107633965Sjdp    for (c = default_caches; c != NULL; c = c->next) {
107733965Sjdp	if (kcm_is_same_session(client, c->uid, c->session)) {
107833965Sjdp	    name = c->name;
107933965Sjdp	    break;
108033965Sjdp	}
108133965Sjdp    }
108233965Sjdp    if (name == NULL)
108333965Sjdp	name = n = kcm_ccache_first_name(client);
108433965Sjdp
108533965Sjdp    if (name == NULL) {
108633965Sjdp	asprintf(&n, "%d", (int)client->uid);
108733965Sjdp	name = n;
108833965Sjdp    }
108933965Sjdp    if (name == NULL)
109033965Sjdp	return ENOMEM;
109133965Sjdp    ret = krb5_store_stringz(response, name);
109233965Sjdp    if (n)
109333965Sjdp	free(n);
109433965Sjdp    return ret;
109533965Sjdp}
109633965Sjdp
109733965Sjdpstatic void
109833965Sjdpkcm_drop_default_cache(krb5_context context, kcm_client *client, char *name)
109933965Sjdp{
110033965Sjdp    struct kcm_default_cache **c;
110133965Sjdp
110233965Sjdp    for (c = &default_caches; *c != NULL; c = &(*c)->next) {
110333965Sjdp	if (!kcm_is_same_session(client, (*c)->uid, (*c)->session))
110433965Sjdp	    continue;
110533965Sjdp	if (strcmp((*c)->name, name) == 0) {
110633965Sjdp	    struct kcm_default_cache *h = *c;
110733965Sjdp	    *c = (*c)->next;
110833965Sjdp	    free(h->name);
110933965Sjdp	    free(h);
111033965Sjdp	    break;
111133965Sjdp	}
111233965Sjdp    }
111333965Sjdp}
111433965Sjdp
111533965Sjdpstatic krb5_error_code
111633965Sjdpkcm_op_set_default_cache(krb5_context context,
111733965Sjdp			 kcm_client *client,
111833965Sjdp			 kcm_operation opcode,
111933965Sjdp			 krb5_storage *request,
112033965Sjdp			 krb5_storage *response)
112133965Sjdp{
112233965Sjdp    struct kcm_default_cache *c;
112333965Sjdp    krb5_error_code ret;
112433965Sjdp    char *name;
112533965Sjdp
112633965Sjdp    ret = krb5_ret_stringz(request, &name);
112733965Sjdp    if (ret)
112833965Sjdp	return ret;
112933965Sjdp
113033965Sjdp    KCM_LOG_REQUEST_NAME(context, client, opcode, name);
113133965Sjdp
113233965Sjdp    for (c = default_caches; c != NULL; c = c->next) {
113333965Sjdp	if (kcm_is_same_session(client, c->uid, c->session))
113433965Sjdp	    break;
113533965Sjdp    }
113633965Sjdp    if (c == NULL) {
113733965Sjdp	c = malloc(sizeof(*c));
113833965Sjdp	if (c == NULL)
113933965Sjdp	    return ENOMEM;
114033965Sjdp	c->session = client->session;
114133965Sjdp	c->uid = client->uid;
114233965Sjdp	c->name = strdup(name);
114333965Sjdp
114433965Sjdp	c->next = default_caches;
114533965Sjdp	default_caches = c;
114633965Sjdp    } else {
114733965Sjdp	free(c->name);
114833965Sjdp	c->name = strdup(name);
114933965Sjdp    }
115033965Sjdp
115133965Sjdp    return 0;
115233965Sjdp}
115333965Sjdp
115433965Sjdpstatic krb5_error_code
115533965Sjdpkcm_op_get_kdc_offset(krb5_context context,
115633965Sjdp		      kcm_client *client,
115733965Sjdp		      kcm_operation opcode,
115833965Sjdp		      krb5_storage *request,
115933965Sjdp		      krb5_storage *response)
116033965Sjdp{
116133965Sjdp    krb5_error_code ret;
116233965Sjdp    kcm_ccache ccache;
116333965Sjdp    char *name;
116433965Sjdp
116533965Sjdp    ret = krb5_ret_stringz(request, &name);
116633965Sjdp    if (ret)
116733965Sjdp	return ret;
116833965Sjdp
116933965Sjdp    KCM_LOG_REQUEST_NAME(context, client, opcode, name);
117033965Sjdp
117133965Sjdp    ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache);
117233965Sjdp    free(name);
117333965Sjdp    if (ret)
117433965Sjdp	return ret;
117533965Sjdp
117633965Sjdp    HEIMDAL_MUTEX_lock(&ccache->mutex);
117733965Sjdp    ret = krb5_store_int32(response, ccache->kdc_offset);
117833965Sjdp    HEIMDAL_MUTEX_unlock(&ccache->mutex);
117933965Sjdp
118033965Sjdp    kcm_release_ccache(context, ccache);
118133965Sjdp
118233965Sjdp    return ret;
118333965Sjdp}
118433965Sjdp
118533965Sjdpstatic krb5_error_code
118633965Sjdpkcm_op_set_kdc_offset(krb5_context context,
118733965Sjdp		      kcm_client *client,
118833965Sjdp		      kcm_operation opcode,
118933965Sjdp		      krb5_storage *request,
119033965Sjdp		      krb5_storage *response)
119133965Sjdp{
119233965Sjdp    krb5_error_code ret;
119333965Sjdp    kcm_ccache ccache;
119433965Sjdp    int32_t offset;
119533965Sjdp    char *name;
119633965Sjdp
119733965Sjdp    ret = krb5_ret_stringz(request, &name);
119833965Sjdp    if (ret)
119933965Sjdp	return ret;
120033965Sjdp
120133965Sjdp    KCM_LOG_REQUEST_NAME(context, client, opcode, name);
120233965Sjdp
120333965Sjdp    ret = krb5_ret_int32(request, &offset);
120433965Sjdp    if (ret) {
120533965Sjdp	free(name);
120633965Sjdp	return ret;
120733965Sjdp    }
120833965Sjdp
120933965Sjdp    ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache);
121033965Sjdp    free(name);
121133965Sjdp    if (ret)
121233965Sjdp	return ret;
121333965Sjdp
121433965Sjdp    HEIMDAL_MUTEX_lock(&ccache->mutex);
121533965Sjdp    ccache->kdc_offset = offset;
121633965Sjdp    HEIMDAL_MUTEX_unlock(&ccache->mutex);
121738891Sjdp
121833965Sjdp    kcm_release_ccache(context, ccache);
121933965Sjdp
122033965Sjdp    return ret;
122133965Sjdp}
122233965Sjdp
122333965Sjdpstruct kcm_ntlm_cred {
122433965Sjdp    kcmuuid_t uuid;
122533965Sjdp    char *user;
122633965Sjdp    char *domain;
122733965Sjdp    krb5_data nthash;
122833965Sjdp    uid_t uid;
122933965Sjdp    pid_t session;
123033965Sjdp    struct kcm_ntlm_cred *next;
123133965Sjdp};
123233965Sjdp
123338891Sjdpstatic struct kcm_ntlm_cred *ntlm_head;
123433965Sjdp
123533965Sjdpstatic void
123633965Sjdpfree_cred(struct kcm_ntlm_cred *cred)
123733965Sjdp{
123833965Sjdp    free(cred->user);
123933965Sjdp    free(cred->domain);
124033965Sjdp    krb5_data_free(&cred->nthash);
124138891Sjdp    free(cred);
124238891Sjdp}
124333965Sjdp
124433965Sjdp
124533965Sjdp/*
124633965Sjdp * name
124733965Sjdp * domain
124838891Sjdp * ntlm hash
124933965Sjdp *
125033965Sjdp * Reply:
125133965Sjdp *   uuid
125233965Sjdp */
125333965Sjdp
125433965Sjdpstatic struct kcm_ntlm_cred *
125533965Sjdpfind_ntlm_cred(const char *user, const char *domain, kcm_client *client)
125633965Sjdp{
125733965Sjdp    struct kcm_ntlm_cred *c;
125833965Sjdp
125933965Sjdp    for (c = ntlm_head; c != NULL; c = c->next)
126033965Sjdp	if ((user[0] == '\0' || strcmp(user, c->user) == 0) &&
126133965Sjdp	    (domain == NULL || strcmp(domain, c->domain) == 0) &&
126233965Sjdp	    kcm_is_same_session(client, c->uid, c->session))
126333965Sjdp	    return c;
126433965Sjdp
126538891Sjdp    return NULL;
126638891Sjdp}
126733965Sjdp
126833965Sjdpstatic krb5_error_code
126933965Sjdpkcm_op_add_ntlm_cred(krb5_context context,
127033965Sjdp		     kcm_client *client,
127133965Sjdp		     kcm_operation opcode,
127233965Sjdp		     krb5_storage *request,
127333965Sjdp		     krb5_storage *response)
127433965Sjdp{
127533965Sjdp    struct kcm_ntlm_cred *cred, *c;
127638891Sjdp    krb5_error_code ret;
127733965Sjdp
127833965Sjdp    cred = calloc(1, sizeof(*cred));
127933965Sjdp    if (cred == NULL)
128033965Sjdp	return ENOMEM;
128133965Sjdp
128238891Sjdp    RAND_bytes(cred->uuid, sizeof(cred->uuid));
128333965Sjdp
128433965Sjdp    ret = krb5_ret_stringz(request, &cred->user);
128533965Sjdp    if (ret)
128633965Sjdp	goto error;
128733965Sjdp
128833965Sjdp    ret = krb5_ret_stringz(request, &cred->domain);
128933965Sjdp    if (ret)
129033965Sjdp	goto error;
129133965Sjdp
129233965Sjdp    ret = krb5_ret_data(request, &cred->nthash);
129333965Sjdp    if (ret)
129433965Sjdp	goto error;
129533965Sjdp
129699467Sobrien    /* search for dups */
129799467Sobrien    c = find_ntlm_cred(cred->user, cred->domain, client);
129833965Sjdp    if (c) {
129933965Sjdp	krb5_data hash = c->nthash;
130033965Sjdp	c->nthash = cred->nthash;
130133965Sjdp	cred->nthash = hash;
130233965Sjdp	free_cred(cred);
130333965Sjdp	cred = c;
130433965Sjdp    } else {
130533965Sjdp	cred->next = ntlm_head;
130633965Sjdp	ntlm_head = cred;
130733965Sjdp    }
130833965Sjdp
130933965Sjdp    cred->uid = client->uid;
131033965Sjdp    cred->session = client->session;
131133965Sjdp
131233965Sjdp    /* write response */
131333965Sjdp    (void)krb5_storage_write(response, &cred->uuid, sizeof(cred->uuid));
131433965Sjdp
131533965Sjdp    return 0;
131633965Sjdp
131733965Sjdp error:
131833965Sjdp    free_cred(cred);
131933965Sjdp
132033965Sjdp    return ret;
132133965Sjdp}
132233965Sjdp
132333965Sjdp/*
132433965Sjdp * { "HAVE_NTLM_CRED",		NULL },
132533965Sjdp *
132633965Sjdp * input:
132733965Sjdp *  name
132833965Sjdp *  domain
132933965Sjdp */
133033965Sjdp
133133965Sjdpstatic krb5_error_code
133233965Sjdpkcm_op_have_ntlm_cred(krb5_context context,
133333965Sjdp		     kcm_client *client,
133433965Sjdp		     kcm_operation opcode,
133559431Sobrien		     krb5_storage *request,
133633965Sjdp		     krb5_storage *response)
133733965Sjdp{
133833965Sjdp    struct kcm_ntlm_cred *c;
133933965Sjdp    char *user = NULL, *domain = NULL;
134033965Sjdp    krb5_error_code ret;
134133965Sjdp
134233965Sjdp    ret = krb5_ret_stringz(request, &user);
134333965Sjdp    if (ret)
134460514Sobrien	goto error;
134533965Sjdp
134660514Sobrien    ret = krb5_ret_stringz(request, &domain);
134760514Sobrien    if (ret)
134860514Sobrien	goto error;
134933965Sjdp
135033965Sjdp    if (domain[0] == '\0') {
135133965Sjdp	free(domain);
135233965Sjdp	domain = NULL;
135333965Sjdp    }
135433965Sjdp
135533965Sjdp    c = find_ntlm_cred(user, domain, client);
135633965Sjdp    if (c == NULL)
135733965Sjdp	ret = ENOENT;
135833965Sjdp
135933965Sjdp error:
136033965Sjdp    free(user);
136133965Sjdp    if (domain)
136233965Sjdp	free(domain);
136333965Sjdp
136433965Sjdp    return ret;
136533965Sjdp}
136633965Sjdp
136733965Sjdp/*
136833965Sjdp * { "DEL_NTLM_CRED",		NULL },
136933965Sjdp *
137033965Sjdp * input:
137160514Sobrien *  name
137233965Sjdp *  domain
137333965Sjdp */
137433965Sjdp
137533965Sjdpstatic krb5_error_code
137633965Sjdpkcm_op_del_ntlm_cred(krb5_context context,
137760514Sobrien		     kcm_client *client,
137833965Sjdp		     kcm_operation opcode,
137933965Sjdp		     krb5_storage *request,
138033965Sjdp		     krb5_storage *response)
138133965Sjdp{
138233965Sjdp    struct kcm_ntlm_cred **cp, *c;
138333965Sjdp    char *user = NULL, *domain = NULL;
138433965Sjdp    krb5_error_code ret;
138560514Sobrien
138633965Sjdp    ret = krb5_ret_stringz(request, &user);
138733965Sjdp    if (ret)
138833965Sjdp	goto error;
138933965Sjdp
139033965Sjdp    ret = krb5_ret_stringz(request, &domain);
139133965Sjdp    if (ret)
139233965Sjdp	goto error;
139333965Sjdp
139433965Sjdp    for (cp = &ntlm_head; *cp != NULL; cp = &(*cp)->next) {
139533965Sjdp	if (strcmp(user, (*cp)->user) == 0 && strcmp(domain, (*cp)->domain) == 0 &&
139633965Sjdp	    kcm_is_same_session(client, (*cp)->uid, (*cp)->session))
139733965Sjdp	{
139860514Sobrien	    c = *cp;
139933965Sjdp	    *cp = c->next;
140060514Sobrien
140160514Sobrien	    free_cred(c);
140260514Sobrien	    break;
140333965Sjdp	}
140433965Sjdp    }
140533965Sjdp
140633965Sjdp error:
140733965Sjdp    free(user);
140833965Sjdp    free(domain);
140933965Sjdp
141033965Sjdp    return ret;
141133965Sjdp}
141233965Sjdp
141333965Sjdp/*
141433965Sjdp * { "DO_NTLM_AUTH",		NULL },
141533965Sjdp *
141633965Sjdp * input:
141733965Sjdp *  name:string
141833965Sjdp *  domain:string
141933965Sjdp *  type2:data
142033965Sjdp *
142133965Sjdp * reply:
142233965Sjdp *  type3:data
142333965Sjdp *  flags:int32
142433965Sjdp *  session-key:data
142533965Sjdp */
142633965Sjdp
142733965Sjdp#define NTLM_FLAG_SESSIONKEY 1
142833965Sjdp#define NTLM_FLAG_NTLM2_SESSION 2
142933965Sjdp#define NTLM_FLAG_KEYEX 4
143033965Sjdp
143133965Sjdpstatic krb5_error_code
143233965Sjdpkcm_op_do_ntlm(krb5_context context,
143333965Sjdp	       kcm_client *client,
143433965Sjdp	       kcm_operation opcode,
143533965Sjdp	       krb5_storage *request,
143633965Sjdp	       krb5_storage *response)
143733965Sjdp{
143833965Sjdp    struct kcm_ntlm_cred *c;
143933965Sjdp    struct ntlm_type2 type2;
144033965Sjdp    struct ntlm_type3 type3;
144133965Sjdp    char *user = NULL, *domain = NULL;
144233965Sjdp    struct ntlm_buf ndata, sessionkey;
144333965Sjdp    krb5_data data;
144433965Sjdp    krb5_error_code ret;
144533965Sjdp    uint32_t flags = 0;
144633965Sjdp
144733965Sjdp    memset(&type2, 0, sizeof(type2));
144833965Sjdp    memset(&type3, 0, sizeof(type3));
144933965Sjdp    sessionkey.data = NULL;
145033965Sjdp    sessionkey.length = 0;
145133965Sjdp
145233965Sjdp    ret = krb5_ret_stringz(request, &user);
145333965Sjdp    if (ret)
145433965Sjdp	goto error;
145533965Sjdp
145633965Sjdp    ret = krb5_ret_stringz(request, &domain);
145733965Sjdp    if (ret)
145889864Sobrien	goto error;
145991050Sobrien
146033965Sjdp    if (domain[0] == '\0') {
146133965Sjdp	free(domain);
146233965Sjdp	domain = NULL;
146333965Sjdp    }
146433965Sjdp
146533965Sjdp    c = find_ntlm_cred(user, domain, client);
146633965Sjdp    if (c == NULL) {
146789864Sobrien	ret = EINVAL;
146833965Sjdp	goto error;
146933965Sjdp    }
147033965Sjdp
147133965Sjdp    ret = krb5_ret_data(request, &data);
147233965Sjdp    if (ret)
147333965Sjdp	goto error;
147433965Sjdp
147533965Sjdp    ndata.data = data.data;
147633965Sjdp    ndata.length = data.length;
147733965Sjdp
147833965Sjdp    ret = heim_ntlm_decode_type2(&ndata, &type2);
147933965Sjdp    krb5_data_free(&data);
148033965Sjdp    if (ret)
148133965Sjdp	goto error;
148233965Sjdp
148333965Sjdp    if (domain && strcmp(domain, type2.targetname) == 0) {
148433965Sjdp	ret = EINVAL;
148533965Sjdp	goto error;
148633965Sjdp    }
148733965Sjdp
148833965Sjdp    type3.username = c->user;
148933965Sjdp    type3.flags = type2.flags;
149033965Sjdp    type3.targetname = type2.targetname;
149133965Sjdp    type3.ws = rk_UNCONST("workstation");
149233965Sjdp
149333965Sjdp    /*
149460514Sobrien     * NTLM Version 1 if no targetinfo buffer.
149533965Sjdp     */
149689864Sobrien
149789864Sobrien    if (1 || type2.targetinfo.length == 0) {
149878836Sobrien	struct ntlm_buf sessionkey;
149933965Sjdp
150033965Sjdp	if (type2.flags & NTLM_NEG_NTLM2_SESSION) {
150189864Sobrien	    unsigned char nonce[8];
150233965Sjdp
150333965Sjdp	    if (RAND_bytes(nonce, sizeof(nonce)) != 1) {
150433965Sjdp		ret = EINVAL;
150533965Sjdp		goto error;
150633965Sjdp	    }
150733965Sjdp
150833965Sjdp	    ret = heim_ntlm_calculate_ntlm2_sess(nonce,
150933965Sjdp						 type2.challenge,
151033965Sjdp						 c->nthash.data,
151133965Sjdp						 &type3.lm,
151233965Sjdp						 &type3.ntlm);
151333965Sjdp	} else {
151433965Sjdp	    ret = heim_ntlm_calculate_ntlm1(c->nthash.data,
151533965Sjdp					    c->nthash.length,
151633965Sjdp					    type2.challenge,
151733965Sjdp					    &type3.ntlm);
151833965Sjdp
151933965Sjdp	}
152033965Sjdp	if (ret)
152160514Sobrien	    goto error;
152233965Sjdp
152333965Sjdp	ret = heim_ntlm_build_ntlm1_master(c->nthash.data,
152489864Sobrien					   c->nthash.length,
152533965Sjdp					   &sessionkey,
152633965Sjdp					   &type3.sessionkey);
152733965Sjdp	if (ret) {
152833965Sjdp	    if (type3.lm.data)
152933965Sjdp		free(type3.lm.data);
153033965Sjdp	    if (type3.ntlm.data)
153133965Sjdp		free(type3.ntlm.data);
153233965Sjdp	    goto error;
153333965Sjdp	}
153433965Sjdp
153533965Sjdp	free(sessionkey.data);
153633965Sjdp	if (ret) {
153733965Sjdp	    if (type3.lm.data)
153833965Sjdp		free(type3.lm.data);
153933965Sjdp	    if (type3.ntlm.data)
154033965Sjdp		free(type3.ntlm.data);
154133965Sjdp	    goto error;
154233965Sjdp	}
154333965Sjdp	flags |= NTLM_FLAG_SESSIONKEY;
154460514Sobrien#if 0
154533965Sjdp    } else {
154633965Sjdp	struct ntlm_buf sessionkey;
154789864Sobrien	unsigned char ntlmv2[16];
154833965Sjdp	struct ntlm_targetinfo ti;
154933965Sjdp
155033965Sjdp	/* verify infotarget */
155133965Sjdp
155233965Sjdp	ret = heim_ntlm_decode_targetinfo(&type2.targetinfo, 1, &ti);
155333965Sjdp	if(ret) {
155433965Sjdp	    _gss_ntlm_delete_sec_context(minor_status,
155533965Sjdp					 context_handle, NULL);
155633965Sjdp	    *minor_status = ret;
155733965Sjdp	    return GSS_S_FAILURE;
155833965Sjdp	}
155933965Sjdp
156033965Sjdp	if (ti.domainname && strcmp(ti.domainname, name->domain) != 0) {
156133965Sjdp	    _gss_ntlm_delete_sec_context(minor_status,
156233965Sjdp					 context_handle, NULL);
156333965Sjdp	    *minor_status = EINVAL;
156433965Sjdp	    return GSS_S_FAILURE;
156533965Sjdp	}
156633965Sjdp
156733965Sjdp	ret = heim_ntlm_calculate_ntlm2(ctx->client->key.data,
156860514Sobrien					ctx->client->key.length,
156933965Sjdp					type3.username,
157033965Sjdp					name->domain,
157133965Sjdp					type2.challenge,
157233965Sjdp					&type2.targetinfo,
157333965Sjdp					ntlmv2,
157433965Sjdp					&type3.ntlm);
157533965Sjdp	if (ret) {
157633965Sjdp	    _gss_ntlm_delete_sec_context(minor_status,
157733965Sjdp					 context_handle, NULL);
157833965Sjdp	    *minor_status = ret;
157933965Sjdp	    return GSS_S_FAILURE;
158033965Sjdp	}
158133965Sjdp
158233965Sjdp	ret = heim_ntlm_build_ntlm1_master(ntlmv2, sizeof(ntlmv2),
158333965Sjdp					   &sessionkey,
158433965Sjdp					   &type3.sessionkey);
158533965Sjdp	memset(ntlmv2, 0, sizeof(ntlmv2));
158633965Sjdp	if (ret) {
158733965Sjdp	    _gss_ntlm_delete_sec_context(minor_status,
158833965Sjdp					 context_handle, NULL);
158933965Sjdp	    *minor_status = ret;
159033965Sjdp	    return GSS_S_FAILURE;
159133965Sjdp	}
159233965Sjdp
159333965Sjdp	flags |= NTLM_FLAG_NTLM2_SESSION |
159433965Sjdp	         NTLM_FLAG_SESSION;
159533965Sjdp
159633965Sjdp	if (type3.flags & NTLM_NEG_KEYEX)
159733965Sjdp	    flags |= NTLM_FLAG_KEYEX;
159833965Sjdp
159933965Sjdp	ret = krb5_data_copy(&ctx->sessionkey,
160033965Sjdp			     sessionkey.data, sessionkey.length);
160133965Sjdp	free(sessionkey.data);
160233965Sjdp	if (ret) {
160333965Sjdp	    _gss_ntlm_delete_sec_context(minor_status,
160433965Sjdp					 context_handle, NULL);
160533965Sjdp	    *minor_status = ret;
160633965Sjdp	    return GSS_S_FAILURE;
160733965Sjdp	}
160833965Sjdp#endif
160933965Sjdp    }
161033965Sjdp
161133965Sjdp#if 0
161233965Sjdp    if (flags & NTLM_FLAG_NTLM2_SESSION) {
161333965Sjdp	_gss_ntlm_set_key(&ctx->u.v2.send, 0, (ctx->flags & NTLM_NEG_KEYEX),
161433965Sjdp			  ctx->sessionkey.data,
161533965Sjdp			  ctx->sessionkey.length);
161633965Sjdp	_gss_ntlm_set_key(&ctx->u.v2.recv, 1, (ctx->flags & NTLM_NEG_KEYEX),
161733965Sjdp			  ctx->sessionkey.data,
161833965Sjdp			  ctx->sessionkey.length);
1619    } else {
1620	flags |= NTLM_FLAG_SESSION;
1621	RC4_set_key(&ctx->u.v1.crypto_recv.key,
1622		    ctx->sessionkey.length,
1623		    ctx->sessionkey.data);
1624	RC4_set_key(&ctx->u.v1.crypto_send.key,
1625		    ctx->sessionkey.length,
1626		    ctx->sessionkey.data);
1627    }
1628#endif
1629
1630    ret = heim_ntlm_encode_type3(&type3, &ndata);
1631    if (ret)
1632	goto error;
1633
1634    data.data = ndata.data;
1635    data.length = ndata.length;
1636    ret = krb5_store_data(response, data);
1637    heim_ntlm_free_buf(&ndata);
1638    if (ret) goto error;
1639
1640    ret = krb5_store_int32(response, flags);
1641    if (ret) goto error;
1642
1643    data.data = sessionkey.data;
1644    data.length = sessionkey.length;
1645
1646    ret = krb5_store_data(response, data);
1647    if (ret) goto error;
1648
1649 error:
1650    free(type3.username);
1651    heim_ntlm_free_type2(&type2);
1652    free(user);
1653    if (domain)
1654	free(domain);
1655
1656    return ret;
1657}
1658
1659
1660/*
1661 * { "GET_NTLM_UUID_LIST",	NULL }
1662 *
1663 * reply:
1664 *   1 user domain
1665 *   0 [ end of list ]
1666 */
1667
1668static krb5_error_code
1669kcm_op_get_ntlm_user_list(krb5_context context,
1670			  kcm_client *client,
1671			  kcm_operation opcode,
1672			  krb5_storage *request,
1673			  krb5_storage *response)
1674{
1675    struct kcm_ntlm_cred *c;
1676    krb5_error_code ret;
1677
1678    for (c = ntlm_head; c != NULL; c = c->next) {
1679	if (!kcm_is_same_session(client, c->uid, c->session))
1680	    continue;
1681
1682	ret = krb5_store_uint32(response, 1);
1683	if (ret)
1684	    return ret;
1685	ret = krb5_store_stringz(response, c->user);
1686	if (ret)
1687	    return ret;
1688	ret = krb5_store_stringz(response, c->domain);
1689	if (ret)
1690	    return ret;
1691    }
1692    return krb5_store_uint32(response, 0);
1693}
1694
1695/*
1696 *
1697 */
1698
1699static struct kcm_op kcm_ops[] = {
1700    { "NOOP", 			kcm_op_noop },
1701    { "GET_NAME",		kcm_op_get_name },
1702    { "RESOLVE",		kcm_op_noop },
1703    { "GEN_NEW", 		kcm_op_gen_new },
1704    { "INITIALIZE",		kcm_op_initialize },
1705    { "DESTROY",		kcm_op_destroy },
1706    { "STORE",			kcm_op_store },
1707    { "RETRIEVE",		kcm_op_retrieve },
1708    { "GET_PRINCIPAL",		kcm_op_get_principal },
1709    { "GET_CRED_UUID_LIST",	kcm_op_get_cred_uuid_list },
1710    { "GET_CRED_BY_UUID",	kcm_op_get_cred_by_uuid },
1711    { "REMOVE_CRED",		kcm_op_remove_cred },
1712    { "SET_FLAGS",		kcm_op_set_flags },
1713    { "CHOWN",			kcm_op_chown },
1714    { "CHMOD",			kcm_op_chmod },
1715    { "GET_INITIAL_TICKET",	kcm_op_get_initial_ticket },
1716    { "GET_TICKET",		kcm_op_get_ticket },
1717    { "MOVE_CACHE",		kcm_op_move_cache },
1718    { "GET_CACHE_UUID_LIST",	kcm_op_get_cache_uuid_list },
1719    { "GET_CACHE_BY_UUID",	kcm_op_get_cache_by_uuid },
1720    { "GET_DEFAULT_CACHE",      kcm_op_get_default_cache },
1721    { "SET_DEFAULT_CACHE",      kcm_op_set_default_cache },
1722    { "GET_KDC_OFFSET",      	kcm_op_get_kdc_offset },
1723    { "SET_KDC_OFFSET",      	kcm_op_set_kdc_offset },
1724    { "ADD_NTLM_CRED",		kcm_op_add_ntlm_cred },
1725    { "HAVE_USER_CRED",		kcm_op_have_ntlm_cred },
1726    { "DEL_NTLM_CRED",		kcm_op_del_ntlm_cred },
1727    { "DO_NTLM_AUTH",		kcm_op_do_ntlm },
1728    { "GET_NTLM_USER_LIST",	kcm_op_get_ntlm_user_list }
1729};
1730
1731
1732const char *
1733kcm_op2string(kcm_operation opcode)
1734{
1735    if (opcode >= sizeof(kcm_ops)/sizeof(kcm_ops[0]))
1736	return "Unknown operation";
1737
1738    return kcm_ops[opcode].name;
1739}
1740
1741krb5_error_code
1742kcm_dispatch(krb5_context context,
1743	     kcm_client *client,
1744	     krb5_data *req_data,
1745	     krb5_data *resp_data)
1746{
1747    krb5_error_code ret;
1748    kcm_method method;
1749    krb5_storage *req_sp = NULL;
1750    krb5_storage *resp_sp = NULL;
1751    uint16_t opcode;
1752
1753    resp_sp = krb5_storage_emem();
1754    if (resp_sp == NULL) {
1755	return ENOMEM;
1756    }
1757
1758    if (client->pid == -1) {
1759	kcm_log(0, "Client had invalid process number");
1760	ret = KRB5_FCC_INTERNAL;
1761	goto out;
1762    }
1763
1764    req_sp = krb5_storage_from_data(req_data);
1765    if (req_sp == NULL) {
1766	kcm_log(0, "Process %d: failed to initialize storage from data",
1767		client->pid);
1768	ret = KRB5_CC_IO;
1769	goto out;
1770    }
1771
1772    ret = krb5_ret_uint16(req_sp, &opcode);
1773    if (ret) {
1774	kcm_log(0, "Process %d: didn't send a message", client->pid);
1775	goto out;
1776    }
1777
1778    if (opcode >= sizeof(kcm_ops)/sizeof(kcm_ops[0])) {
1779	kcm_log(0, "Process %d: invalid operation code %d",
1780		client->pid, opcode);
1781	ret = KRB5_FCC_INTERNAL;
1782	goto out;
1783    }
1784    method = kcm_ops[opcode].method;
1785    if (method == NULL) {
1786	kcm_log(0, "Process %d: operation code %s not implemented",
1787		client->pid, kcm_op2string(opcode));
1788	ret = KRB5_FCC_INTERNAL;
1789	goto out;
1790    }
1791
1792    /* seek past place for status code */
1793    krb5_storage_seek(resp_sp, 4, SEEK_SET);
1794
1795    ret = (*method)(context, client, opcode, req_sp, resp_sp);
1796
1797out:
1798    if (req_sp != NULL) {
1799	krb5_storage_free(req_sp);
1800    }
1801
1802    krb5_storage_seek(resp_sp, 0, SEEK_SET);
1803    krb5_store_int32(resp_sp, ret);
1804
1805    ret = krb5_storage_to_data(resp_sp, resp_data);
1806    krb5_storage_free(resp_sp);
1807
1808    return ret;
1809}
1810
1811