/*
 * ctx.c
 *
 * Copyright (c) 2004 The Regents of the University of Michigan.
 * All rights reserved.
 *
 * Copyright (c) 2004 Marius Aamodt Eriksen <marius@umich.edu>.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * $Id: ctx.c,v 1.4 2004/06/14 19:38:56 marius Exp $
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/queue.h>
#include <sys/tree.h>
#include <sys/param.h>

#include <rpc/rpc.h>
#include <gssapi.h>

#include <rpcx/rpc_dev.h>
#include <rpcx/rpcauth_gss.h>
#include <rpcx/rpcclnt_redirect.h>

#include <auth_gss.h>
#include <poll.h>
#include <fcntl.h>
#include <stdlib.h>
#include <err.h>
#include <stdio.h>
#include <string.h>

#include "rpcd.h"

static int _ctxcompare(struct rpcd_ctx *, struct rpcd_ctx *);

static SPLAY_HEAD(ctxtree, rpcd_ctx) _ctxs;

SPLAY_PROTOTYPE(ctxtree, rpcd_ctx, ctx_node, _ctxcompare);
SPLAY_GENERATE(ctxtree, rpcd_ctx, ctx_node, _ctxcompare);

void
ctx_init(void)
{
	SPLAY_INIT(&_ctxs);
}

/*
 * TODO:
 * 
 *    - keep tailq of per-mount, so that we can easily free each
 *      ctx that belongs to a certain mntpoint.
 */

struct rpcd_ctx *
ctx_get(uid_t uid, char *export, char *mount)
{
	struct rpcd_ctx *ctx, searchctx;
	char *p, *host;

	if (set_krb5_ccache(uid) < 0)
		return (NULL);

	searchctx.ctx_uid = uid;
	strlcpy(searchctx.ctx_svc_export,
	    export, sizeof(searchctx.ctx_svc_export));
	strlcpy(searchctx.ctx_mntpt,
	    mount, sizeof(searchctx.ctx_mntpt));
	if ((ctx = SPLAY_FIND(ctxtree, &_ctxs, &searchctx)) != NULL)
		return (ctx);

	if ((ctx = malloc(sizeof(*ctx))) == NULL)
		return (NULL);

	*ctx = searchctx;

	host = export;
	p = strchr(host, ':');
	if (p != NULL)
		*p = '\0';

	if ((ctx->ctx_host = strdup(host)) == NULL) {
		free(ctx);
		return (NULL);
	}

	if (ctx_refresh(ctx) < 0) {
		free(ctx);
		return (NULL);
	}

	SPLAY_INSERT(ctxtree, &_ctxs, ctx);

	return (ctx);
}

int
ctx_refresh(struct rpcd_ctx *ctx)
{
	char svchost[128];
	struct rpc_gss_sec sec;
	AUTH *auth;

	ctx->ctx_clnt = clnt_create(ctx->ctx_host, 100003, 4, "tcp");
	if (ctx->ctx_clnt == NULL) {
		warnx("Cannot create rpcclnt for %s", ctx->ctx_host);
		return (NULL);
	}

	snprintf(svchost, sizeof(svchost), "nfs@%s", ctx->ctx_host);

	sec.mech = (gss_OID)&krb5oid;
	sec.qop = GSS_C_QOP_DEFAULT;
	sec.svc = RPCSEC_GSS_SVC_NONE;
	if ((auth = authgss_create_default(ctx->ctx_clnt,
		 svchost, &sec)) == NULL) {
		warnx("Failed to initialize context for user %d", ctx->ctx_uid);
		clnt_destroy(ctx->ctx_clnt);
		return (-1);
	}

	ctx->ctx_clnt->cl_auth = auth;

	if (!authgss_service(ctx->ctx_clnt->cl_auth, RPCSEC_GSS_SVC_INTEGRITY))
		warnx("Failed to initialize integrity for user %d",
		    ctx->ctx_uid);

	return (0);
}

void
ctx_put(struct rpcd_ctx *ctx)
{
	SPLAY_REMOVE(ctxtree, &_ctxs, ctx);
	free(ctx);
}

static int
_ctxcompare(struct rpcd_ctx *ctx0, struct rpcd_ctx *ctx1)
{
	int diff;

	diff = ctx0->ctx_uid - ctx1->ctx_uid;
	if (diff != 0)
		return (diff);

	diff = strcmp(ctx0->ctx_svc_export, ctx1->ctx_svc_export);
	if (diff != 0)
		return (diff);

	return (strcmp(ctx0->ctx_mntpt, ctx1->ctx_mntpt));
}
