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