OpenSolaris_b135/cmd/gss/gssd/gssd_proc.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 *  RPC server procedures for the gssapi usermode daemon gssd.
 */

#include <stdio.h>
#include <stdio_ext.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <strings.h>
#include <limits.h>
#include <sys/param.h>
#include <sys/syslog.h>
#include <mechglueP.h>
#include "gssd.h"
#include <gssapi/gssapi.h>
#include <rpc/rpc.h>
#include <stdlib.h>
#include <syslog.h>
#include <sys/resource.h>

#define	SRVTAB	""
#define	FDCACHE_PERCENTAGE	.75	/* Percentage of total FD limit */
#define	FDCACHE_DEFAULT		16	/* Default LRU cache size */
#define	GSSD_FD_LIMIT		255	/* Increase number of fds allowed */

extern int gssd_debug;			/* declared in gssd.c */
static OM_uint32 gssd_time_verf;	/* verifies same gssd */
static OM_uint32 context_verf;		/* context sequence numbers */

struct gssd_ctx_slot {
	struct gssd_ctx_slot *lru_next;
	struct gssd_ctx_slot *lru_prev;
	bool_t		inuse;
	OM_uint32	create_time;
	OM_uint32	verf;
	gss_ctx_id_t	ctx;
	gss_ctx_id_t	rpcctx;
};

struct gssd_ctx_slot *gssd_ctx_slot_tbl;
struct gssd_ctx_slot *gssd_lru_head;

static int max_contexts;

static int checkfrom(struct svc_req *, uid_t *);
extern void set_gssd_uid(uid_t);
extern int __rpc_get_local_uid(SVCXPRT *, uid_t *);

void
gssd_setup(char *arg)
{
	int i;
	struct rlimit rl;
	hrtime_t high_res_time;

	gssd_time_verf = (OM_uint32)time(NULL);
	max_contexts = FDCACHE_DEFAULT;

	/*
	 * Use low order bits of high resolution time to get a reasonably
	 * random number to start the context sequencing.  This alternative
	 * to using a time value avoid clock resets via NTP or ntpdate.
	 */
	high_res_time = gethrtime();
	context_verf = (OM_uint32)high_res_time;

	/*
	 * Increase resource limit of FDs in case we get alot accept/init_
	 * sec_context calls before we're able to export them.  This can
	 * happen in very heavily load environments where gssd doesn't get
	 * much time to work on its backlog.
	 */
	if ((getrlimit(RLIMIT_NOFILE, &rl)) == 0) {
		rl.rlim_cur = (rl.rlim_max >= GSSD_FD_LIMIT) ?
				GSSD_FD_LIMIT : rl.rlim_max;
		if ((setrlimit(RLIMIT_NOFILE, &rl)) == 0)
			max_contexts = rl.rlim_cur * FDCACHE_PERCENTAGE;
		(void) enable_extended_FILE_stdio(-1, -1);
	}

	gssd_ctx_slot_tbl = (struct gssd_ctx_slot *)
		malloc(sizeof (struct gssd_ctx_slot) * max_contexts);

	if (gssd_ctx_slot_tbl == NULL) {
		(void) fprintf(stderr,
			gettext("[%s] could not allocate %d byte context table"
			"\n"), arg,
			(sizeof (struct gssd_ctx_slot) * max_contexts));
		exit(1);
	}

	for (i = 1; i < max_contexts; i++) {
		gssd_ctx_slot_tbl[i-1].lru_next = &gssd_ctx_slot_tbl[i];
		gssd_ctx_slot_tbl[i].lru_prev = &gssd_ctx_slot_tbl[i-1];
		gssd_ctx_slot_tbl[i].inuse = FALSE;
		gssd_ctx_slot_tbl[i].verf = 0;
		gssd_ctx_slot_tbl[i].create_time = 0;
		gssd_ctx_slot_tbl[i].rpcctx = (gss_ctx_id_t)(i + 1);
	}

	gssd_ctx_slot_tbl[max_contexts - 1].lru_next = &gssd_ctx_slot_tbl[0];
	gssd_ctx_slot_tbl[0].lru_prev = &gssd_ctx_slot_tbl[max_contexts - 1];
	gssd_ctx_slot_tbl[0].inuse = FALSE;
	gssd_ctx_slot_tbl[0].verf = 0;
	gssd_ctx_slot_tbl[0].create_time = 0;
	gssd_ctx_slot_tbl[0].rpcctx = (gss_ctx_id_t)1;

	gssd_lru_head = &gssd_ctx_slot_tbl[0];
}

static OM_uint32 syslog_interval = 60;

static struct gssd_ctx_slot *
gssd_alloc_slot(gss_ctx_id_t ctx)
{
	struct gssd_ctx_slot *lru;
	OM_uint32 current_time;
	static OM_uint32 last_syslog = 0;
	static bool_t first_take = TRUE;
	static int tooks;
	OM_uint32 minor_status;

	lru = gssd_lru_head;
	gssd_lru_head = lru->lru_next;

	current_time = (OM_uint32) time(NULL);

	if (last_syslog == 0)
		last_syslog = current_time;	/* Save 1st alloc time */

	if (lru->inuse) {
		if (lru->ctx != GSS_C_NO_CONTEXT)
			(void) gss_delete_sec_context(&minor_status,
				&lru->ctx, NULL);
		tooks++;

		if (((current_time - last_syslog) > syslog_interval) ||
			first_take) {
			syslog(LOG_WARNING, gettext("re-used an existing "
				"context slot of age %u seconds (%d slots re-"
				"used during last %u seconds)"),
				current_time - lru->create_time, tooks,
				current_time - last_syslog);

			last_syslog = current_time;
			tooks = 0;
			first_take = FALSE;
		}
	}

	/*
	 * Assign the next context verifier to the context (avoiding zero).
	 */
	context_verf++;
	if (context_verf == 0)
		context_verf = 1;
	lru->verf = context_verf;

	lru->create_time = current_time;
	lru->ctx = ctx;
	lru->inuse = TRUE;
	return (lru);
}

/*
 * We always add 1 because we don't want slot 0 to be confused
 * with GSS_C_NO_CONTEXT.
 */

static struct gssd_ctx_slot *
gssd_handle_to_slot(GSS_CTX_ID_T *h)
{
	intptr_t i;

	if (h->GSS_CTX_ID_T_len == 0) {
		return (NULL);
	}
	if (h->GSS_CTX_ID_T_len != sizeof (i))
		return (NULL);

	i = (*(intptr_t *)(h->GSS_CTX_ID_T_val)) - 1;

	if (i < 0 || i >= max_contexts)
		return (NULL);

	return (&gssd_ctx_slot_tbl[i]);
}

static void
gssd_rel_slot(struct gssd_ctx_slot *lru)
{
	struct gssd_ctx_slot *prev, *next;

	if (lru == NULL)
		return;

	lru->inuse = FALSE;

	/*
	 * Remove entry from its current location in list
	 */
	prev = lru->lru_prev;
	next = lru->lru_next;
	prev->lru_next = next;
	next->lru_prev = prev;

	/*
	 * Since it is no longer in use, it is the least recently
	 * used.
	 */
	prev = gssd_lru_head->lru_prev;
	next = gssd_lru_head;

	prev->lru_next = lru;
	lru->lru_prev = prev;

	next->lru_prev = lru;
	lru->lru_next = next;

	gssd_lru_head = lru;
}

static void
gssd_convert_context_handle(GSS_CTX_ID_T *h,
	gss_ctx_id_t *context_handle,
	OM_uint32 verf,
	bool_t *context_verf_ok,
	struct gssd_ctx_slot **slotp)
{
	struct gssd_ctx_slot *slot;

	*context_verf_ok = FALSE;
	*context_handle = (gss_ctx_id_t)1;
	if (slotp != NULL)
		*slotp = NULL;

	if (h->GSS_CTX_ID_T_len == 0) {
		*context_handle = GSS_C_NO_CONTEXT;
		*context_verf_ok = TRUE;
		return;
	}

	slot = gssd_handle_to_slot(h);

	if (slot == NULL)
		return;

	if (verf != slot->verf)
		return;

	*context_verf_ok = TRUE;
	*context_handle = slot->ctx;
	if (slotp != NULL)
		*slotp = slot;
}

bool_t
gss_acquire_cred_1_svc(argp, res, rqstp)
	gss_acquire_cred_arg *argp;
	gss_acquire_cred_res *res;
	struct svc_req *rqstp;
{
	OM_uint32 		minor_status;
	gss_name_t		desired_name;
	gss_OID_desc		name_type_desc;
	gss_OID			name_type = &name_type_desc;
	OM_uint32		time_req;
	gss_OID_set_desc	desired_mechs_desc;
	gss_OID_set		desired_mechs;
	int			cred_usage;
	gss_cred_id_t 		output_cred_handle;
	gss_OID_set 		actual_mechs;
	gss_buffer_desc		external_name;
	uid_t			uid;
	int			i, j;

	if (gssd_debug)
		fprintf(stderr, gettext("gss_acquire_cred\n"));

	memset(res, 0, sizeof (*res));

	/*
	 * if the request isn't from root, null out the result pointer
	 * entries, so the next time through xdr_free won't try to
	 * free unmalloc'd memory and then return NULL
	 */

	if (checkfrom(rqstp, &uid) == 0) {
		res->output_cred_handle.GSS_CRED_ID_T_val = NULL;
		res->actual_mechs.GSS_OID_SET_val = NULL;
		return (FALSE);
	}

/* set the uid sent as the RPC argument */

	uid = argp->uid;
	set_gssd_uid(uid);

/* convert the desired name from external to internal format */

	external_name.length = argp->desired_name.GSS_BUFFER_T_len;
	external_name.value = (void *)malloc(external_name.length);
	if (!external_name.value)
		return (GSS_S_FAILURE);
	memcpy(external_name.value, argp->desired_name.GSS_BUFFER_T_val,
		external_name.length);

	if (argp->name_type.GSS_OID_len == 0) {
		name_type = GSS_C_NULL_OID;
	} else {
		name_type->length = argp->name_type.GSS_OID_len;
		name_type->elements = (void *)malloc(name_type->length);
		if (!name_type->elements) {
			free(external_name.value);
			return (GSS_S_FAILURE);
		}
		memcpy(name_type->elements, argp->name_type.GSS_OID_val,
			name_type->length);
	}

	if (gss_import_name(&minor_status, &external_name, name_type,
			    &desired_name) != GSS_S_COMPLETE) {

		res->status = (OM_uint32) GSS_S_FAILURE;
		res->minor_status = minor_status;

		free(external_name.value);
		if (name_type != GSS_C_NULL_OID)
			free(name_type->elements);

		return (TRUE);
	}

/*
 * copy the XDR structured arguments into their corresponding local GSSAPI
 * variables.
 */

	cred_usage = argp->cred_usage;
	time_req = argp->time_req;

	if (argp->desired_mechs.GSS_OID_SET_len != 0) {
		desired_mechs = &desired_mechs_desc;
		desired_mechs->count =
			(int)argp->desired_mechs.GSS_OID_SET_len;
		desired_mechs->elements = (gss_OID)
			malloc(sizeof (gss_OID_desc) * desired_mechs->count);
		if (!desired_mechs->elements) {
			free(external_name.value);
			free(name_type->elements);
			return (GSS_S_FAILURE);
		}
		for (i = 0; i < desired_mechs->count; i++) {
			desired_mechs->elements[i].length =
				(OM_uint32)argp->desired_mechs.
				GSS_OID_SET_val[i].GSS_OID_len;
			desired_mechs->elements[i].elements =
				(void *)malloc(desired_mechs->elements[i].
						length);
			if (!desired_mechs->elements[i].elements) {
				free(external_name.value);
				free(name_type->elements);
				for (j = 0; j < (i -1); j++) {
					free
					(desired_mechs->elements[j].elements);
				}
				free(desired_mechs->elements);
				return (GSS_S_FAILURE);
			}
			memcpy(desired_mechs->elements[i].elements,
				argp->desired_mechs.GSS_OID_SET_val[i].
				GSS_OID_val,
				desired_mechs->elements[i].length);
		}
	} else
		desired_mechs = GSS_C_NULL_OID_SET;

	/* call the gssapi routine */

	res->status = (OM_uint32)gss_acquire_cred(&res->minor_status,
				desired_name,
				time_req,
				desired_mechs,
				cred_usage,
				&output_cred_handle,
				&actual_mechs,
				&res->time_rec);

	/*
	 * convert the output args from the parameter given in the call to the
	 * variable in the XDR result
	 */

	res->output_cred_handle.GSS_CRED_ID_T_len = sizeof (gss_cred_id_t);
	res->output_cred_handle.GSS_CRED_ID_T_val =
		(void *)malloc(sizeof (gss_cred_id_t));
	if (!res->output_cred_handle.GSS_CRED_ID_T_val) {
		free(external_name.value);
		free(name_type->elements);
		for (i = 0; i < desired_mechs->count; i++) {
			free(desired_mechs->elements[i].elements);
			}
		free(desired_mechs->elements);
		return (GSS_S_FAILURE);
	}
	memcpy(res->output_cred_handle.GSS_CRED_ID_T_val, &output_cred_handle,
		sizeof (gss_cred_id_t));

	if (actual_mechs != GSS_C_NULL_OID_SET) {
		res->actual_mechs.GSS_OID_SET_len =
			(uint_t)actual_mechs->count;
		res->actual_mechs.GSS_OID_SET_val = (GSS_OID *)
			malloc(sizeof (GSS_OID) * actual_mechs->count);
		if (!res->actual_mechs.GSS_OID_SET_val) {
			free(external_name.value);
			free(name_type->elements);
			for (i = 0; i < desired_mechs->count; i++) {
				free(desired_mechs->elements[i].elements);
			}
			free(desired_mechs->elements);
			free(res->output_cred_handle.GSS_CRED_ID_T_val);
			return (GSS_S_FAILURE);
		}
		for (i = 0; i < actual_mechs->count; i++) {
			res->actual_mechs.GSS_OID_SET_val[i].GSS_OID_len =
				(uint_t)actual_mechs->elements[i].length;
			res->actual_mechs.GSS_OID_SET_val[i].GSS_OID_val =
				(char *)malloc(actual_mechs->elements[i].
						length);
			if (!res->actual_mechs.GSS_OID_SET_val[i].GSS_OID_val) {
				free(external_name.value);
				free(name_type->elements);
				free(desired_mechs->elements);
				for (j = 0; j < desired_mechs->count; j++) {
					free
					(desired_mechs->elements[i].elements);
				}
				free(res->actual_mechs.GSS_OID_SET_val);
				for (j = 0; j < (i - 1); j++) {
					free
					(res->actual_mechs.
						GSS_OID_SET_val[j].GSS_OID_val);
				}
				return (GSS_S_FAILURE);
			}
			memcpy(res->actual_mechs.GSS_OID_SET_val[i].GSS_OID_val,
				actual_mechs->elements[i].elements,
				actual_mechs->elements[i].length);
		}
	} else
		res->actual_mechs.GSS_OID_SET_len = 0;

	/*
	 * set the time verifier for credential handle.  To ensure that the
	 * timestamp is not the same as previous gssd process, verify that
	 * time is not the same as set earlier at start of process.  If it
	 * is, sleep one second and reset. (due to one second granularity)
	 */

	if (res->status == GSS_S_COMPLETE) {
		res->gssd_cred_verifier = (OM_uint32)time(NULL);
		if (res->gssd_cred_verifier == gssd_time_verf) {
			sleep(1);
			gssd_time_verf = (OM_uint32)time(NULL);
		}
		res->gssd_cred_verifier = gssd_time_verf;
	}

	/*
	 * now release the space allocated by the underlying gssapi mechanism
	 * library for actual_mechs as well as by this routine for
	 * external_name, name_type and desired_name
	 */

	free(external_name.value);
	if (name_type != GSS_C_NULL_OID)
		free(name_type->elements);
	gss_release_name(&minor_status, &desired_name);

	if (actual_mechs != GSS_C_NULL_OID_SET) {
		for (i = 0; i < actual_mechs->count; i++)
			free(actual_mechs->elements[i].elements);
		free(actual_mechs->elements);
		free(actual_mechs);
	}

	if (desired_mechs != GSS_C_NULL_OID_SET) {
		for (i = 0; i < desired_mechs->count; i++)
			free(desired_mechs->elements[i].elements);
		free(desired_mechs->elements);

	}

/* return to caller */

	return (TRUE);
}

bool_t
gss_add_cred_1_svc(argp, res, rqstp)
	gss_add_cred_arg *argp;
	gss_add_cred_res *res;
	struct svc_req *rqstp;
{

	OM_uint32 		minor_status;
	gss_name_t		desired_name;
	gss_OID_desc		name_type_desc;
	gss_OID			name_type = &name_type_desc;
	gss_OID_desc		desired_mech_type_desc;
	gss_OID			desired_mech_type = &desired_mech_type_desc;
	int			cred_usage;
	gss_cred_id_t 		input_cred_handle;
	gss_OID_set 		actual_mechs;
	gss_buffer_desc		external_name;
	uid_t			uid;
	int			i, j;

	if (gssd_debug)
		fprintf(stderr, gettext("gss_add_cred\n"));

	if (argp->gssd_cred_verifier != gssd_time_verf) {
		res->status = (OM_uint32)GSS_S_DEFECTIVE_CREDENTIAL;
		res->minor_status = 0;
		res->actual_mechs.GSS_OID_SET_len = 0;
		res->actual_mechs.GSS_OID_SET_val = NULL;
		res->initiator_time_rec = 0;
		res->acceptor_time_rec = 0;
		fprintf(stderr, gettext("gss_add_cred defective cred\n"));
		return (TRUE);
	}
	memset(res, 0, sizeof (*res));

	/*
	 * if the request isn't from root, null out the result pointer
	 * entries, so the next time through xdr_free won't try to
	 * free unmalloc'd memory and then return NULL
	 */

	if (checkfrom(rqstp, &uid) == 0) {
		return (FALSE);
	}

/* set the uid sent as the RPC argument */

	uid = argp->uid;
	set_gssd_uid(uid);

/* convert the desired name from external to internal format */

	external_name.length = argp->desired_name.GSS_BUFFER_T_len;
	external_name.value = (void *)argp->desired_name.GSS_BUFFER_T_val;
	name_type->length = argp->name_type.GSS_OID_len;
	name_type->elements = (void *)argp->name_type.GSS_OID_val;

	if (gss_import_name(&minor_status, &external_name, name_type,
			    &desired_name) != GSS_S_COMPLETE) {

		if (gssd_debug)
			fprintf(stderr,
				gettext("gss_add_cred:import name"),
				gettext(" failed status %d \n"),
				res->status);
		res->status = (OM_uint32)GSS_S_FAILURE;
		res->minor_status = minor_status;
		return (TRUE);
	}

/*
 * copy the XDR structured arguments into their corresponding local GSSAPI
 * variables.
 */

	cred_usage = argp->cred_usage;
	if (argp->desired_mech_type.GSS_OID_len == 0)
		desired_mech_type = GSS_C_NULL_OID;
	else {
		desired_mech_type->length =
			(OM_uint32)argp->desired_mech_type.GSS_OID_len;
		desired_mech_type->elements =
			(void *)malloc(desired_mech_type->length);
		if (!desired_mech_type->elements) {
			return (GSS_S_FAILURE);
		}
		memcpy(desired_mech_type->elements,
			argp->desired_mech_type.GSS_OID_val,
			desired_mech_type->length);
	}
	input_cred_handle =
		(argp->input_cred_handle.GSS_CRED_ID_T_len == 0 ?
			GSS_C_NO_CREDENTIAL :
			/*LINTED*/
			*((gss_cred_id_t *)argp->input_cred_handle.
				GSS_CRED_ID_T_val));

	if (input_cred_handle != GSS_C_NO_CREDENTIAL)
	/* verify the input_cred_handle */
		if (argp->gssd_cred_verifier != gssd_time_verf) {
			res->status = (OM_uint32)GSS_S_DEFECTIVE_CREDENTIAL;
			res->minor_status = 0;
			return (TRUE);
		}

	/* call the gssapi routine */

	res->status = (OM_uint32)gss_add_cred(&res->minor_status,
				input_cred_handle,
				desired_name,
				desired_mech_type,
				cred_usage,
				argp->initiator_time_req,
				argp->acceptor_time_req,
				NULL,
				&actual_mechs,
				&res->initiator_time_rec,
				&res->acceptor_time_rec);

	if ((res->status != GSS_S_COMPLETE) &&
		(res->status != GSS_S_DUPLICATE_ELEMENT) &&
		(gssd_debug))
		fprintf(stderr, gettext("gss_add_cred failed status %d \n"),
			res->status);
	/*
	 * convert the output args from the parameter given in the call to the
	 * variable in the XDR result
	 */
	if (actual_mechs != GSS_C_NULL_OID_SET) {
		res->actual_mechs.GSS_OID_SET_len =
			(uint_t)actual_mechs->count;
		res->actual_mechs.GSS_OID_SET_val = (GSS_OID *)
			malloc(sizeof (GSS_OID) * actual_mechs->count);
		if (!res->actual_mechs.GSS_OID_SET_val) {
			free(desired_mech_type->elements);
			free(desired_mech_type);
			return (GSS_S_FAILURE);
		}
		for (i = 0; i < actual_mechs->count; i++) {
			res->actual_mechs.GSS_OID_SET_val[i].GSS_OID_len =
				(uint_t)actual_mechs->elements[i].length;
			res->actual_mechs.GSS_OID_SET_val[i].GSS_OID_val =
				(char *)malloc(actual_mechs->elements[i].
						length);
			if (!res->actual_mechs.GSS_OID_SET_val[i].GSS_OID_val) {
				free(desired_mech_type->elements);
				free(desired_mech_type);
				free(res->actual_mechs.GSS_OID_SET_val);
				for (j = 0; j < (i - 1); j++) {
					free
					(res->actual_mechs.
						GSS_OID_SET_val[j].GSS_OID_val);
				}
				return (GSS_S_FAILURE);
			}
			memcpy(res->actual_mechs.GSS_OID_SET_val[i].GSS_OID_val,
				actual_mechs->elements[i].elements,
				actual_mechs->elements[i].length);
		}
	} else
		res->actual_mechs.GSS_OID_SET_len = 0;

	/*
	 * now release the space allocated for
	 * desired_name  and desired_mech_type
	 */

	gss_release_name(&minor_status, &desired_name);
	free(desired_mech_type->elements);
	gss_release_oid_set(&minor_status, &actual_mechs);
	/*
	 * if (actual_mechs != GSS_C_NULL_OID_SET) {
	 * 	for (i = 0; i < actual_mechs->count; i++)
	 * 		free(actual_mechs->elements[i].elements);
	 * 	free(actual_mechs->elements);
	 * 	free(actual_mechs);
	 * }
	 */


/* return to caller */

	return (TRUE);
}

bool_t
gss_release_cred_1_svc(argp, res, rqstp)
gss_release_cred_arg *argp;
gss_release_cred_res *res;
struct svc_req *rqstp;
{

	uid_t uid;
	gss_cred_id_t cred_handle;

	memset(res, 0, sizeof (*res));

	if (gssd_debug)
		fprintf(stderr, gettext("gss_release_cred\n"));

	if (checkfrom(rqstp, &uid) == 0)
		return (FALSE);

	/* set the uid sent as the RPC argument */

	uid = argp->uid;
	set_gssd_uid(uid);

	/*
	 * if the cred_handle verifier is not correct,
	 * set status to GSS_S_DEFECTIVE_CREDENTIAL and return
	 */

	if (argp->gssd_cred_verifier != gssd_time_verf) {
		res->status = (OM_uint32)GSS_S_DEFECTIVE_CREDENTIAL;
		return (TRUE);
	}

	/*
	 * if the cred_handle length is 0
	 * set cred_handle argument to GSS_S_NO_CREDENTIAL
	 */

	if (argp->cred_handle.GSS_CRED_ID_T_len == 0)
		cred_handle = GSS_C_NO_CREDENTIAL;
	else
		cred_handle =
		(gss_cred_id_t)argp->cred_handle.GSS_CRED_ID_T_val;

	/* call the gssapi routine */

	res->status = (OM_uint32)gss_release_cred(&res->minor_status,
					&cred_handle);

	/* return to caller */

	return (TRUE);
}

bool_t
gss_init_sec_context_1_svc(argp, res, rqstp)
gss_init_sec_context_arg *argp;
gss_init_sec_context_res *res;
struct svc_req *rqstp;
{

	OM_uint32 	minor_status;
	gss_ctx_id_t	context_handle;
	bool_t context_verf_ok;
	gss_cred_id_t	claimant_cred_handle;
	gss_buffer_desc	external_name;
	gss_OID_desc	name_type_desc;
	gss_OID		name_type = &name_type_desc;
	gss_name_t	internal_name;

	gss_OID_desc	mech_type_desc;
	gss_OID		mech_type = &mech_type_desc;
	struct gss_channel_bindings_struct
			input_chan_bindings;
	gss_channel_bindings_t input_chan_bindings_ptr;
	gss_buffer_desc input_token;
	gss_buffer_desc output_token;
	gss_buffer_t input_token_ptr;
	gss_OID actual_mech_type;
	struct gssd_ctx_slot *slot = NULL;

	uid_t uid;

	memset(res, 0, sizeof (*res));

	if (gssd_debug)
		fprintf(stderr, gettext("gss_init_sec_context\n"));

	/*
	 * if the request isn't from root, null out the result pointer
	 * entries, so the next time through xdr_free won't try to
	 * free unmalloc'd memory and then return NULL
	 */

	if (checkfrom(rqstp, &uid) == 0) {
		res->context_handle.GSS_CTX_ID_T_val =  NULL;
		res->actual_mech_type.GSS_OID_val = NULL;
		res->output_token.GSS_BUFFER_T_val = NULL;
		return (FALSE);
	}

/* set the uid sent as the RPC argument */

	uid = argp->uid;
	set_gssd_uid(uid);

/*
 * copy the supplied context handle into the local context handle, so it
 * can be supplied to the gss_init_sec_context call
 */

	gssd_convert_context_handle(&argp->context_handle, &context_handle,
		argp->gssd_context_verifier, &context_verf_ok, &slot);

	claimant_cred_handle =
		(argp->claimant_cred_handle.GSS_CRED_ID_T_len == 0 ?
		GSS_C_NO_CREDENTIAL :
		/*LINTED*/
		*((gss_cred_id_t *)argp->claimant_cred_handle.
			GSS_CRED_ID_T_val));

	if (claimant_cred_handle != GSS_C_NO_CREDENTIAL) {
		/* verify the verifier_cred_handle */
		if (argp->gssd_cred_verifier != gssd_time_verf) {
			res->context_handle.GSS_CTX_ID_T_val = NULL;
			res->output_token.GSS_BUFFER_T_val = NULL;
			res->actual_mech_type.GSS_OID_val = NULL;
			res->context_handle.GSS_CTX_ID_T_len = 0;
			res->output_token.GSS_BUFFER_T_len = 0;
			res->actual_mech_type.GSS_OID_len = 0;
			res->status = (OM_uint32)GSS_S_DEFECTIVE_CREDENTIAL;
			res->minor_status = 0;
			return (TRUE);
		}
	}

	if (context_handle != GSS_C_NO_CONTEXT) {
		/* verify the verifier_context_handle */

		if (!context_verf_ok) {
			res->context_handle.GSS_CTX_ID_T_val = NULL;
			res->output_token.GSS_BUFFER_T_val = NULL;
			res->actual_mech_type.GSS_OID_val = NULL;
			res->context_handle.GSS_CTX_ID_T_len = 0;
			res->output_token.GSS_BUFFER_T_len = 0;
			res->actual_mech_type.GSS_OID_len = 0;
			res->status = (OM_uint32)GSS_S_NO_CONTEXT;
			res->minor_status = 0;
			return (TRUE);
		}
	}

	/* convert the target name from external to internal format */

	external_name.length = argp->target_name.GSS_BUFFER_T_len;
	external_name.value = (void *)argp->target_name.GSS_BUFFER_T_val;

	if (argp->name_type.GSS_OID_len == 0) {
		name_type = GSS_C_NULL_OID;
	} else {
		name_type->length = argp->name_type.GSS_OID_len;
		name_type->elements = (void *)malloc(name_type->length);
		if (!name_type->elements)
			return (GSS_S_FAILURE);
		memcpy(name_type->elements, argp->name_type.GSS_OID_val,
			name_type->length);
	}

	if (argp->mech_type.GSS_OID_len == 0)
		mech_type = GSS_C_NULL_OID;
	else {
		mech_type->length = (OM_uint32)argp->mech_type.GSS_OID_len;
		mech_type->elements = (void *)argp->mech_type.GSS_OID_val;
	}

	if (gss_import_name(&minor_status, &external_name, name_type,
			    &internal_name) != GSS_S_COMPLETE) {

		if (name_type != GSS_C_NULL_OID)
			free(name_type->elements);
		res->status = (OM_uint32)GSS_S_FAILURE;
		res->minor_status = minor_status;

		return (TRUE);
	}
/*
 * copy the XDR structured arguments into their corresponding local GSSAPI
 * variables.
 */

	if (argp->input_chan_bindings.present == YES) {
		input_chan_bindings_ptr = &input_chan_bindings;
		input_chan_bindings.initiator_addrtype =
			(OM_uint32)argp->input_chan_bindings.
			initiator_addrtype;
		input_chan_bindings.initiator_address.length =
			(uint_t)argp->input_chan_bindings.initiator_address.
			GSS_BUFFER_T_len;
		input_chan_bindings.initiator_address.value =
			(void *)argp->input_chan_bindings.initiator_address.
			GSS_BUFFER_T_val;
		input_chan_bindings.acceptor_addrtype =
			(OM_uint32)argp->input_chan_bindings.acceptor_addrtype;
		input_chan_bindings.acceptor_address.length =
			(uint_t)argp->input_chan_bindings.acceptor_address.
			GSS_BUFFER_T_len;
		input_chan_bindings.acceptor_address.value =
			(void *)argp->input_chan_bindings.acceptor_address.
			GSS_BUFFER_T_val;
		input_chan_bindings.application_data.length =
			(uint_t)argp->input_chan_bindings.application_data.
			GSS_BUFFER_T_len;
		input_chan_bindings.application_data.value =
			(void *)argp->input_chan_bindings.application_data.
			GSS_BUFFER_T_val;
	} else {
		input_chan_bindings_ptr = GSS_C_NO_CHANNEL_BINDINGS;
		input_chan_bindings.initiator_addrtype = 0;
		input_chan_bindings.initiator_address.length = 0;
		input_chan_bindings.initiator_address.value = 0;
		input_chan_bindings.acceptor_addrtype = 0;
		input_chan_bindings.acceptor_address.length = 0;
		input_chan_bindings.acceptor_address.value = 0;
		input_chan_bindings.application_data.length = 0;
		input_chan_bindings.application_data.value = 0;
	}

	if (argp->input_token.GSS_BUFFER_T_len == 0) {
		input_token_ptr = GSS_C_NO_BUFFER;
	} else {
		input_token_ptr = &input_token;
		input_token.length = (size_t)
				argp->input_token.GSS_BUFFER_T_len;
		input_token.value = (void *)argp->input_token.GSS_BUFFER_T_val;
	}

/* call the gssapi routine */

	res->status = (OM_uint32)gss_init_sec_context(&res->minor_status,
			(gss_cred_id_t)argp->claimant_cred_handle.
						GSS_CRED_ID_T_val,
					&context_handle,
					internal_name,
					mech_type,
					argp->req_flags,
					argp->time_req,
					input_chan_bindings_ptr,
					input_token_ptr,
					&actual_mech_type,
					&output_token,
					&res->ret_flags,
					&res->time_rec);

	/*
	 * convert the output args from the parameter given in the call to the
	 * variable in the XDR result
	 */

	if (res->status == (OM_uint32)GSS_S_COMPLETE ||
		res->status == (OM_uint32)GSS_S_CONTINUE_NEEDED) {

		if (slot == NULL || slot->ctx != context_handle) {
			/*
			 * Note that gssd_alloc_slot() will delete ctx's as long
			 * as we don't call gssd_rel_slot().
			 */
			slot = gssd_alloc_slot(context_handle);
		}

		res->gssd_context_verifier = slot->verf;

		res->context_handle.GSS_CTX_ID_T_len = sizeof (gss_ctx_id_t);
		res->context_handle.GSS_CTX_ID_T_val =
			(void *)malloc(sizeof (gss_ctx_id_t));
		if (!res->context_handle.GSS_CTX_ID_T_val) {
			free(name_type->elements);
			return (GSS_S_FAILURE);
		}

		memcpy(res->context_handle.GSS_CTX_ID_T_val, &slot->rpcctx,
			sizeof (gss_ctx_id_t));

		res->output_token.GSS_BUFFER_T_len =
			(uint_t)output_token.length;
		res->output_token.GSS_BUFFER_T_val =
			(char *)output_token.value;

		/*
		 * the actual mech type parameter
		 * is ready only upon GSS_S_COMPLETE
		 */
		if (res->status == GSS_S_COMPLETE) {
			res->actual_mech_type.GSS_OID_len =
				(uint_t)actual_mech_type->length;
			res->actual_mech_type.GSS_OID_val =
				(void *)malloc(actual_mech_type->length);
			if (!res->actual_mech_type.GSS_OID_val) {
				free(name_type->elements);
				free(res->context_handle.GSS_CTX_ID_T_val);
				return (GSS_S_FAILURE);
			}
			memcpy(res->actual_mech_type.GSS_OID_val,
				(char *)actual_mech_type->elements,
				actual_mech_type->length);
		} else
			res->actual_mech_type.GSS_OID_len = 0;
	} else {
		if (context_handle != GSS_C_NO_CONTEXT) {
			(void) gss_delete_sec_context(&minor_status,
				&context_handle, NULL);
		}
		res->context_handle.GSS_CTX_ID_T_len = 0;
		res->actual_mech_type.GSS_OID_len = 0;
		res->output_token.GSS_BUFFER_T_len = 0;
	}

	/*
	 * now release the space allocated by the underlying gssapi mechanism
	 * library for internal_name and for the name_type.
	 */

	gss_release_name(&minor_status, &internal_name);
	if (name_type != GSS_C_NULL_OID)
		free(name_type->elements);


	/* return to caller */
	return (TRUE);
}

bool_t
gss_accept_sec_context_1_svc(argp, res, rqstp)
gss_accept_sec_context_arg *argp;
gss_accept_sec_context_res *res;
struct svc_req *rqstp;
{
	uid_t uid;
	OM_uint32 minor_status;
	gss_ctx_id_t context_handle = NULL;
	gss_cred_id_t verifier_cred_handle;
	gss_buffer_desc external_name;
	gss_name_t internal_name = NULL;

	gss_buffer_desc input_token_buffer;
	gss_buffer_t input_token_buffer_ptr;
	struct gss_channel_bindings_struct
			input_chan_bindings;
	gss_channel_bindings_t input_chan_bindings_ptr;
	gss_OID mech_type;
	gss_buffer_desc output_token;
	gss_cred_id_t delegated_cred_handle;
	bool_t context_verf_ok;
	struct gssd_ctx_slot *slot = NULL;

	memset(res, 0, sizeof (*res));

	if (gssd_debug)
		fprintf(stderr, gettext("gss_accept_sec_context\n"));

	/*
	 * if the request isn't from root, null out the result pointer
	 * entries, so the next time through xdr_free won't try to
	 * free unmalloc'd memory and then return NULL
	 */

	if (checkfrom(rqstp, &uid) == 0) {
		res->context_handle.GSS_CTX_ID_T_val = NULL;
		res->src_name.GSS_BUFFER_T_val = NULL;
		res->mech_type.GSS_OID_val = NULL;
		res->output_token.GSS_BUFFER_T_val = NULL;
		res->delegated_cred_handle.GSS_CRED_ID_T_val = NULL;
		return (FALSE);
	}

	/* set the uid sent as the RPC argument */

	uid = argp->uid;
	set_gssd_uid(uid);

	/*
	 * copy the supplied context handle into the local context handle, so
	 * it can be supplied to the gss_accept_sec_context call
	 */

	gssd_convert_context_handle(&argp->context_handle, &context_handle,
		argp->gssd_context_verifier, &context_verf_ok, &slot);

	if (context_handle != GSS_C_NO_CONTEXT)
		/* verify the context_handle */
		if (!context_verf_ok) {
			res->context_handle.GSS_CTX_ID_T_val = NULL;
			res->src_name.GSS_BUFFER_T_val = NULL;
			res->mech_type.GSS_OID_val = NULL;
			res->output_token.GSS_BUFFER_T_val = NULL;
			res->delegated_cred_handle.GSS_CRED_ID_T_val = NULL;
			res->src_name.GSS_BUFFER_T_len = 0;
			res->context_handle.GSS_CTX_ID_T_len = 0;
			res->delegated_cred_handle.GSS_CRED_ID_T_len = 0;
			res->output_token.GSS_BUFFER_T_len = 0;
			res->mech_type.GSS_OID_len = 0;
			res->status = (OM_uint32)GSS_S_NO_CONTEXT;
			res->minor_status = 0;
			return (TRUE);
		}

	/*
	 * copy the XDR structured arguments into their corresponding local
	 * GSSAPI variable equivalents.
	 */


	verifier_cred_handle =
		(argp->verifier_cred_handle.GSS_CRED_ID_T_len == 0 ?
			GSS_C_NO_CREDENTIAL :
			/*LINTED*/
			*((gss_cred_id_t *)argp->verifier_cred_handle.
				GSS_CRED_ID_T_val));

	if (verifier_cred_handle != GSS_C_NO_CREDENTIAL)
	/* verify the verifier_cred_handle */
		if (argp->gssd_cred_verifier != gssd_time_verf) {
			res->context_handle.GSS_CTX_ID_T_val = NULL;
			res->src_name.GSS_BUFFER_T_val = NULL;
			res->mech_type.GSS_OID_val = NULL;
			res->output_token.GSS_BUFFER_T_val = NULL;
			res->delegated_cred_handle.GSS_CRED_ID_T_val = NULL;
			res->src_name.GSS_BUFFER_T_len = 0;
			res->context_handle.GSS_CTX_ID_T_len = 0;
			res->delegated_cred_handle.GSS_CRED_ID_T_len = 0;
			res->output_token.GSS_BUFFER_T_len = 0;
			res->mech_type.GSS_OID_len = 0;
			res->status = (OM_uint32)GSS_S_DEFECTIVE_CREDENTIAL;
			res->minor_status = 0;
			return (TRUE);
		}

	input_token_buffer_ptr = &input_token_buffer;
	input_token_buffer.length = (size_t)argp->input_token_buffer.
		GSS_BUFFER_T_len;
	input_token_buffer.value = (void *)argp->input_token_buffer.
		GSS_BUFFER_T_val;

	if (argp->input_chan_bindings.present == YES) {
		input_chan_bindings_ptr = &input_chan_bindings;
		input_chan_bindings.initiator_addrtype =
			(OM_uint32)argp->input_chan_bindings.
					initiator_addrtype;
		input_chan_bindings.initiator_address.length =
			(uint_t)argp->input_chan_bindings.initiator_address.
					GSS_BUFFER_T_len;
		input_chan_bindings.initiator_address.value =
			(void *)argp->input_chan_bindings.initiator_address.
					GSS_BUFFER_T_val;
		input_chan_bindings.acceptor_addrtype =
			(OM_uint32)argp->input_chan_bindings.
					acceptor_addrtype;
		input_chan_bindings.acceptor_address.length =
			(uint_t)argp->input_chan_bindings.acceptor_address.
					GSS_BUFFER_T_len;
		input_chan_bindings.acceptor_address.value =
			(void *)argp->input_chan_bindings.acceptor_address.
					GSS_BUFFER_T_val;
		input_chan_bindings.application_data.length =
			(uint_t)argp->input_chan_bindings.application_data.
					GSS_BUFFER_T_len;
		input_chan_bindings.application_data.value =
			(void *)argp->input_chan_bindings.application_data.
					GSS_BUFFER_T_val;
	} else {
		input_chan_bindings_ptr = GSS_C_NO_CHANNEL_BINDINGS;
		input_chan_bindings.initiator_addrtype = 0;
		input_chan_bindings.initiator_address.length = 0;
		input_chan_bindings.initiator_address.value = 0;
		input_chan_bindings.acceptor_addrtype = 0;
		input_chan_bindings.acceptor_address.length = 0;
		input_chan_bindings.acceptor_address.value = 0;
		input_chan_bindings.application_data.length = 0;
		input_chan_bindings.application_data.value = 0;
	}


	/* call the gssapi routine */

	res->status = (OM_uint32)gss_accept_sec_context(&res->minor_status,
						&context_handle,
						verifier_cred_handle,
						input_token_buffer_ptr,
						input_chan_bindings_ptr,
						&internal_name,
						&mech_type,
						&output_token,
						&res->ret_flags,
						&res->time_rec,
						&delegated_cred_handle);

	/* convert the src name from internal to external format */

	if (res->status == (OM_uint32)GSS_S_COMPLETE ||
		res->status == (OM_uint32)GSS_S_CONTINUE_NEEDED) {

		/*
		 * upon GSS_S_CONTINUE_NEEDED only the following
		 * parameters are ready: minor, ctxt, and output token
		 */
		res->context_handle.GSS_CTX_ID_T_len = sizeof (gss_ctx_id_t);
		res->context_handle.GSS_CTX_ID_T_val =
			(void *)malloc(sizeof (gss_ctx_id_t));
		if (!res->context_handle.GSS_CTX_ID_T_val) {
			res->status = (OM_uint32)GSS_S_FAILURE;
			res->minor_status = 0;
			return (TRUE);
		}

		if (slot == NULL || slot->ctx != context_handle) {
			/*
			 * Note that gssd_alloc_slot() will delete ctx's as long
			 * as we don't call gssd_rel_slot().
			 */
			slot = gssd_alloc_slot(context_handle);
		}

		memcpy(res->context_handle.GSS_CTX_ID_T_val, &slot->rpcctx,
			sizeof (gss_ctx_id_t));
		res->gssd_context_verifier = slot->verf;

		res->output_token.GSS_BUFFER_T_len =
				(uint_t)output_token.length;
		res->output_token.GSS_BUFFER_T_val =
				(char *)output_token.value;

		if (res->status == GSS_S_COMPLETE) {
			if (gss_export_name(&minor_status, internal_name,
					&external_name)
				!= GSS_S_COMPLETE) {

				res->status = (OM_uint32)GSS_S_FAILURE;
				res->minor_status = minor_status;
				gss_release_name(&minor_status, &internal_name);
				gss_delete_sec_context(&minor_status,
						&context_handle, NULL);
				free(res->context_handle.GSS_CTX_ID_T_val);
				res->context_handle.GSS_CTX_ID_T_val = NULL;
				res->context_handle.GSS_CTX_ID_T_len = 0;
				gss_release_buffer(&minor_status,
						&output_token);
				res->output_token.GSS_BUFFER_T_len = 0;
				res->output_token.GSS_BUFFER_T_val = NULL;
				return (TRUE);
			}
			res->src_name.GSS_BUFFER_T_len =
				(uint_t)external_name.length;
			res->src_name.GSS_BUFFER_T_val =
				(void *)external_name.value;

			res->delegated_cred_handle.GSS_CRED_ID_T_len =
				sizeof (gss_cred_id_t);
			res->delegated_cred_handle.GSS_CRED_ID_T_val =
				(void *)malloc(sizeof (gss_cred_id_t));
			if (!res->delegated_cred_handle.GSS_CRED_ID_T_val) {
				free(res->context_handle.GSS_CTX_ID_T_val);
				gss_release_name(&minor_status, &internal_name);
				gss_delete_sec_context(&minor_status,
						&context_handle, NULL);
				gss_release_buffer(&minor_status,
						&external_name);
				res->status = (OM_uint32)GSS_S_FAILURE;
				res->minor_status = 0;
				return (TRUE);
			}
			memcpy(res->delegated_cred_handle.GSS_CRED_ID_T_val,
				&delegated_cred_handle,
				sizeof (gss_cred_id_t));

			res->mech_type.GSS_OID_len = (uint_t)mech_type->length;
			res->mech_type.GSS_OID_val =
				(void *)malloc(mech_type->length);
			if (!res->mech_type.GSS_OID_val) {
			    free(res->context_handle.GSS_CTX_ID_T_val);
			    free(res->delegated_cred_handle.GSS_CRED_ID_T_val);
			    gss_release_name(&minor_status, &internal_name);
			    gss_delete_sec_context(&minor_status,
						&context_handle, NULL);
			    gss_release_buffer(&minor_status, &external_name);
			    res->status = (OM_uint32)GSS_S_FAILURE;
			    res->minor_status = 0;
			    return (TRUE);
			}
			memcpy(res->mech_type.GSS_OID_val, mech_type->elements,
				mech_type->length);

			/* release the space allocated for internal_name */
			gss_release_name(&minor_status, &internal_name);

		} else {    /* GSS_S_CONTINUE_NEEDED */
			res->src_name.GSS_BUFFER_T_len = 0;
			res->delegated_cred_handle.GSS_CRED_ID_T_len = 0;
			res->mech_type.GSS_OID_len = 0;
		}
	} else {
		if (context_handle != GSS_C_NO_CONTEXT) {
			(void) gss_delete_sec_context(&minor_status,
				&context_handle, NULL);
		}
		res->src_name.GSS_BUFFER_T_len = 0;
		res->context_handle.GSS_CTX_ID_T_len = 0;
                res->delegated_cred_handle.GSS_CRED_ID_T_len = 0;
                res->output_token.GSS_BUFFER_T_len =
			(uint_t)output_token.length;
                res->output_token.GSS_BUFFER_T_val =
			(char *)output_token.value;

                res->mech_type.GSS_OID_len = 0;
	}

/* return to caller */

	return (TRUE);
}

bool_t
gss_process_context_token_1_svc(argp, res, rqstp)
gss_process_context_token_arg *argp;
gss_process_context_token_res *res;
struct svc_req *rqstp;
{

	uid_t uid;
	gss_buffer_desc token_buffer;
	gss_ctx_id_t context_handle;
	bool_t context_verf_ok;

	memset(res, 0, sizeof (*res));

	if (gssd_debug)
		fprintf(stderr, gettext("gss_process_context_token\n"));

	if (checkfrom(rqstp, &uid) == 0)
		return (FALSE);

	gssd_convert_context_handle(&argp->context_handle, &context_handle,
		argp->gssd_context_verifier, &context_verf_ok, NULL);

	/* verify the context_handle */

	if (!context_verf_ok) {
		res->status = (OM_uint32) GSS_S_NO_CONTEXT;
		res->minor_status = 0;
		return (TRUE);
	}

	/* set the uid sent as the RPC argument */

	uid = argp->uid;
	set_gssd_uid(uid);

	/*
	 * copy the XDR structured arguments into their corresponding local
	 * GSSAPI variable equivalents.
	 */

	token_buffer.length = (size_t)argp->token_buffer.GSS_BUFFER_T_len;
	token_buffer.value = (void *)argp->token_buffer.GSS_BUFFER_T_val;


	/* call the gssapi routine */

	res->status = (OM_uint32)gss_process_context_token(&res->minor_status,
				context_handle,
				&token_buffer);


	/* return to caller */

	return (TRUE);
}

bool_t
gss_delete_sec_context_1_svc(argp, res, rqstp)
gss_delete_sec_context_arg *argp;
gss_delete_sec_context_res *res;
struct svc_req *rqstp;
{
	uid_t uid;
	gss_ctx_id_t  context_handle;
	gss_buffer_desc output_token;
	bool_t context_verf_ok;
	struct gssd_ctx_slot *slot = NULL;

	memset(res, 0, sizeof (*res));

	if (gssd_debug)
		fprintf(stderr, gettext("gss_delete_sec_context\n"));


	/*
	 * copy the supplied context handle into the local context handle, so it
	 * can be supplied to the gss_delete_sec_context call
	 */
	gssd_convert_context_handle(&argp->context_handle, &context_handle,
		argp->gssd_context_verifier, &context_verf_ok, &slot);

	/* verify the context_handle */
	if (!context_verf_ok) {
		res->context_handle.GSS_CTX_ID_T_val = NULL;
		res->context_handle.GSS_CTX_ID_T_len = 0;
		res->output_token.GSS_BUFFER_T_val = NULL;
		res->output_token.GSS_BUFFER_T_len = 0;
		res->status = (OM_uint32)GSS_S_NO_CONTEXT;
		res->minor_status = 0;
		return (TRUE);
	}

	/*
	 * if the request isn't from root, null out the result pointer
	 * entries, so the next time through xdr_free won't try to
	 * free unmalloc'd memory and then return NULL
	 */

	if (checkfrom(rqstp, &uid) == 0) {
		res->context_handle.GSS_CTX_ID_T_val = NULL;
		res->output_token.GSS_BUFFER_T_val = NULL;
		return (FALSE);
	}

	/* call the gssapi routine */

	res->status = (OM_uint32)gss_delete_sec_context(&res->minor_status,
						&context_handle,
						&output_token);

	/*
	 * convert the output args from the parameter given in the call to the
	 * variable in the XDR result. If the delete succeeded, return a zero
	 * context handle.
	 */

	if (res->status == GSS_S_COMPLETE) {
		if (context_handle != GSS_C_NO_CONTEXT)
			return (GSS_S_FAILURE);
		res->context_handle.GSS_CTX_ID_T_len = 0;
		res->context_handle.GSS_CTX_ID_T_val = NULL;
		res->output_token.GSS_BUFFER_T_len =
			(uint_t)output_token.length;
		res->output_token.GSS_BUFFER_T_val =
			(char *)output_token.value;

		if (slot != NULL) {
			/*
			 * gss_delete_sec_context deletes the context if it
			 * succeeds so clear slot->ctx to avoid a dangling
			 * reference.
			 */
			slot->ctx = GSS_C_NO_CONTEXT;
			gssd_rel_slot(slot);
		}
	} else {
		res->context_handle.GSS_CTX_ID_T_len = sizeof (gss_ctx_id_t);
		res->context_handle.GSS_CTX_ID_T_val =
			(void *)malloc(sizeof (gss_ctx_id_t));
		if (!res->context_handle.GSS_CTX_ID_T_val) {
			return (GSS_S_FAILURE);
		}

		if (slot == NULL || slot->ctx != context_handle) {
			/*
			 * Note that gssd_alloc_slot() will delete ctx's as long
			 * as we don't call gssd_rel_slot().
			 */
			slot = gssd_alloc_slot(context_handle);
			/*
			 * Note that no verifier is returned in the .x
			 * protocol. So if the context changes, we won't
			 * be able to release it now. So it will have to
			 * be LRUed out.
			 */
		}

		memcpy(res->context_handle.GSS_CTX_ID_T_val, &slot->rpcctx,
			sizeof (gss_ctx_id_t));

		res->output_token.GSS_BUFFER_T_len = 0;
		res->output_token.GSS_BUFFER_T_val = NULL;
	}

	/* return to caller */


	return (TRUE);
}


bool_t
gss_export_sec_context_1_svc(argp, res, rqstp)
	gss_export_sec_context_arg *argp;
	gss_export_sec_context_res *res;
	struct svc_req *rqstp;
{

	uid_t		uid;
	gss_ctx_id_t	context_handle;
	gss_buffer_desc	output_token;
	bool_t		context_verf_ok;
	struct gssd_ctx_slot *slot = NULL;

	memset(res, 0, sizeof (*res));

	if (gssd_debug)
		fprintf(stderr, "gss_export_sec_context\n");

	/*
	 * if the request isn't from root, null out the result pointer
	 * entries, so the next time through xdr_free won't try to
	 * free unmalloc'd memory and then return NULL
	 */

	if (checkfrom(rqstp, &uid) == 0) {
		res->context_handle.GSS_CTX_ID_T_val = NULL;
		res->output_token.GSS_BUFFER_T_val = NULL;
		return (FALSE);
	}

/*
 * copy the supplied context handle into the local context handle, so it
 * can be supplied to the gss_export_sec_context call
 */

	gssd_convert_context_handle(&argp->context_handle, &context_handle,
		argp->gssd_context_verifier, &context_verf_ok, &slot);

	/* verify the context_handle */

	if (!context_verf_ok) {
		res->status = (OM_uint32)GSS_S_NO_CONTEXT;
		/* the rest of "res" was cleared by a previous memset() */
		return (TRUE);
	}

	/* call the gssapi routine */

	res->status = (OM_uint32)gss_export_sec_context(&res->minor_status,
					&context_handle,
					&output_token);

/*
 * convert the output args from the parameter given in the call to the
 * variable in the XDR result. If the delete succeeded, return a zero context
 * handle.
 */
	if (res->status == GSS_S_COMPLETE) {
		if (context_handle != GSS_C_NO_CONTEXT)
			return (GSS_S_FAILURE);
		res->context_handle.GSS_CTX_ID_T_len = 0;
		res->context_handle.GSS_CTX_ID_T_val = NULL;
		res->output_token.GSS_BUFFER_T_len =
						(uint_t)output_token.length;
		res->output_token.GSS_BUFFER_T_val =
						(char *)output_token.value;

		if (slot != NULL) {
			/*
			 * gss_export_sec_context deletes the context if it
			 * succeeds so set slot->ctx to avoid a dangling
			 * reference.
			 */
			slot->ctx = GSS_C_NO_CONTEXT;
			gssd_rel_slot(slot);
		}
	} else {
		res->context_handle.GSS_CTX_ID_T_len = sizeof (gss_ctx_id_t);
		res->context_handle.GSS_CTX_ID_T_val =
					(void *)malloc(sizeof (gss_ctx_id_t));

		if (slot == NULL || slot->ctx != context_handle) {
			/*
			 * Note that gssd_alloc_slot() will delete ctx's as long
			 * as we don't call gssd_rel_slot().
			 */
			slot = gssd_alloc_slot(context_handle);
			/*
			 * Note that no verifier is returned in the .x
			 * protocol. So if the context changes, we won't
			 * be able to release it now. So it will have to
			 * be LRUed out.
			 */
		}

		memcpy(res->context_handle.GSS_CTX_ID_T_val, &slot->rpcctx,
			sizeof (gss_ctx_id_t));
		res->output_token.GSS_BUFFER_T_len = 0;
		res->output_token.GSS_BUFFER_T_val = NULL;
	}


	/* return to caller */

	return (TRUE);
}

/*
 * This routine doesn't appear to ever be called.
 */
bool_t
gss_import_sec_context_1_svc(argp, res, rqstp)
	gss_import_sec_context_arg *argp;
	gss_import_sec_context_res *res;
	struct svc_req *rqstp;
{

	uid_t		uid;
	gss_ctx_id_t	context_handle;
	gss_buffer_desc	input_token;
	gss_buffer_t input_token_ptr;

	memset(res, 0, sizeof (*res));

	if (gssd_debug)
		fprintf(stderr, "gss_export_sec_context\n");

	/*
	 * if the request isn't from root, null out the result pointer
	 * entries, so the next time through xdr_free won't try to
	 * free unmalloc'd memory and then return NULL
	 */

	if (checkfrom(rqstp, &uid) == 0) {
		res->context_handle.GSS_CTX_ID_T_val = NULL;
		return (FALSE);
	}


	if (argp->input_token.GSS_BUFFER_T_len == 0) {
		input_token_ptr = GSS_C_NO_BUFFER;
	} else {
		input_token_ptr = &input_token;
		input_token.length = (size_t)
				argp->input_token.GSS_BUFFER_T_len;
		input_token.value = (void *) argp->input_token.GSS_BUFFER_T_val;
	}


/* call the gssapi routine */

	res->status = (OM_uint32) gss_import_sec_context(&res->minor_status,
					input_token_ptr,
					&context_handle);

/*
 * convert the output args from the parameter given in the call to the
 * variable in the XDR result. If the delete succeeded, return a zero context
 * handle.
 */
	if (res->status == GSS_S_COMPLETE) {
		res->context_handle.GSS_CTX_ID_T_len = sizeof (gss_ctx_id_t);
		res->context_handle.GSS_CTX_ID_T_val =
					(void *) malloc(sizeof (gss_ctx_id_t));
		memcpy(res->context_handle.GSS_CTX_ID_T_val, &context_handle,
			sizeof (gss_ctx_id_t));
	} else {
		res->context_handle.GSS_CTX_ID_T_len = 0;
		res->context_handle.GSS_CTX_ID_T_val = NULL;
	}


	/* return to caller */

	return (TRUE);
}

bool_t
gss_context_time_1_svc(argp, res, rqstp)
gss_context_time_arg *argp;
gss_context_time_res *res;
struct svc_req *rqstp;
{
	uid_t uid;

	memset(res, 0, sizeof (*res));

	if (gssd_debug)
		fprintf(stderr, gettext("gss_context_time\n"));

	/*
	 * if the request isn't from root, null out the result pointer
	 * entries, so the next time through xdr_free won't try to
	 * free unmalloc'd memory and then return NULL
	 */

	if (checkfrom(rqstp, &uid) == 0)
		return (FALSE);

	/* set the uid sent as the RPC argument */

	uid = argp->uid;
	set_gssd_uid(uid);

	/* Semantics go here */

	return (TRUE);
}

bool_t
gss_sign_1_svc(argp, res, rqstp)
gss_sign_arg *argp;
gss_sign_res *res;
struct svc_req *rqstp;
{

	uid_t uid;

	gss_buffer_desc message_buffer;
	gss_buffer_desc msg_token;
	gss_ctx_id_t	context_handle;
	bool_t context_verf_ok;

	memset(res, 0, sizeof (*res));

	if (gssd_debug)
		fprintf(stderr, gettext("gss_sign\n"));

	gssd_convert_context_handle(&argp->context_handle, &context_handle,
		argp->gssd_context_verifier, &context_verf_ok, NULL);

	/* verify the context_handle */
	if (!context_verf_ok) {
		res->msg_token.GSS_BUFFER_T_val = NULL;
		res->msg_token.GSS_BUFFER_T_len = 0;
		res->status = (OM_uint32) GSS_S_NO_CONTEXT;
		res->minor_status = 0;
		return (TRUE);
	}


	/*
	 * if the request isn't from root, null out the result pointer
	 * entries, so the next time through xdr_free won't try to
	 * free unmalloc'd memory and then return NULL
	 */

	if (checkfrom(rqstp, &uid) == 0) {
		res->msg_token.GSS_BUFFER_T_val = NULL;
		return (FALSE);
	}

	/*
	 * copy the XDR structured arguments into their corresponding local
	 * GSSAPI variable equivalents.
	 */

	message_buffer.length = (size_t)argp->message_buffer.GSS_BUFFER_T_len;
	message_buffer.value = (void *)argp->message_buffer.GSS_BUFFER_T_val;

	/* call the gssapi routine */

	res->status = (OM_uint32)gss_sign(&res->minor_status,
					context_handle,
					argp->qop_req,
					(gss_buffer_t)&message_buffer,
					(gss_buffer_t)&msg_token);
	/*
	 * convert the output args from the parameter given in the call to
	 * the variable in the XDR result
	 */

	if (res->status == GSS_S_COMPLETE) {
		res->msg_token.GSS_BUFFER_T_len = (uint_t)msg_token.length;
		res->msg_token.GSS_BUFFER_T_val = (char *)msg_token.value;
	}

	/* return to caller */

	return (TRUE);
}

bool_t
gss_verify_1_svc(argp, res, rqstp)
gss_verify_arg *argp;
gss_verify_res *res;
struct svc_req *rqstp;
{

	uid_t uid;

	gss_buffer_desc message_buffer;
	gss_buffer_desc token_buffer;
	gss_ctx_id_t context_handle;
	bool_t context_verf_ok;

	memset(res, 0, sizeof (*res));

	if (gssd_debug)
		fprintf(stderr, gettext("gss_verify\n"));

	gssd_convert_context_handle(&argp->context_handle, &context_handle,
		argp->gssd_context_verifier, &context_verf_ok, NULL);

	/* verify the context_handle */
	if (!context_verf_ok) {
		res->status = (OM_uint32) GSS_S_NO_CONTEXT;
		res->minor_status = 0;
		return (TRUE);
	}

	/*
	 * if the request isn't from root, null out the result pointer
	 * entries, so the next time through xdr_free won't try to
	 * free unmalloc'd memory and then return NULL
	 */

	if (checkfrom(rqstp, &uid) == 0)
		return (FALSE);

	/*
	 * copy the XDR structured arguments into their corresponding local
	 * GSSAPI variable equivalents.
	 */

	message_buffer.length = (size_t)argp->message_buffer.GSS_BUFFER_T_len;
	message_buffer.value = (void *)argp->message_buffer.GSS_BUFFER_T_val;

	token_buffer.length = (size_t)argp->token_buffer.GSS_BUFFER_T_len;
	token_buffer.value = (void *)argp->token_buffer.GSS_BUFFER_T_val;

	/* call the gssapi routine */

	res->status = (OM_uint32)gss_verify(&res->minor_status,
				context_handle,
				&message_buffer,
				&token_buffer,
				&res->qop_state);

	/* return to caller */

	return (TRUE);
}

/* EXPORT DELETE START */

bool_t
gss_seal_1_svc(argp, res, rqstp)
gss_seal_arg *argp;
gss_seal_res *res;
struct svc_req *rqstp;
{
	uid_t uid;

	gss_buffer_desc input_message_buffer;
	gss_buffer_desc output_message_buffer;
	gss_ctx_id_t context_handle;
	bool_t context_verf_ok;

	memset(res, 0, sizeof (*res));

	if (gssd_debug)
		fprintf(stderr, gettext("gss_seal\n"));

	gssd_convert_context_handle(&argp->context_handle, &context_handle,
		argp->gssd_context_verifier, &context_verf_ok, NULL);

	/* verify the context_handle */

	if (!context_verf_ok) {
		res->output_message_buffer.GSS_BUFFER_T_val = NULL;
		res->output_message_buffer.GSS_BUFFER_T_len = 0;
		res->status = (OM_uint32) GSS_S_NO_CONTEXT;
		res->minor_status = 0;
		return (TRUE);
	}

	/*
	 * if the request isn't from root, null out the result pointer
	 * entries, so the next time through xdr_free won't try to
	 * free unmalloc'd memory and then return NULL
	 */

	if (checkfrom(rqstp, &uid) == 0) {
		res->output_message_buffer.GSS_BUFFER_T_val = NULL;
		return (FALSE);

	}


	/*
	 * copy the XDR structured arguments into their corresponding local
	 * GSSAPI variable equivalents.
	 */

	input_message_buffer.length = (size_t)argp->input_message_buffer.
					GSS_BUFFER_T_len;
	input_message_buffer.value = (void *)argp->input_message_buffer.
					GSS_BUFFER_T_val;


	/* call the gssapi routine */

	res->status = (OM_uint32)gss_seal(&res->minor_status,
				context_handle,
				argp->conf_req_flag,
				argp->qop_req,
				&input_message_buffer,
				&res->conf_state,
				&output_message_buffer);
	/*
	 * convert the output args from the parameter given in the call to the
	 * variable in the XDR result
	 */

	if (res->status == GSS_S_COMPLETE) {
		res->output_message_buffer.GSS_BUFFER_T_len =
				(uint_t)output_message_buffer.length;
		res->output_message_buffer.GSS_BUFFER_T_val =
				(char *)output_message_buffer.value;
	}

/* return to caller */

	return (TRUE);
}

bool_t
gss_unseal_1_svc(argp, res, rqstp)
gss_unseal_arg *argp;
gss_unseal_res *res;
struct svc_req *rqstp;
{

	uid_t uid;

	gss_buffer_desc input_message_buffer;
	gss_buffer_desc output_message_buffer;
	gss_ctx_id_t context_handle;
	bool_t context_verf_ok;

	memset(res, 0, sizeof (*res));

	if (gssd_debug)
		fprintf(stderr, gettext("gss_unseal\n"));

	/* verify the context_handle */
	gssd_convert_context_handle(&argp->context_handle, &context_handle,
		argp->gssd_context_verifier, &context_verf_ok, NULL);

	/* verify the context_handle */
	if (!context_verf_ok) {
		res->output_message_buffer.GSS_BUFFER_T_val = NULL;
		res->output_message_buffer.GSS_BUFFER_T_len = 0;
		res->status = (OM_uint32)GSS_S_NO_CONTEXT;
		res->minor_status = 0;
		return (TRUE);
	}

	/*
	 * if the request isn't from root, null out the result pointer
	 * entries, so the next time through xdr_free won't try to
	 * free unmalloc'd memory and then return NULL
	 */

	if (checkfrom(rqstp, &uid) == 0) {
		res->output_message_buffer.GSS_BUFFER_T_val = NULL;
		return (FALSE);
	}


	/*
	 * copy the XDR structured arguments into their corresponding local
	 * GSSAPI variable equivalents.
	 */

	input_message_buffer.length = (size_t)argp->input_message_buffer.
					GSS_BUFFER_T_len;
	input_message_buffer.value = (void *)argp->input_message_buffer.
					GSS_BUFFER_T_val;

	/* call the gssapi routine */

	res->status = (OM_uint32)gss_unseal(&res->minor_status,
				context_handle,
				&input_message_buffer,
				&output_message_buffer,
				&res->conf_state,
				&res->qop_state);

	/*
	 * convert the output args from the parameter given in the call to the
	 * variable in the XDR result
	 */

	if (res->status == GSS_S_COMPLETE) {
		res->output_message_buffer.GSS_BUFFER_T_len =
				(uint_t)output_message_buffer.length;
		res->output_message_buffer.GSS_BUFFER_T_val =
				(char *)output_message_buffer.value;
	}


	/* return to caller */

	return (TRUE);
}

/* EXPORT DELETE END */

bool_t
gss_display_status_1_svc(argp, res, rqstp)
gss_display_status_arg *argp;
gss_display_status_res *res;
struct svc_req *rqstp;
{
	uid_t uid;
	gss_OID mech_type;
	gss_OID_desc mech_type_desc;
	gss_buffer_desc status_string;

	memset(res, 0, sizeof (*res));

	if (gssd_debug)
		fprintf(stderr, gettext("gss_display_status\n"));

	/*
	 * if the request isn't from root, null out the result pointer
	 * entries, so the next time through xdr_free won't try to
	 * free unmalloc'd memory and then return NULL
	 */

	if (checkfrom(rqstp, &uid) == 0) {
		res->status_string.GSS_BUFFER_T_val = NULL;
		return (FALSE);
	}

	/* set the uid sent as the RPC argument */

	uid = argp->uid;
	set_gssd_uid(uid);

	/*
	 * copy the XDR structured arguments into their corresponding local
	 * GSSAPI variables.
	 */

	if (argp->mech_type.GSS_OID_len == 0)
		mech_type = GSS_C_NULL_OID;
	else {
		mech_type = &mech_type_desc;
		mech_type_desc.length = (OM_uint32) argp->mech_type.GSS_OID_len;
		mech_type_desc.elements = (void *) argp->mech_type.GSS_OID_val;
	}


	/* call the gssapi routine */

	res->status = (OM_uint32) gss_display_status(&res->minor_status,
					argp->status_value,
					argp->status_type,
					mech_type,
					(OM_uint32 *)&res->message_context,
					&status_string);

	/*
	 * convert the output args from the parameter given in the call to the
	 * variable in the XDR result
	 */

	if (res->status == GSS_S_COMPLETE) {
		res->status_string.GSS_BUFFER_T_len =
			(uint_t)status_string.length;
		res->status_string.GSS_BUFFER_T_val =
			(char *)status_string.value;
	}

	return (TRUE);

}

/*ARGSUSED*/
bool_t
gss_indicate_mechs_1_svc(argp, res, rqstp)
	void *argp;
	gss_indicate_mechs_res *res;
	struct svc_req *rqstp;
{
	gss_OID_set oid_set;
	uid_t uid;

	memset(res, 0, sizeof (*res));

	if (gssd_debug)
		fprintf(stderr, gettext("gss_indicate_mechs\n"));

	res->mech_set.GSS_OID_SET_val = NULL;

	/*
	 * if the request isn't from root, null out the result pointer
	 * entries, so the next time through xdr_free won't try to
	 * free unmalloc'd memory and then return NULL
	 */

	if (checkfrom(rqstp, &uid) == 0) {
		return (FALSE);
	}

	res->status = gss_indicate_mechs(&res->minor_status, &oid_set);

	if (res->status == GSS_S_COMPLETE) {
		int i, j;

		res->mech_set.GSS_OID_SET_len = oid_set->count;
		res->mech_set.GSS_OID_SET_val = (void *)
				malloc(oid_set->count * sizeof (GSS_OID));
		if (!res->mech_set.GSS_OID_SET_val) {
			return (GSS_S_FAILURE);
		}
		for (i = 0; i < oid_set->count; i++) {
			res->mech_set.GSS_OID_SET_val[i].GSS_OID_len =
				oid_set->elements[i].length;
			res->mech_set.GSS_OID_SET_val[i].GSS_OID_val =
				(char *)malloc(oid_set->elements[i].length);
			if (!res->mech_set.GSS_OID_SET_val[i].GSS_OID_val) {
				for (j = 0; j < (i -1); j++) {
				free
				(res->mech_set.GSS_OID_SET_val[i].GSS_OID_val);
				}
				free(res->mech_set.GSS_OID_SET_val);
				return (GSS_S_FAILURE);
			}
			memcpy(res->mech_set.GSS_OID_SET_val[i].GSS_OID_val,
				oid_set->elements[i].elements,
				oid_set->elements[i].length);
		}
	}

	return (TRUE);
}

bool_t
gss_inquire_cred_1_svc(argp, res, rqstp)
gss_inquire_cred_arg *argp;
gss_inquire_cred_res *res;
struct svc_req *rqstp;
{

	uid_t uid;

	OM_uint32 minor_status;
	gss_cred_id_t cred_handle;
	gss_buffer_desc external_name;
	gss_OID name_type;
	gss_name_t internal_name;
	gss_OID_set mechanisms;
	int i, j;

	memset(res, 0, sizeof (*res));

	if (gssd_debug)
		fprintf(stderr, gettext("gss_inquire_cred\n"));

	/* verify the verifier_cred_handle */

	if (argp->gssd_cred_verifier != gssd_time_verf) {
		res->name.GSS_BUFFER_T_val = NULL;
		res->name_type.GSS_OID_val = NULL;
		res->mechanisms.GSS_OID_SET_val = NULL;
		res->status = (OM_uint32) GSS_S_DEFECTIVE_CREDENTIAL;
		res->minor_status = 0;
		return (TRUE);
	}

	/*
	 * if the request isn't from root, null out the result pointer
	 * entries, so the next time through xdr_free won't try to
	 * free unmalloc'd memory and then return NULL
	 */

	if (checkfrom(rqstp, &uid) == 0) {
		res->name.GSS_BUFFER_T_val = NULL;
		res->name_type.GSS_OID_val = NULL;
		res->mechanisms.GSS_OID_SET_val = NULL;
		return (FALSE);
	}

	/* set the uid sent as the RPC argument */

	uid = argp->uid;
	set_gssd_uid(uid);

	cred_handle = (argp->cred_handle.GSS_CRED_ID_T_len == 0 ?
			GSS_C_NO_CREDENTIAL :
			/*LINTED*/
			*((gss_cred_id_t *)argp->cred_handle.
				GSS_CRED_ID_T_val));

	/* call the gssapi routine */

	res->status = (OM_uint32)gss_inquire_cred(&res->minor_status,
					cred_handle,
					&internal_name,
					&res->lifetime,
					&res->cred_usage,
					&mechanisms);

	if (res->status != GSS_S_COMPLETE)
		return (TRUE);

	/* convert the returned name from internal to external format */

	if (gss_display_name(&minor_status, internal_name,
				&external_name, &name_type)
			!= GSS_S_COMPLETE) {

		res->status = (OM_uint32)GSS_S_FAILURE;
		res->minor_status = minor_status;

		gss_release_name(&minor_status, &internal_name);

		if (mechanisms != GSS_C_NULL_OID_SET) {
			for (i = 0; i < mechanisms->count; i++)
				free(mechanisms->elements[i].elements);
			free(mechanisms->elements);
			free(mechanisms);
		}

		return (TRUE);
	}

	/*
	 * convert the output args from the parameter given in the call to the
	 * variable in the XDR result
	 */


	res->name.GSS_BUFFER_T_len = (uint_t)external_name.length;
	res->name.GSS_BUFFER_T_val = (void *)external_name.value;

	/*
	 * we have to allocate storage for name_type here, since the value
	 * returned from gss_display_name points to the underlying mechanism
	 * static storage. If we didn't allocate storage, the next time
	 * through this routine, the xdr_free() call at the beginning would
	 * try to free up that static storage.
	 */

	res->name_type.GSS_OID_len = (uint_t)name_type->length;
	res->name_type.GSS_OID_val = (void *)malloc(name_type->length);
	if (!res->name_type.GSS_OID_val) {
		return (GSS_S_FAILURE);
	}
	memcpy(res->name_type.GSS_OID_val, name_type->elements,
		name_type->length);

	if (mechanisms != GSS_C_NULL_OID_SET) {
		res->mechanisms.GSS_OID_SET_len =
			(uint_t)mechanisms->count;
		res->mechanisms.GSS_OID_SET_val = (GSS_OID *)
				malloc(sizeof (GSS_OID) * mechanisms->count);
		if (!res->mechanisms.GSS_OID_SET_val) {
			free(res->name_type.GSS_OID_val);
			return (GSS_S_FAILURE);
		}
		for (i = 0; i < mechanisms->count; i++) {
			res->mechanisms.GSS_OID_SET_val[i].GSS_OID_len =
				(uint_t)mechanisms->elements[i].length;
			res->mechanisms.GSS_OID_SET_val[i].GSS_OID_val =
				(char *)malloc(mechanisms->elements[i].
						length);
			if (!res->mechanisms.GSS_OID_SET_val[i].GSS_OID_val) {
				free(res->name_type.GSS_OID_val);
				for (j = 0; j < i; j++) {
				free(res->mechanisms.
					GSS_OID_SET_val[i].GSS_OID_val);
				}
				free(res->mechanisms.GSS_OID_SET_val);
				return (GSS_S_FAILURE);
			}
			memcpy(res->mechanisms.GSS_OID_SET_val[i].GSS_OID_val,
				mechanisms->elements[i].elements,
				mechanisms->elements[i].length);
		}
	} else
		res->mechanisms.GSS_OID_SET_len = 0;

	/* release the space allocated for internal_name and mechanisms */
	gss_release_name(&minor_status, &internal_name);

	if (mechanisms != GSS_C_NULL_OID_SET) {
		for (i = 0; i < mechanisms->count; i++)
			free(mechanisms->elements[i].elements);
		free(mechanisms->elements);
		free(mechanisms);
	}

	/* return to caller */
	return (TRUE);
}


bool_t
gss_inquire_cred_by_mech_1_svc(argp, res, rqstp)
gss_inquire_cred_by_mech_arg *argp;
gss_inquire_cred_by_mech_res *res;
struct svc_req *rqstp;
{

	uid_t uid;

	gss_cred_id_t cred_handle;
	gss_OID_desc		mech_type_desc;
	gss_OID 		mech_type = &mech_type_desc;

	memset(res, 0, sizeof (*res));

	if (gssd_debug)
		fprintf(stderr, gettext("gss_inquire_cred\n"));

	/* verify the verifier_cred_handle */

	if (argp->gssd_cred_verifier != gssd_time_verf) {
		res->status = (OM_uint32) GSS_S_DEFECTIVE_CREDENTIAL;
		res->minor_status = 0;
		return (TRUE);
	}

	/*
	 * if the request isn't from root, null out the result pointer
	 * entries, so the next time through xdr_free won't try to
	 * free unmalloc'd memory and then return NULL
	 */

	if (checkfrom(rqstp, &uid) == 0) {
		return (FALSE);
	}

	/* set the uid sent as the RPC argument */

	uid = argp->uid;
	set_gssd_uid(uid);

	cred_handle = (argp->cred_handle.GSS_CRED_ID_T_len == 0 ?
			GSS_C_NO_CREDENTIAL :
			/*LINTED*/
			*((gss_cred_id_t *)argp->cred_handle.
				GSS_CRED_ID_T_val));

	/* call the gssapi routine */

	if (argp->mech_type.GSS_OID_len == 0)
		mech_type = GSS_C_NULL_OID;
	else {
		mech_type->length =
			(OM_uint32)argp->mech_type.GSS_OID_len;
		mech_type->elements =
			(void *)malloc(mech_type->length);
		if (!mech_type->elements) {
			return (GSS_S_FAILURE);
		}
		memcpy(mech_type->elements,
			argp->mech_type.GSS_OID_val,
			mech_type->length);
	}
	res->status = (OM_uint32)gss_inquire_cred_by_mech(
					&res->minor_status, cred_handle,
					mech_type, NULL, NULL,
					NULL, NULL);

	/* return to caller */
	return (TRUE);
}


bool_t
gsscred_name_to_unix_cred_1_svc(argsp, res, rqstp)
gsscred_name_to_unix_cred_arg *argsp;
gsscred_name_to_unix_cred_res *res;
struct svc_req *rqstp;
{
	uid_t uid;
	gss_OID_desc oid;
	gss_name_t gssName;
	gss_buffer_desc gssBuf = GSS_C_EMPTY_BUFFER;
	OM_uint32 minor;
	int gidsLen;
	gid_t *gids, gidOut;

	if (gssd_debug)
		fprintf(stderr, gettext("gsscred_name_to_unix_cred\n"));

	memset(res, 0, sizeof (*res));

	/*
	 * check the request originator
	 */
	if (checkfrom(rqstp, &uid) == 0)
		return (FALSE);

	/* set the uid from the rpc request */
	uid = argsp->uid;
	set_gssd_uid(uid);

	/*
	 * convert the principal name to gss internal format
	 * need not malloc the input parameters
	 */
	gssBuf.length = argsp->pname.GSS_BUFFER_T_len;
	gssBuf.value = (void*)argsp->pname.GSS_BUFFER_T_val;
	oid.length = argsp->name_type.GSS_OID_len;
	oid.elements = (void*)argsp->name_type.GSS_OID_val;

	res->major = gss_import_name(&minor, &gssBuf, &oid, &gssName);
	if (res->major != GSS_S_COMPLETE)
		return (TRUE);

	/* retrieve the mechanism type from the arguments */
	oid.length = argsp->mech_type.GSS_OID_len;
	oid.elements = (void*)argsp->mech_type.GSS_OID_val;

	/* call the gss extensions to map the principal name to unix creds */
	res->major = gsscred_name_to_unix_cred(gssName, &oid, &uid, &gidOut,
					&gids, &gidsLen);
	gss_release_name(&minor, &gssName);

	if (res->major == GSS_S_COMPLETE) {
		res->uid = uid;
		res->gid = gidOut;
		res->gids.GSSCRED_GIDS_val = gids;
		res->gids.GSSCRED_GIDS_len = gidsLen;
	}

	return (TRUE);
} /* gsscred_name_to_unix_cred_svc_1 */

bool_t
gsscred_expname_to_unix_cred_1_svc(argsp, res, rqstp)
gsscred_expname_to_unix_cred_arg *argsp;
gsscred_expname_to_unix_cred_res *res;
struct svc_req *rqstp;
{
	uid_t uid;
	gss_buffer_desc expName = GSS_C_EMPTY_BUFFER;
	int gidsLen;
	gid_t *gids, gidOut;

	if (gssd_debug)
		fprintf(stderr, gettext("gsscred_expname_to_unix_cred\n"));

	memset(res, 0, sizeof (*res));

	/*
	 * check the request originator
	 */
	if (checkfrom(rqstp, &uid) == 0)
		return (FALSE);

	/* set the uid from the rpc request */
	uid = argsp->uid;
	set_gssd_uid(uid);

	/*
	 * extract the export name from arguments
	 * need not malloc the input parameters
	 */
	expName.length = argsp->expname.GSS_BUFFER_T_len;
	expName.value = (void*)argsp->expname.GSS_BUFFER_T_val;

	res->major = gsscred_expname_to_unix_cred(&expName, &uid,
					&gidOut, &gids, &gidsLen);

	if (res->major == GSS_S_COMPLETE) {
		res->uid = uid;
		res->gid = gidOut;
		res->gids.GSSCRED_GIDS_val = gids;
		res->gids.GSSCRED_GIDS_len = gidsLen;
	}

	return (TRUE);
} /* gsscred_expname_to_unix_cred_1_svc */

bool_t
gss_get_group_info_1_svc(argsp, res, rqstp)
gss_get_group_info_arg *argsp;
gss_get_group_info_res *res;
struct svc_req *rqstp;
{
	uid_t uid;
	int gidsLen;
	gid_t *gids, gidOut;

	if (gssd_debug)
		fprintf(stderr, gettext("gss_get_group_info\n"));

	memset(res, 0, sizeof (*res));

	/*
	 * check the request originator
	 */
	if (checkfrom(rqstp, &uid) == 0)
		return (FALSE);

	/* set the uid from the rpc request */
	uid = argsp->uid;
	set_gssd_uid(uid);

	/*
	 * extract the uid from the arguments
	 */
	uid = argsp->puid;
	res->major = gss_get_group_info(uid, &gidOut, &gids, &gidsLen);
	if (res->major == GSS_S_COMPLETE) {
		res->gid = gidOut;
		res->gids.GSSCRED_GIDS_val = gids;
		res->gids.GSSCRED_GIDS_len = gidsLen;
	}

	return (TRUE);
} /* gss_get_group_info_1_svc */

/*ARGSUSED*/
bool_t
gss_get_kmod_1_svc(argsp, res, rqstp)
	gss_get_kmod_arg *argsp;
	gss_get_kmod_res *res;
	struct svc_req *rqstp;
{
	gss_OID_desc oid;
	char *kmodName;

	if (gssd_debug)
		fprintf(stderr, gettext("gss_get_kmod\n"));

	res->module_follow = FALSE;
	oid.length = argsp->mech_oid.GSS_OID_len;
	oid.elements = (void *)argsp->mech_oid.GSS_OID_val;
	kmodName = __gss_get_kmodName(&oid);

	if (kmodName != NULL) {
		res->module_follow = TRUE;
		res->gss_get_kmod_res_u.modname = kmodName;
	}

	return (TRUE);
}

/*
 *  Returns 1 if caller is ok, else 0.
 *  If caller ok, the uid is returned in uidp.
 */
static int
checkfrom(rqstp, uidp)
struct svc_req *rqstp;
uid_t *uidp;
{
	SVCXPRT *xprt = rqstp->rq_xprt;
	struct authunix_parms *aup;
	uid_t uid;

	/* check client agent uid to ensure it is privileged */
	if (__rpc_get_local_uid(xprt, &uid) < 0) {
		syslog(LOG_ERR, gettext("__rpc_get_local_uid failed %s %s"),
			xprt->xp_netid, xprt->xp_tp);
		goto weakauth;
	}
	if (gssd_debug)
		fprintf(stderr, gettext("checkfrom: local_uid  %d\n"), uid);
	if (uid != 0) {
		syslog(LOG_ERR,
			gettext("checkfrom: caller (uid %d) not privileged"),
			uid);
		goto weakauth;
	}

	/*
	 *  Request came from local privileged process.
	 *  Proceed to get uid of client if needed by caller.
	 */
	if (uidp) {
		if (rqstp->rq_cred.oa_flavor != AUTH_SYS) {
		syslog(LOG_ERR, gettext("checkfrom: not UNIX credentials"));
			goto weakauth;
		}
		/*LINTED*/
		aup = (struct authunix_parms *)rqstp->rq_clntcred;
		*uidp = aup->aup_uid;
		if (gssd_debug) {
			fprintf(stderr,
				gettext("checkfrom: caller's uid %d\n"), *uidp);
		}
	}
	return (1);

	weakauth:
	svcerr_weakauth(xprt);
	return (0);
}