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