Home | History | Annotate | Download | only in libgss
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 /*
     27  *  glue routine for gss_accept_sec_context
     28  */
     29 
     30 #include <mechglueP.h>
     31 #ifdef HAVE_STDLIB_H
     32 #include <stdlib.h>
     33 #endif
     34 #include <string.h>
     35 #include <errno.h>
     36 
     37 static OM_uint32
     38 val_acc_sec_ctx_args(
     39 	OM_uint32 *minor_status,
     40 	gss_ctx_id_t *context_handle,
     41 	gss_buffer_t input_token_buffer,
     42 	gss_name_t *src_name,
     43 	gss_OID *mech_type,
     44 	gss_buffer_t output_token,
     45 	gss_cred_id_t *d_cred)
     46 {
     47 
     48 	/* Initialize outputs. */
     49 
     50 	if (minor_status != NULL)
     51 		*minor_status = 0;
     52 
     53 	if (src_name != NULL)
     54 		*src_name = GSS_C_NO_NAME;
     55 
     56 	if (mech_type != NULL)
     57 		*mech_type = GSS_C_NO_OID;
     58 
     59 	if (output_token != GSS_C_NO_BUFFER) {
     60 		output_token->length = 0;
     61 		output_token->value = NULL;
     62 	}
     63 
     64 	if (d_cred != NULL)
     65 		*d_cred = GSS_C_NO_CREDENTIAL;
     66 
     67 	/* Validate arguments. */
     68 
     69 	if (minor_status == NULL)
     70 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
     71 
     72 	if (context_handle == NULL)
     73 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
     74 
     75 	if (input_token_buffer == GSS_C_NO_BUFFER)
     76 		return (GSS_S_CALL_INACCESSIBLE_READ);
     77 
     78 	if (output_token == GSS_C_NO_BUFFER)
     79 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
     80 
     81 	return (GSS_S_COMPLETE);
     82 }
     83 
     84 OM_uint32
     85 gss_accept_sec_context(minor_status,
     86 			context_handle,
     87 			verifier_cred_handle,
     88 			input_token_buffer,
     89 			input_chan_bindings,
     90 			src_name,
     91 			mech_type,
     92 			output_token,
     93 			ret_flags,
     94 			time_rec,
     95 			d_cred)
     96 
     97 OM_uint32 			*minor_status;
     98 gss_ctx_id_t			*context_handle;
     99 const gss_cred_id_t		verifier_cred_handle;
    100 const gss_buffer_t		input_token_buffer;
    101 const gss_channel_bindings_t	input_chan_bindings;
    102 gss_name_t			*src_name;
    103 gss_OID				*mech_type;
    104 gss_buffer_t			output_token;
    105 OM_uint32			*ret_flags;
    106 OM_uint32			*time_rec;
    107 gss_cred_id_t			*d_cred; /* delegated cred handle */
    108 
    109 {
    110 	OM_uint32		status, temp_status, t_minstat;
    111 	gss_union_ctx_id_t	union_ctx_id;
    112 	gss_union_cred_t	union_cred;
    113 	gss_cred_id_t	input_cred_handle = GSS_C_NO_CREDENTIAL;
    114 	gss_cred_id_t	tmp_d_cred = GSS_C_NO_CREDENTIAL;
    115 	gss_name_t		internal_name = GSS_C_NO_NAME;
    116 	gss_name_t		tmp_src_name = GSS_C_NO_NAME;
    117 	gss_OID_desc	token_mech_type_desc;
    118 	gss_OID		token_mech_type = &token_mech_type_desc;
    119 	gss_OID		actual_mech = GSS_C_NO_OID;
    120 	OM_uint32	flags;
    121 	gss_mechanism	mech;
    122 
    123 	status = val_acc_sec_ctx_args(minor_status,
    124 				context_handle,
    125 				input_token_buffer,
    126 				src_name,
    127 				mech_type,
    128 				output_token,
    129 				d_cred);
    130 	if (status != GSS_S_COMPLETE)
    131 		return (status);
    132 
    133 	/*
    134 	 * if context_handle is GSS_C_NO_CONTEXT, allocate a union context
    135 	 * descriptor to hold the mech type information as well as the
    136 	 * underlying mechanism context handle. Otherwise, cast the
    137 	 * value of *context_handle to the union context variable.
    138 	 */
    139 
    140 	if (*context_handle == GSS_C_NO_CONTEXT) {
    141 
    142 		if (input_token_buffer == GSS_C_NO_BUFFER)
    143 			return (GSS_S_CALL_INACCESSIBLE_READ);
    144 
    145 		/* Get the token mech type */
    146 		status = __gss_get_mech_type(token_mech_type,
    147 					input_token_buffer);
    148 
    149 		if (status)
    150 			return (status);
    151 
    152 		status = GSS_S_FAILURE;
    153 		union_ctx_id = (gss_union_ctx_id_t)
    154 			malloc(sizeof (gss_union_ctx_id_desc));
    155 		if (!union_ctx_id)
    156 			return (GSS_S_FAILURE);
    157 
    158 		union_ctx_id->internal_ctx_id = GSS_C_NO_CONTEXT;
    159 		status = generic_gss_copy_oid(&t_minstat,
    160 					token_mech_type,
    161 					&union_ctx_id->mech_type);
    162 		if (status != GSS_S_COMPLETE) {
    163 			free(union_ctx_id);
    164 			return (status);
    165 		}
    166 
    167 		/* set the new context handle to caller's data */
    168 		*context_handle = (gss_ctx_id_t)union_ctx_id;
    169 	} else {
    170 		union_ctx_id = (gss_union_ctx_id_t)*context_handle;
    171 		token_mech_type = union_ctx_id->mech_type;
    172 	}
    173 
    174 	/*
    175 	 * get the appropriate cred handle from the union cred struct.
    176 	 * defaults to GSS_C_NO_CREDENTIAL if there is no cred, which will
    177 	 * use the default credential.
    178 	 */
    179 	union_cred = (gss_union_cred_t)verifier_cred_handle;
    180 	input_cred_handle = __gss_get_mechanism_cred(union_cred,
    181 						token_mech_type);
    182 
    183 	/*
    184 	 * now select the approprate underlying mechanism routine and
    185 	 * call it.
    186 	 */
    187 
    188 	mech = __gss_get_mechanism(token_mech_type);
    189 	if (mech && mech->gss_accept_sec_context) {
    190 		status = mech->gss_accept_sec_context(
    191 					mech->context,
    192 					minor_status,
    193 					&union_ctx_id->internal_ctx_id,
    194 					input_cred_handle,
    195 					input_token_buffer,
    196 					input_chan_bindings,
    197 					&internal_name,
    198 					&actual_mech,
    199 					output_token,
    200 					&flags,
    201 					time_rec,
    202 					d_cred ? &tmp_d_cred : NULL);
    203 
    204 		/* If there's more work to do, keep going... */
    205 		if (status == GSS_S_CONTINUE_NEEDED)
    206 			return (GSS_S_CONTINUE_NEEDED);
    207 
    208 		/* if the call failed, return with failure */
    209 		if (status != GSS_S_COMPLETE)
    210 			goto error_out;
    211 
    212 		if (mech_type != NULL)
    213 			*mech_type = actual_mech;
    214 
    215 		/*
    216 		 * if src_name is non-NULL,
    217 		 * convert internal_name into a union name equivalent
    218 		 * First call the mechanism specific display_name()
    219 		 * then call gss_import_name() to create
    220 		 * the union name struct cast to src_name
    221 		 */
    222 		if (internal_name != NULL) {
    223 			temp_status = __gss_convert_name_to_union_name(
    224 				&t_minstat, mech,
    225 				internal_name, &tmp_src_name);
    226 			if (temp_status != GSS_S_COMPLETE) {
    227 				*minor_status = t_minstat;
    228 				if (output_token->length)
    229 					(void) gss_release_buffer(
    230 						&t_minstat,
    231 						output_token);
    232 				if (internal_name != GSS_C_NO_NAME)
    233 					mech->gss_release_name(
    234 						mech->context,
    235 						&t_minstat,
    236 						&internal_name);
    237 				return (temp_status);
    238 			}
    239 			if (src_name != NULL) {
    240 				*src_name = tmp_src_name;
    241 			}
    242 		} else if (src_name != NULL) {
    243 			*src_name = GSS_C_NO_NAME;
    244 		}
    245 
    246 		/* Ensure we're returning correct creds format */
    247 		if ((flags & GSS_C_DELEG_FLAG) &&
    248 		    tmp_d_cred != GSS_C_NO_CREDENTIAL) {
    249 			/*
    250 			 * If we got back an OID different from the original
    251 			 * token OID, assume the delegated_cred is already
    252 			 * a proper union_cred and just return it.  Don't
    253 			 * try to re-wrap it.  This is for SPNEGO or other
    254 			 * pseudo-mechanisms.
    255 			 */
    256 			if (actual_mech != GSS_C_NO_OID &&
    257 			    token_mech_type != GSS_C_NO_OID &&
    258 			    !g_OID_equal(actual_mech, token_mech_type)) {
    259 				*d_cred = tmp_d_cred;
    260 			} else {
    261 				gss_union_cred_t d_u_cred = NULL;
    262 
    263 				d_u_cred = malloc(sizeof (gss_union_cred_desc));
    264 				if (d_u_cred == NULL) {
    265 					status = GSS_S_FAILURE;
    266 					goto error_out;
    267 				}
    268 				(void) memset(d_u_cred, 0,
    269 					    sizeof (gss_union_cred_desc));
    270 
    271 				d_u_cred->count = 1;
    272 
    273 				status = generic_gss_copy_oid(
    274 					&t_minstat,
    275 					actual_mech,
    276 					&d_u_cred->mechs_array);
    277 
    278 				if (status != GSS_S_COMPLETE) {
    279 					free(d_u_cred);
    280 					goto error_out;
    281 				}
    282 
    283 				d_u_cred->cred_array = malloc(
    284 						sizeof (gss_cred_id_t));
    285 				if (d_u_cred->cred_array != NULL) {
    286 					d_u_cred->cred_array[0] = tmp_d_cred;
    287 				} else {
    288 					free(d_u_cred);
    289 					status = GSS_S_FAILURE;
    290 					goto error_out;
    291 				}
    292 
    293 				if (status != GSS_S_COMPLETE) {
    294 					free(d_u_cred->cred_array);
    295 					free(d_u_cred);
    296 					goto error_out;
    297 				}
    298 
    299 				internal_name = GSS_C_NO_NAME;
    300 
    301 				d_u_cred->auxinfo.creation_time = time(0);
    302 				d_u_cred->auxinfo.time_rec = 0;
    303 
    304 				if (mech->gss_inquire_cred) {
    305 					status = mech->gss_inquire_cred(
    306 						mech->context,
    307 						minor_status,
    308 						tmp_d_cred,
    309 						&internal_name,
    310 						&d_u_cred->auxinfo.time_rec,
    311 						&d_u_cred->auxinfo.cred_usage,
    312 						NULL);
    313 				}
    314 
    315 				if (internal_name != NULL) {
    316 					temp_status =
    317 					    __gss_convert_name_to_union_name(
    318 						&t_minstat, mech,
    319 						internal_name, &tmp_src_name);
    320 					if (temp_status != GSS_S_COMPLETE) {
    321 						*minor_status = t_minstat;
    322 						if (output_token->length)
    323 						    (void) gss_release_buffer(
    324 								&t_minstat,
    325 								output_token);
    326 						free(d_u_cred->cred_array);
    327 						free(d_u_cred);
    328 						return (temp_status);
    329 					}
    330 				}
    331 
    332 				if (tmp_src_name != NULL) {
    333 					status = gss_display_name(
    334 						&t_minstat,
    335 						tmp_src_name,
    336 						&d_u_cred->auxinfo.name,
    337 						&d_u_cred->auxinfo.name_type);
    338 				}
    339 
    340 				*d_cred = (gss_cred_id_t)d_u_cred;
    341 			}
    342 		}
    343 		if (ret_flags != NULL) {
    344 			*ret_flags = flags;
    345 		}
    346 
    347 		if (src_name == NULL && tmp_src_name != NULL)
    348 			(void) gss_release_name(&t_minstat,
    349 					&tmp_src_name);
    350 		return	(status);
    351 	} else {
    352 
    353 		status = GSS_S_BAD_MECH;
    354 	}
    355 
    356 error_out:
    357 	if (union_ctx_id) {
    358 		if (union_ctx_id->mech_type) {
    359 			if (union_ctx_id->mech_type->elements)
    360 				free(union_ctx_id->mech_type->elements);
    361 			free(union_ctx_id->mech_type);
    362 		}
    363 		free(union_ctx_id);
    364 		*context_handle = GSS_C_NO_CONTEXT;
    365 	}
    366 
    367 #if 0
    368 	/*
    369 	 * Solaris Kerberos
    370 	 * Don't release, it causes a problem with error token.
    371 	 */
    372 	if (output_token->length)
    373 		(void) gss_release_buffer(&t_minstat, output_token);
    374 #endif
    375 
    376 	if (src_name)
    377 		*src_name = GSS_C_NO_NAME;
    378 
    379 	if (tmp_src_name != GSS_C_NO_NAME)
    380 		(void) gss_release_buffer(&t_minstat,
    381 			(gss_buffer_t)tmp_src_name);
    382 
    383 	return (status);
    384 }
    385