OpenSolaris_b135/lib/libgss/g_acquire_cred.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.
 */

/*
 *  glue routine for gss_acquire_cred
 */
#include <mechglueP.h>
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#include <string.h>
#include <errno.h>
#include <time.h>

/* local functions */
static gss_OID_set create_actual_mechs(const gss_OID, int);

static gss_OID_set
create_actual_mechs(mechs_array, count)
	const gss_OID	mechs_array;
	int count;
{
	gss_OID_set 	actual_mechs;
	int		i;
	OM_uint32	minor;

	actual_mechs = (gss_OID_set) malloc(sizeof (gss_OID_set_desc));
	if (!actual_mechs)
		return (NULL);

	actual_mechs->elements = (gss_OID)
		malloc(sizeof (gss_OID_desc) * count);
	if (!actual_mechs->elements) {
		free(actual_mechs);
		return (NULL);
	}

	actual_mechs->count = 0;

	for (i = 0; i < count; i++) {
		actual_mechs->elements[i].elements = (void *)
			malloc(mechs_array[i].length);
		if (actual_mechs->elements[i].elements == NULL) {
			(void) gss_release_oid_set(&minor, &actual_mechs);
			return (NULL);
		}
		g_OID_copy(&actual_mechs->elements[i], &mechs_array[i]);
		actual_mechs->count++;
	}

	return (actual_mechs);
}

static OM_uint32
val_acq_cred_args(
	OM_uint32 *minor_status,
	gss_cred_id_t *output_cred_handle,
	gss_OID_set *actual_mechs,
	OM_uint32 *time_rec)
{

	/* Initialize outputs. */

	if (minor_status != NULL)
		*minor_status = 0;

	if (output_cred_handle != NULL)
		*output_cred_handle = GSS_C_NO_CREDENTIAL;

	if (actual_mechs != NULL)
		*actual_mechs = GSS_C_NULL_OID_SET;

	if (time_rec != NULL)
		*time_rec = 0;

	/* Validate arguments. */

	if (minor_status == NULL)
		return (GSS_S_CALL_INACCESSIBLE_WRITE);

	if (output_cred_handle == NULL)
		return (GSS_S_CALL_INACCESSIBLE_WRITE);

	return (GSS_S_COMPLETE);
}

OM_uint32
gss_acquire_cred(minor_status,
			desired_name,
			time_req,
			desired_mechs,
			cred_usage,
			output_cred_handle,
			actual_mechs,
			time_rec)

OM_uint32 *		minor_status;
const gss_name_t	desired_name;
OM_uint32		time_req;
const gss_OID_set	desired_mechs;
int			cred_usage;
gss_cred_id_t 		*output_cred_handle;
gss_OID_set *		actual_mechs;
OM_uint32 *		time_rec;

{
	OM_uint32 major = GSS_S_FAILURE;
	OM_uint32 initTimeOut, acceptTimeOut, outTime = GSS_C_INDEFINITE;
	gss_OID_set_desc default_OID_set;
	gss_OID_set mechs;
	gss_OID_desc default_OID;
	gss_mechanism mech;
	int i;
	gss_union_cred_t creds;

	major = val_acq_cred_args(minor_status,
				output_cred_handle,
				actual_mechs,
				time_rec);
	if (major != GSS_S_COMPLETE)
		return (major);

	/* Initial value needed below. */
	major = GSS_S_FAILURE;

	/*
	 * if desired_mechs equals GSS_C_NULL_OID_SET, then pick an
	 * appropriate default.  We use the first mechanism in the
	 * mechansim list as the default. This set is created with
	 * statics thus needs not be freed
	 */
	if (desired_mechs == GSS_C_NULL_OID_SET) {
		mech = __gss_get_mechanism(NULL);
		if (mech == NULL)
			return (GSS_S_BAD_MECH);

		mechs = &default_OID_set;
		default_OID_set.count = 1;
		default_OID_set.elements = &default_OID;
		default_OID.length = mech->mech_type.length;
		default_OID.elements = mech->mech_type.elements;
	} else
		mechs = desired_mechs;

	if (mechs->count == NULL)
		return (GSS_S_BAD_MECH);

	/* allocate the output credential structure */
	creds = (gss_union_cred_t)malloc(sizeof (gss_union_cred_desc));
	if (creds == NULL)
		return (GSS_S_FAILURE);

	/* initialize to 0s */
	(void) memset(creds, 0, sizeof (gss_union_cred_desc));

	/* for each requested mech attempt to obtain a credential */
	for (i = 0; i < mechs->count; i++) {
		major = gss_add_cred(minor_status, (gss_cred_id_t)creds,
				desired_name,
				&mechs->elements[i],
				cred_usage, time_req, time_req, NULL,
				NULL, &initTimeOut, &acceptTimeOut);
		if (major == GSS_S_COMPLETE) {
			/* update the credential's time */
			if (cred_usage == GSS_C_ACCEPT) {
				if (outTime > acceptTimeOut)
					outTime = acceptTimeOut;
			} else if (cred_usage == GSS_C_INITIATE) {
				if (outTime > initTimeOut)
					outTime = initTimeOut;
			} else {
				/*
				 * time_rec is the lesser of the
				 * init/accept times
				 */
				if (initTimeOut > acceptTimeOut)
					outTime = (outTime > acceptTimeOut) ?
						acceptTimeOut : outTime;
				else
					outTime = (outTime > initTimeOut) ?
						initTimeOut : outTime;
			}
		}
	} /* for */

	/* ensure that we have at least one credential element */
	if (creds->count < 1) {
		free(creds);
		return (major);
	}

	/*
	 * fill in output parameters
	 * setup the actual mechs output parameter
	 */
	if (actual_mechs != NULL) {
		if ((*actual_mechs = create_actual_mechs(creds->mechs_array,
					creds->count)) == NULL) {
			(void) gss_release_cred(minor_status,
				(gss_cred_id_t *)&creds);
			*minor_status = 0;
			return (GSS_S_FAILURE);
		}
	}

	if (time_rec)
		*time_rec = outTime;


	*output_cred_handle = (gss_cred_id_t)creds;
	return (GSS_S_COMPLETE);
}

static OM_uint32
val_add_cred_args(
	OM_uint32 *minor_status,
	gss_cred_id_t input_cred_handle,
	gss_cred_id_t *output_cred_handle,
	gss_OID_set *actual_mechs,
	OM_uint32 *initiator_time_rec,
	OM_uint32 *acceptor_time_rec)
{

	/* Initialize outputs. */

	if (minor_status != NULL)
		*minor_status = 0;

	if (output_cred_handle != NULL)
		*output_cred_handle = GSS_C_NO_CREDENTIAL;

	if (actual_mechs != NULL)
		*actual_mechs = GSS_C_NO_OID_SET;

	if (acceptor_time_rec != NULL)
		*acceptor_time_rec = 0;

	if (initiator_time_rec != NULL)
		*initiator_time_rec = 0;

	/* Validate arguments. */

	if (minor_status == NULL)
		return (GSS_S_CALL_INACCESSIBLE_WRITE);

	if (input_cred_handle == GSS_C_NO_CREDENTIAL &&
		output_cred_handle == NULL)

		return (GSS_S_CALL_INACCESSIBLE_WRITE | GSS_S_NO_CRED);

	return (GSS_S_COMPLETE);
}

/* V2 INTERFACE */
OM_uint32
gss_add_cred(minor_status, input_cred_handle,
			desired_name, desired_mech, cred_usage,
			initiator_time_req, acceptor_time_req,
			output_cred_handle, actual_mechs,
			initiator_time_rec, acceptor_time_rec)
	OM_uint32		*minor_status;
	const gss_cred_id_t	input_cred_handle;
	const gss_name_t	desired_name;
	const gss_OID		desired_mech;
	gss_cred_usage_t	cred_usage;
	OM_uint32		initiator_time_req;
	OM_uint32		acceptor_time_req;
	gss_cred_id_t		*output_cred_handle;
	gss_OID_set		*actual_mechs;
	OM_uint32		*initiator_time_rec;
	OM_uint32		*acceptor_time_rec;
{
	OM_uint32		status, time_req, time_rec, temp_minor_status;
	gss_mechanism 		mech;
	gss_union_name_t	union_name = NULL;
	gss_union_cred_t	union_cred, new_union_cred;
	gss_name_t		internal_name = GSS_C_NO_NAME;
	gss_name_t		allocated_name = GSS_C_NO_NAME;
	gss_cred_id_t		cred = NULL;
	gss_OID			new_mechs_array = NULL;
	gss_cred_id_t		*new_cred_array = NULL;

	status = val_add_cred_args(minor_status,
				input_cred_handle,
				output_cred_handle,
				actual_mechs,
				initiator_time_rec,
				acceptor_time_rec);
	if (status != GSS_S_COMPLETE)
		return (status);

	mech = __gss_get_mechanism(desired_mech);
	if (!mech)
		return (GSS_S_BAD_MECH);
	else if (!mech->gss_acquire_cred)
		return (GSS_S_UNAVAILABLE);

	if (input_cred_handle == GSS_C_NO_CREDENTIAL) {
		union_cred = malloc(sizeof (gss_union_cred_desc));
		if (union_cred == NULL)
			return (GSS_S_FAILURE);

		(void) memset(union_cred, 0, sizeof (gss_union_cred_desc));
	} else {
		/* Input Cred is non-NULL */
		union_cred = (gss_union_cred_t)input_cred_handle;

		if (__gss_get_mechanism_cred(union_cred, desired_mech) !=
			GSS_C_NO_CREDENTIAL) {
			status = GSS_S_DUPLICATE_ELEMENT;
			goto errout;
		}

		/*
		 * If no name was given, determine the name from the
		 * existing credential.
		 */
		if (desired_name == GSS_C_NO_NAME) {
			if (gss_import_name(minor_status,
				&union_cred->auxinfo.name,
				union_cred->auxinfo.name_type,
				&allocated_name) == GSS_S_COMPLETE &&
			    (gss_canonicalize_name(minor_status,
					allocated_name,
					&mech->mech_type,
					NULL) == GSS_S_COMPLETE)) {
				internal_name = allocated_name;
			}
		} /* else, get the name from the desired_name below */
	}
	if (desired_name != GSS_C_NO_NAME) {
		/* may need to create a mechanism specific name */
		union_name = (gss_union_name_t)desired_name;

		if (union_name->mech_type &&
			g_OID_equal(union_name->mech_type,
					&mech->mech_type))
			internal_name = union_name->mech_name;
		else {
			if (__gss_import_internal_name(minor_status,
				&mech->mech_type, union_name,
				&allocated_name) != GSS_S_COMPLETE) {
				status = GSS_S_BAD_NAME;
				goto errout;
			}
			internal_name = allocated_name;
		}
	}

	if (cred_usage == GSS_C_ACCEPT)
		time_req = acceptor_time_req;
	else if (cred_usage == GSS_C_INITIATE)
		time_req = initiator_time_req;
	else if (cred_usage == GSS_C_BOTH)
		time_req = (acceptor_time_req > initiator_time_req) ?
			acceptor_time_req : initiator_time_req;

	status = mech->gss_acquire_cred(mech->context, minor_status,
				internal_name, time_req,
				GSS_C_NULL_OID_SET, cred_usage,
				&cred, NULL, &time_rec);

	if (status != GSS_S_COMPLETE)
		goto errout;

	/* may need to set credential auxinfo structure */
	if (union_cred->auxinfo.creation_time == 0) {
		union_cred->auxinfo.creation_time = time(NULL);
		union_cred->auxinfo.time_rec = time_rec;
		union_cred->auxinfo.cred_usage = cred_usage;

	/*
	 * If internal_name is GSS_C_NO_NAME a cred with no associated
	 * name was requested: don't set auxinfo.name or auxinfo.name_type.
	 */
		if (internal_name != GSS_C_NO_NAME) {
			if ((status = mech->gss_display_name(mech->context,
					&temp_minor_status, internal_name,
					&union_cred->auxinfo.name,
					&union_cred->auxinfo.name_type)) !=
				GSS_S_COMPLETE)
				goto errout;
		}
	}

	/* now add the new credential elements */
	new_mechs_array = (gss_OID)
		malloc(sizeof (gss_OID_desc) * (union_cred->count+1));

	new_cred_array = (gss_cred_id_t *)
		malloc(sizeof (gss_cred_id_t) * (union_cred->count+1));

	if (!new_mechs_array || !new_cred_array) {
		status = GSS_S_FAILURE;
		goto errout;
	}

	if (acceptor_time_rec)
		if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH)
			*acceptor_time_rec = time_rec;
	if (initiator_time_rec)
		if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH)
			*initiator_time_rec = time_rec;

	/*
	 * OK, expand the mechanism array and the credential array
	 */
	(void) memcpy(new_mechs_array, union_cred->mechs_array,
		sizeof (gss_OID_desc) * union_cred->count);
	(void) memcpy(new_cred_array, union_cred->cred_array,
		sizeof (gss_cred_id_t) * union_cred->count);

	new_cred_array[union_cred->count] = cred;
	if ((new_mechs_array[union_cred->count].elements =
			malloc(mech->mech_type.length)) == NULL)
		goto errout;

	g_OID_copy(&new_mechs_array[union_cred->count],
			&mech->mech_type);

	if (actual_mechs) {
		*actual_mechs = create_actual_mechs(new_mechs_array,
					union_cred->count + 1);
		if (*actual_mechs == NULL) {
			free(new_mechs_array[union_cred->count].elements);
			goto errout;
		}
	}

	if (output_cred_handle == NULL) {
		free(union_cred->mechs_array);
		free(union_cred->cred_array);
		new_union_cred = union_cred;
	} else {
		new_union_cred = malloc(sizeof (gss_union_cred_desc));
		if (new_union_cred == NULL) {
			free(new_mechs_array[union_cred->count].elements);
			goto errout;
		}
		*new_union_cred = *union_cred;
		*output_cred_handle = (gss_cred_id_t)new_union_cred;
	}

	new_union_cred->mechs_array = new_mechs_array;
	new_union_cred->cred_array = new_cred_array;
	new_union_cred->count++;

	/* We're done with the internal name. Free it if we allocated it. */

	if (allocated_name)
		(void) __gss_release_internal_name(&temp_minor_status,
					&mech->mech_type,
					&allocated_name);

	return (GSS_S_COMPLETE);

errout:
	if (new_mechs_array)
		free(new_mechs_array);
	if (new_cred_array)
		free(new_cred_array);

	if (cred != NULL && mech->gss_release_cred)
		mech->gss_release_cred(mech->context,
				&temp_minor_status, &cred);

	if (allocated_name)
		(void) __gss_release_internal_name(&temp_minor_status,
					&mech->mech_type,
					&allocated_name);

	if (input_cred_handle == GSS_C_NO_CREDENTIAL && union_cred) {
		if (union_cred->auxinfo.name.value)
			free(union_cred->auxinfo.name.value);
		free(union_cred);
	}

	return (status);
}