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