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