Home | History | Annotate | Download | only in libgss
      1     0  stevel /*
      2     0  stevel  * CDDL HEADER START
      3     0  stevel  *
      4     0  stevel  * The contents of this file are subject to the terms of the
      5  5053     gtb  * Common Development and Distribution License (the "License").
      6  5053     gtb  * You may not use this file except in compliance with the License.
      7     0  stevel  *
      8     0  stevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9     0  stevel  * or http://www.opensolaris.org/os/licensing.
     10     0  stevel  * See the License for the specific language governing permissions
     11     0  stevel  * and limitations under the License.
     12     0  stevel  *
     13     0  stevel  * When distributing Covered Code, include this CDDL HEADER in each
     14     0  stevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15     0  stevel  * If applicable, add the following below this CDDL HEADER, with the
     16     0  stevel  * fields enclosed by brackets "[]" replaced with your own identifying
     17     0  stevel  * information: Portions Copyright [yyyy] [name of copyright owner]
     18     0  stevel  *
     19     0  stevel  * CDDL HEADER END
     20     0  stevel  */
     21     0  stevel /*
     22  9698   Peter  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23     0  stevel  * Use is subject to license terms.
     24     0  stevel  */
     25     0  stevel 
     26     0  stevel /*
     27     0  stevel  *  glue routine gss_import_name
     28     0  stevel  *
     29     0  stevel  */
     30     0  stevel 
     31     0  stevel #include <mechglueP.h>
     32     0  stevel #include <stdio.h>
     33     0  stevel #ifdef HAVE_STDLIB_H
     34     0  stevel #include <stdlib.h>
     35     0  stevel #endif
     36     0  stevel #include <string.h>
     37     0  stevel #include <errno.h>
     38     0  stevel 
     39     0  stevel extern int
     40     0  stevel get_der_length(unsigned char **, unsigned int, unsigned int *);
     41     0  stevel 
     42     0  stevel /* local function to import GSS_C_EXPORT_NAME names */
     43     0  stevel static OM_uint32 importExportName(OM_uint32 *, gss_union_name_t);
     44     0  stevel 
     45  9698   Peter static OM_uint32
     46  9698   Peter val_imp_name_args(
     47  9698   Peter 	OM_uint32 *minor_status,
     48  9698   Peter 	gss_buffer_t input_name_buffer,
     49  9698   Peter 	gss_name_t *output_name)
     50  9698   Peter {
     51  9698   Peter 
     52  9698   Peter 	/* Initialize outputs. */
     53  9698   Peter 
     54  9698   Peter 	if (minor_status != NULL)
     55  9698   Peter 		*minor_status = 0;
     56  9698   Peter 
     57  9698   Peter 	if (output_name != NULL)
     58  9698   Peter 		*output_name = GSS_C_NO_NAME;
     59  9698   Peter 
     60  9698   Peter 	/* Validate arguments. */
     61  9698   Peter 
     62  9698   Peter 	if (minor_status == NULL)
     63  9698   Peter 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
     64  9698   Peter 
     65  9698   Peter 	if (output_name == NULL)
     66  9698   Peter 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
     67  9698   Peter 
     68  9698   Peter 	if (input_name_buffer == GSS_C_NO_BUFFER)
     69  9698   Peter 		return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME);
     70  9698   Peter 
     71  9698   Peter 	if (GSS_EMPTY_BUFFER(input_name_buffer))
     72  9698   Peter 		return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME);
     73  9698   Peter 
     74  9698   Peter 	return (GSS_S_COMPLETE);
     75  9698   Peter }
     76     0  stevel 
     77     0  stevel OM_uint32
     78     0  stevel gss_import_name(minor_status,
     79     0  stevel 		input_name_buffer,
     80     0  stevel 		input_name_type,
     81     0  stevel 		output_name)
     82     0  stevel 
     83     0  stevel OM_uint32 *minor_status;
     84     0  stevel const gss_buffer_t input_name_buffer;
     85     0  stevel const gss_OID input_name_type;
     86     0  stevel gss_name_t *output_name;
     87     0  stevel {
     88     0  stevel 	gss_union_name_t union_name;
     89     0  stevel 	OM_uint32 major_status = GSS_S_FAILURE, tmp;
     90     0  stevel 
     91  9698   Peter 	major_status = val_imp_name_args(minor_status,
     92  9698   Peter 					input_name_buffer,
     93  9698   Peter 					output_name);
     94  9698   Peter 	if (major_status != GSS_S_COMPLETE)
     95  9698   Peter 		return (major_status);
     96     0  stevel 
     97     0  stevel 	/*
     98     0  stevel 	 * First create the union name struct that will hold the external
     99     0  stevel 	 * name and the name type.
    100     0  stevel 	 */
    101     0  stevel 	union_name = (gss_union_name_t)malloc(sizeof (gss_union_name_desc));
    102     0  stevel 	if (!union_name)
    103     0  stevel 		return (GSS_S_FAILURE);
    104     0  stevel 
    105     0  stevel 	union_name->mech_type = 0;
    106     0  stevel 	union_name->mech_name = 0;
    107     0  stevel 	union_name->name_type = 0;
    108     0  stevel 	union_name->external_name = 0;
    109     0  stevel 
    110     0  stevel 	/*
    111     0  stevel 	 * All we do here is record the external name and name_type.
    112     0  stevel 	 * When the name is actually used, the underlying gss_import_name()
    113     0  stevel 	 * is called for the appropriate mechanism.  The exception to this
    114     0  stevel 	 * rule is when the name of GSS_C_NT_EXPORT_NAME type.  If that is
    115     0  stevel 	 * the case, then we make it MN in this call.
    116     0  stevel 	 */
    117  5053     gtb 	major_status = gssint_create_copy_buffer(input_name_buffer,
    118     0  stevel 					&union_name->external_name, 0);
    119     0  stevel 	if (major_status != GSS_S_COMPLETE) {
    120     0  stevel 		free(union_name);
    121     0  stevel 		return (major_status);
    122     0  stevel 	}
    123     0  stevel 
    124     0  stevel 	if (input_name_type != GSS_C_NULL_OID) {
    125     0  stevel 		major_status = generic_gss_copy_oid(minor_status,
    126     0  stevel 						input_name_type,
    127     0  stevel 						&union_name->name_type);
    128     0  stevel 		if (major_status != GSS_S_COMPLETE)
    129     0  stevel 			goto allocation_failure;
    130     0  stevel 	}
    131     0  stevel 
    132     0  stevel 	/*
    133     0  stevel 	 * In MIT Distribution the mechanism is determined from the nametype;
    134     0  stevel 	 * This is not a good idea - first mechanism that supports a given
    135     0  stevel 	 * name type is picked up; later on the caller can request a
    136     0  stevel 	 * different mechanism. So we don't determine the mechanism here. Now
    137     0  stevel 	 * the user level and kernel level import_name routine looks similar
    138     0  stevel 	 * except the kernel routine makes a copy of the nametype structure. We
    139     0  stevel 	 * do however make this an MN for names of GSS_C_NT_EXPORT_NAME type.
    140     0  stevel 	 */
    141     0  stevel 	if (input_name_type != GSS_C_NULL_OID &&
    142     0  stevel 	    g_OID_equal(input_name_type, GSS_C_NT_EXPORT_NAME)) {
    143     0  stevel 		major_status = importExportName(minor_status, union_name);
    144     0  stevel 		if (major_status != GSS_S_COMPLETE)
    145     0  stevel 			goto allocation_failure;
    146     0  stevel 	}
    147     0  stevel 
    148     0  stevel 	*output_name = (gss_name_t)union_name;
    149     0  stevel 	return (GSS_S_COMPLETE);
    150     0  stevel 
    151     0  stevel allocation_failure:
    152     0  stevel 	if (union_name) {
    153     0  stevel 		if (union_name->external_name) {
    154     0  stevel 			if (union_name->external_name->value)
    155     0  stevel 				free(union_name->external_name->value);
    156     0  stevel 			free(union_name->external_name);
    157     0  stevel 		}
    158     0  stevel 		if (union_name->name_type)
    159     0  stevel 			(void) generic_gss_release_oid(&tmp,
    160     0  stevel 						    &union_name->name_type);
    161     0  stevel 		if (union_name->mech_name)
    162     0  stevel 			(void) __gss_release_internal_name(minor_status,
    163     0  stevel 						union_name->mech_type,
    164     0  stevel 						&union_name->mech_name);
    165     0  stevel 		if (union_name->mech_type)
    166     0  stevel 			(void) generic_gss_release_oid(&tmp,
    167     0  stevel 						    &union_name->mech_type);
    168     0  stevel 		free(union_name);
    169     0  stevel 	}
    170     0  stevel 	return (major_status);
    171     0  stevel }
    172     0  stevel 
    173     0  stevel 
    174     0  stevel /*
    175     0  stevel  * GSS export name constants
    176     0  stevel  */
    177     0  stevel static const char *expNameTokId = "\x04\x01";
    178     0  stevel static const int expNameTokIdLen = 2;
    179     0  stevel static const int mechOidLenLen = 2;
    180     0  stevel static const int nameTypeLenLen = 2;
    181     0  stevel 
    182     0  stevel static OM_uint32
    183     0  stevel importExportName(minor, unionName)
    184     0  stevel OM_uint32 *minor;
    185     0  stevel gss_union_name_t unionName;
    186     0  stevel {
    187     0  stevel 	gss_OID_desc mechOid;
    188     0  stevel 	gss_buffer_desc expName;
    189     0  stevel 	unsigned char *buf;
    190     0  stevel 	gss_mechanism mech;
    191     0  stevel 	OM_uint32 major, mechOidLen, nameLen, curLength;
    192     0  stevel 	unsigned int bytes;
    193     0  stevel 
    194     0  stevel 	expName.value = unionName->external_name->value;
    195     0  stevel 	expName.length = unionName->external_name->length;
    196     0  stevel 
    197     0  stevel 	curLength = expNameTokIdLen + mechOidLenLen;
    198     0  stevel 	if (expName.length < curLength)
    199     0  stevel 		return (GSS_S_DEFECTIVE_TOKEN);
    200     0  stevel 
    201     0  stevel 	buf = (unsigned char *)expName.value;
    202     0  stevel 	if (memcmp(expNameTokId, buf, expNameTokIdLen) != 0)
    203     0  stevel 		return (GSS_S_DEFECTIVE_TOKEN);
    204     0  stevel 
    205     0  stevel 	buf += expNameTokIdLen;
    206     0  stevel 
    207     0  stevel 	/* extract the mechanism oid length */
    208     0  stevel 	mechOidLen = (*buf++ << 8);
    209     0  stevel 	mechOidLen |= (*buf++);
    210     0  stevel 	curLength += mechOidLen;
    211     0  stevel 	if (expName.length < curLength)
    212     0  stevel 		return (GSS_S_DEFECTIVE_TOKEN);
    213     0  stevel 	/*
    214     0  stevel 	 * The mechOid itself is encoded in DER format, OID Tag (0x06)
    215     0  stevel 	 * length and the value of mech_OID
    216     0  stevel 	 */
    217     0  stevel 	if (*buf++ != 0x06)
    218     0  stevel 		return (GSS_S_DEFECTIVE_TOKEN);
    219     0  stevel 
    220     0  stevel 	/*
    221     0  stevel 	 * mechoid Length is encoded twice; once in 2 bytes as
    222     0  stevel 	 * explained in RFC2743 (under mechanism independent exported
    223     0  stevel 	 * name object format) and once using DER encoding
    224     0  stevel 	 *
    225     0  stevel 	 * We verify both lengths.
    226     0  stevel 	 */
    227     0  stevel 
    228     0  stevel 	mechOid.length = get_der_length(&buf,
    229     0  stevel 				(expName.length - curLength), &bytes);
    230     0  stevel 	mechOid.elements = (void *)buf;
    231     0  stevel 
    232     0  stevel 	/*
    233     0  stevel 	 * 'bytes' is the length of the DER length, '1' is for the DER
    234     0  stevel 	 * tag for OID
    235     0  stevel 	 */
    236     0  stevel 	if ((bytes + mechOid.length + 1) != mechOidLen)
    237     0  stevel 		return (GSS_S_DEFECTIVE_TOKEN);
    238     0  stevel 
    239     0  stevel 	buf += mechOid.length;
    240     0  stevel 	if ((mech = __gss_get_mechanism(&mechOid)) == NULL)
    241     0  stevel 		return (GSS_S_BAD_MECH);
    242     0  stevel 
    243     0  stevel 	if (mech->gss_import_name == NULL)
    244     0  stevel 		return (GSS_S_UNAVAILABLE);
    245     0  stevel 
    246     0  stevel 	/*
    247     0  stevel 	 * we must now determine if we should unwrap the name ourselves
    248     0  stevel 	 * or make the mechanism do it - we should only unwrap it
    249     0  stevel 	 * if we create it; so if mech->gss_export_name == NULL, we must
    250     0  stevel 	 * have created it.
    251     0  stevel 	 */
    252     0  stevel 	if (mech->gss_export_name) {
    253     0  stevel 		if ((major = mech->gss_import_name(mech->context, minor,
    254     0  stevel 				&expName, (gss_OID)GSS_C_NT_EXPORT_NAME,
    255     0  stevel 				&unionName->mech_name)) != GSS_S_COMPLETE ||
    256     0  stevel 			(major = generic_gss_copy_oid(minor, &mechOid,
    257     0  stevel 					&unionName->mech_type)) !=
    258     0  stevel 				GSS_S_COMPLETE) {
    259     0  stevel 			return (major);
    260     0  stevel 		}
    261     0  stevel 		return (major);
    262     0  stevel 	}
    263     0  stevel 	/*
    264     0  stevel 	 * we must have exported the name - so we now need to reconstruct it
    265     0  stevel 	 * and call the mechanism to create it
    266     0  stevel 	 *
    267     0  stevel 	 * WARNING:	Older versions of __gss_export_internal_name() did
    268     0  stevel 	 *		not export names correctly, but now it does.  In
    269     0  stevel 	 *		order to stay compatible with existing exported
    270     0  stevel 	 *		names we must support names exported the broken
    271     0  stevel 	 *		way.
    272     0  stevel 	 *
    273     0  stevel 	 * Specifically, __gss_export_internal_name() used to include
    274     0  stevel 	 * the name type OID in the encoding of the exported MN.
    275     0  stevel 	 * Additionally, the Kerberos V mech used to make display names
    276     0  stevel 	 * that included a null terminator which was counted in the
    277     0  stevel 	 * display name gss_buffer_desc.
    278     0  stevel 	 */
    279     0  stevel 	curLength += 4;		/* 4 bytes for name len */
    280     0  stevel 	if (expName.length < curLength)
    281     0  stevel 		return (GSS_S_DEFECTIVE_TOKEN);
    282     0  stevel 
    283     0  stevel 	/* next 4 bytes in the name are the name length */
    284     0  stevel 	nameLen = (*buf++) << 24;
    285     0  stevel 	nameLen |= (*buf++ << 16);
    286     0  stevel 	nameLen |= (*buf++ << 8);
    287     0  stevel 	nameLen |= (*buf++);
    288     0  stevel 
    289     0  stevel 	/*
    290     0  stevel 	 * we use < here because bad code in rpcsec_gss rounds up exported
    291     0  stevel 	 * name token lengths and pads with nulls, otherwise != would be
    292     0  stevel 	 * appropriate
    293     0  stevel 	 */
    294     0  stevel 	curLength += nameLen;   /* this is the total length */
    295     0  stevel 	if (expName.length < curLength)
    296     0  stevel 		return (GSS_S_DEFECTIVE_TOKEN);
    297     0  stevel 
    298     0  stevel 	/*
    299     0  stevel 	 * We detect broken exported names here: they always start with
    300     0  stevel 	 * a two-octet network-byte order OID length, which is always
    301     0  stevel 	 * less than 256 bytes, so the first octet of the length is
    302     0  stevel 	 * always '\0', which is not allowed in GSS-API display names
    303     0  stevel 	 * (or never occurs in them anyways).  Of course, the OID
    304     0  stevel 	 * shouldn't be there, but it is.  After the OID (sans DER tag
    305     0  stevel 	 * and length) there's the name itself, though null-terminated;
    306     0  stevel 	 * this null terminator should also not be there, but it is.
    307     0  stevel 	 */
    308     0  stevel 	if (nameLen > 0 && *buf == '\0') {
    309     0  stevel 		OM_uint32 nameTypeLen;
    310     0  stevel 		/* next two bytes are the name oid */
    311     0  stevel 		if (nameLen < nameTypeLenLen)
    312     0  stevel 			return (GSS_S_DEFECTIVE_TOKEN);
    313     0  stevel 
    314     0  stevel 		nameLen -= nameTypeLenLen;
    315     0  stevel 
    316     0  stevel 		nameTypeLen = (*buf++) << 8;
    317     0  stevel 		nameTypeLen |= (*buf++);
    318     0  stevel 
    319     0  stevel 		if (nameLen < nameTypeLen)
    320     0  stevel 			return (GSS_S_DEFECTIVE_TOKEN);
    321     0  stevel 
    322     0  stevel 		buf += nameTypeLen;
    323     0  stevel 		nameLen -= nameTypeLen;
    324     0  stevel 
    325     0  stevel 		/*
    326     0  stevel 		 * adjust for expected null terminator that should
    327     0  stevel 		 * really not be there
    328     0  stevel 		 */
    329     0  stevel 		if (nameLen > 0 && *(buf + nameLen - 1) == '\0')
    330     0  stevel 			nameLen--;
    331     0  stevel 	}
    332     0  stevel 
    333     0  stevel 	/*
    334     0  stevel 	 * Can a name be null?  Let the mech decide.
    335     0  stevel 	 *
    336     0  stevel 	 * NOTE: We use GSS_C_NULL_OID as the name type when importing
    337     0  stevel 	 *	 the unwrapped name.  Presumably the exported name had,
    338     0  stevel 	 *	 prior to being exported been obtained in such a way
    339     0  stevel 	 *	 that it has been properly perpared ("canonicalized," in
    340     0  stevel 	 *	 GSS-API terms) accroding to some name type; we cannot
    341     0  stevel 	 *	 tell what that name type was now, but the name should
    342     0  stevel 	 *	 need no further preparation other than the lowest
    343     0  stevel 	 *	 common denominator afforded by the mech to names
    344     0  stevel 	 *	 imported with GSS_C_NULL_OID.  For the Kerberos V mech
    345     0  stevel 	 *	 this means doing less busywork too (particularly once
    346     0  stevel 	 *	 IDN is thrown in with Kerberos V extensions).
    347     0  stevel 	 */
    348     0  stevel 	expName.length = nameLen;
    349     0  stevel 	expName.value = nameLen ? (void *)buf : NULL;
    350     0  stevel 	major = mech->gss_import_name(mech->context, minor, &expName,
    351     0  stevel 			    GSS_C_NULL_OID, &unionName->mech_name);
    352     0  stevel 	if (major != GSS_S_COMPLETE)
    353     0  stevel 		return (major);
    354     0  stevel 
    355     0  stevel 	return (generic_gss_copy_oid(minor, &mechOid, &unionName->mech_type));
    356     0  stevel } /* importExportName */
    357