1 /* 2 * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.security.jgss; 27 28 import org.ietf.jgss.*; 29 import sun.security.jgss.spi.*; 30 import java.util.Set; 31 import java.util.HashMap; 32 import java.util.HashSet; 33 import java.util.Arrays; 34 import java.io.IOException; 35 import java.io.UnsupportedEncodingException; 36 import sun.security.util.ObjectIdentifier; 37 import sun.security.util.DerInputStream; 38 import sun.security.util.DerOutputStream; 39 40 /** 41 * This is the implementation class for GSSName. Conceptually the 42 * GSSName is a container with mechanism specific name elements. Each 43 * name element is a representation of how that particular mechanism 44 * would canonicalize this principal. 45 * 46 * Generally a GSSName is created by an application when it supplies 47 * a sequence of bytes and a nametype that helps each mechanism 48 * decide how to interpret those bytes. 49 * 50 * It is not necessary to create name elements for each available 51 * mechanism at the time the application creates the GSSName. This 52 * implementation does this lazily, as and when name elements for 53 * mechanisms are required to be handed out. (Generally, other GSS 54 * classes like GSSContext and GSSCredential request specific 55 * elements depending on the mechanisms that they are dealing with.) 56 * Assume that getting a mechanism to parse the applciation specified 57 * bytes is an expensive call. 58 * 59 * When a GSSName is canonicalized wrt some mechanism, it is supposed 60 * to discard all elements of other mechanisms and retain only the 61 * element for this mechanism. In GSS terminology this is called a 62 * Mechanism Name or MN. This implementation tries to retain the 63 * application provided bytes and name type just in case the MN is 64 * asked to produce an element for a mechanism that is different. 65 * 66 * When a GSSName is to be exported, the name element for the desired 67 * mechanism is converted to a byte representation and written 68 * out. It might happen that a name element for that mechanism cannot 69 * be obtained. This happens when the mechanism is just not supported 70 * in this GSS-API or when the mechanism is supported but bytes 71 * corresponding to the nametypes that it understands are not 72 * available in this GSSName. 73 * 74 * This class is safe for sharing. Each retrieval of a name element 75 * from getElement() might potentially add a new element to the 76 * hashmap of elements, but getElement() is synchronized. 77 * 78 * @author Mayank Upadhyay 79 * @since 1.4 80 */ 81 82 public class GSSNameImpl implements GSSName { 83 84 /** 85 * The old Oid used in RFC 2853. Now supported as 86 * input parameters in: 87 * 88 * 1. The four overloaded GSSManager.createName(*) methods 89 * 2. GSSManager.getMechsForName(Oid) 90 * 91 * Note that even if a GSSName is created with this old Oid, 92 * its internal name type and getStringNameType() output are 93 * always the new value. 94 */ 95 final static Oid oldHostbasedServiceName; 96 97 static { 98 Oid tmp = null; 99 try { 100 tmp = new Oid("1.3.6.1.5.6.2"); 101 } catch (Exception e) { 102 // should never happen 103 } 104 oldHostbasedServiceName = tmp; 105 } 106 107 private GSSManagerImpl gssManager = null; 108 109 /* 110 * Store whatever the application passed in. We will use this to 111 * get individual mechanisms to create name elements as and when 112 * needed. 113 * Store both the String and the byte[]. Leave I18N to the 114 * mechanism by allowing it to extract bytes from the String! 115 */ 116 117 private String appNameStr = null; 118 private byte[] appNameBytes = null; 119 private Oid appNameType = null; 120 121 /* 122 * When we figure out what the printable name would be, we store 123 * both the name and its type. 124 */ 125 126 private String printableName = null; 127 private Oid printableNameType = null; 128 129 private HashMap<Oid, GSSNameSpi> elements = null; 130 private GSSNameSpi mechElement = null; 131 wrapElement(GSSManagerImpl gssManager, GSSNameSpi mechElement)132 static GSSNameImpl wrapElement(GSSManagerImpl gssManager, 133 GSSNameSpi mechElement) throws GSSException { 134 return (mechElement == null ? 135 null : new GSSNameImpl(gssManager, mechElement)); 136 } 137 GSSNameImpl(GSSManagerImpl gssManager, GSSNameSpi mechElement)138 GSSNameImpl(GSSManagerImpl gssManager, GSSNameSpi mechElement) { 139 this.gssManager = gssManager; 140 appNameStr = printableName = mechElement.toString(); 141 appNameType = printableNameType = mechElement.getStringNameType(); 142 this.mechElement = mechElement; 143 elements = new HashMap<Oid, GSSNameSpi>(1); 144 elements.put(mechElement.getMechanism(), this.mechElement); 145 } 146 GSSNameImpl(GSSManagerImpl gssManager, Object appName, Oid appNameType)147 GSSNameImpl(GSSManagerImpl gssManager, 148 Object appName, 149 Oid appNameType) 150 throws GSSException { 151 this(gssManager, appName, appNameType, null); 152 } 153 GSSNameImpl(GSSManagerImpl gssManager, Object appName, Oid appNameType, Oid mech)154 GSSNameImpl(GSSManagerImpl gssManager, 155 Object appName, 156 Oid appNameType, 157 Oid mech) 158 throws GSSException { 159 160 if (oldHostbasedServiceName.equals(appNameType)) { 161 appNameType = GSSName.NT_HOSTBASED_SERVICE; 162 } 163 if (appName == null) 164 throw new GSSExceptionImpl(GSSException.BAD_NAME, 165 "Cannot import null name"); 166 if (mech == null) mech = ProviderList.DEFAULT_MECH_OID; 167 if (NT_EXPORT_NAME.equals(appNameType)) { 168 importName(gssManager, appName); 169 } else { 170 init(gssManager, appName, appNameType, mech); 171 } 172 } 173 init(GSSManagerImpl gssManager, Object appName, Oid appNameType, Oid mech)174 private void init(GSSManagerImpl gssManager, 175 Object appName, Oid appNameType, 176 Oid mech) 177 throws GSSException { 178 179 this.gssManager = gssManager; 180 this.elements = 181 new HashMap<Oid, GSSNameSpi>(gssManager.getMechs().length); 182 183 if (appName instanceof String) { 184 this.appNameStr = (String) appName; 185 /* 186 * If appNameType is null, then the nametype for this printable 187 * string is determined only by interrogating the 188 * mechanism. Thus, defer the setting of printableName and 189 * printableNameType till later. 190 */ 191 if (appNameType != null) { 192 printableName = appNameStr; 193 printableNameType = appNameType; 194 } 195 } else { 196 this.appNameBytes = (byte[]) appName; 197 } 198 199 this.appNameType = appNameType; 200 201 mechElement = getElement(mech); 202 203 /* 204 * printableName will be null if appName was in a byte[] or if 205 * appName was in a String but appNameType was null. 206 */ 207 if (printableName == null) { 208 printableName = mechElement.toString(); 209 printableNameType = mechElement.getStringNameType(); 210 } 211 212 /* 213 * At this point the GSSNameImpl has the following set: 214 * appNameStr or appNameBytes 215 * appNameType (could be null) 216 * printableName 217 * printableNameType 218 * mechElement (which also exists in the hashmap of elements) 219 */ 220 } 221 importName(GSSManagerImpl gssManager, Object appName)222 private void importName(GSSManagerImpl gssManager, 223 Object appName) 224 throws GSSException { 225 226 int pos = 0; 227 byte[] bytes = null; 228 229 if (appName instanceof String) { 230 try { 231 bytes = ((String) appName).getBytes("UTF-8"); 232 } catch (UnsupportedEncodingException e) { 233 // Won't happen 234 } 235 } else 236 bytes = (byte[]) appName; 237 238 if ((bytes[pos++] != 0x04) || 239 (bytes[pos++] != 0x01)) 240 throw new GSSExceptionImpl(GSSException.BAD_NAME, 241 "Exported name token id is corrupted!"); 242 243 int oidLen = (((0xFF & bytes[pos++]) << 8) | 244 (0xFF & bytes[pos++])); 245 ObjectIdentifier temp = null; 246 try { 247 DerInputStream din = new DerInputStream(bytes, pos, 248 oidLen); 249 temp = new ObjectIdentifier(din); 250 } catch (IOException e) { 251 throw new GSSExceptionImpl(GSSException.BAD_NAME, 252 "Exported name Object identifier is corrupted!"); 253 } 254 Oid oid = new Oid(temp.toString()); 255 pos += oidLen; 256 int mechPortionLen = (((0xFF & bytes[pos++]) << 24) | 257 ((0xFF & bytes[pos++]) << 16) | 258 ((0xFF & bytes[pos++]) << 8) | 259 (0xFF & bytes[pos++])); 260 if (mechPortionLen < 0 || pos > bytes.length - mechPortionLen) { 261 throw new GSSExceptionImpl(GSSException.BAD_NAME, 262 "Exported name mech name is corrupted!"); 263 } 264 byte[] mechPortion = new byte[mechPortionLen]; 265 System.arraycopy(bytes, pos, mechPortion, 0, mechPortionLen); 266 267 init(gssManager, mechPortion, NT_EXPORT_NAME, oid); 268 } 269 canonicalize(Oid mech)270 public GSSName canonicalize(Oid mech) throws GSSException { 271 if (mech == null) mech = ProviderList.DEFAULT_MECH_OID; 272 273 return wrapElement(gssManager, getElement(mech)); 274 } 275 276 /** 277 * This method may return false negatives. But if it says two 278 * names are equals, then there is some mechanism that 279 * authenticates them as the same principal. 280 */ equals(GSSName other)281 public boolean equals(GSSName other) throws GSSException { 282 283 if (this.isAnonymous() || other.isAnonymous()) 284 return false; 285 286 if (other == this) 287 return true; 288 289 if (! (other instanceof GSSNameImpl)) 290 return equals(gssManager.createName(other.toString(), 291 other.getStringNameType())); 292 293 /* 294 * XXX Do a comparison of the appNameStr/appNameBytes if 295 * available. If that fails, then proceed with this test. 296 */ 297 298 GSSNameImpl that = (GSSNameImpl) other; 299 300 GSSNameSpi myElement = this.mechElement; 301 GSSNameSpi element = that.mechElement; 302 303 /* 304 * XXX If they are not of the same mechanism type, convert both to 305 * Kerberos since it is guaranteed to be present. 306 */ 307 if ((myElement == null) && (element != null)) { 308 myElement = this.getElement(element.getMechanism()); 309 } else if ((myElement != null) && (element == null)) { 310 element = that.getElement(myElement.getMechanism()); 311 } 312 313 if (myElement != null && element != null) { 314 return myElement.equals(element); 315 } 316 317 if ((this.appNameType != null) && 318 (that.appNameType != null)) { 319 if (!this.appNameType.equals(that.appNameType)) { 320 return false; 321 } 322 byte[] myBytes = null; 323 byte[] bytes = null; 324 try { 325 myBytes = 326 (this.appNameStr != null ? 327 this.appNameStr.getBytes("UTF-8") : 328 this.appNameBytes); 329 bytes = 330 (that.appNameStr != null ? 331 that.appNameStr.getBytes("UTF-8") : 332 that.appNameBytes); 333 } catch (UnsupportedEncodingException e) { 334 // Won't happen 335 } 336 337 return Arrays.equals(myBytes, bytes); 338 } 339 340 return false; 341 342 } 343 344 /** 345 * Returns a hashcode value for this GSSName. 346 * 347 * @return a hashCode value 348 */ hashCode()349 public int hashCode() { 350 /* 351 * XXX 352 * In order to get this to work reliably and properly(!), obtain a 353 * Kerberos name element for the name and then call hashCode on its 354 * string representation. But this cannot be done if the nametype 355 * is not one of those supported by the Kerberos provider and hence 356 * this name cannot be imported by Kerberos. In that case return a 357 * constant value! 358 */ 359 360 return 1; 361 } 362 equals(Object another)363 public boolean equals(Object another) { 364 365 try { 366 // XXX This can lead to an infinite loop. Extract info 367 // and create a GSSNameImpl with it. 368 369 if (another instanceof GSSName) 370 return equals((GSSName) another); 371 } catch (GSSException e) { 372 // Squelch it and return false 373 } 374 375 return false; 376 } 377 378 /** 379 * Returns a flat name representation for this object. The name 380 * format is defined in RFC 2743: 381 *<pre> 382 * Length Name Description 383 * 2 TOK_ID Token Identifier 384 * For exported name objects, this 385 * must be hex 04 01. 386 * 2 MECH_OID_LEN Length of the Mechanism OID 387 * MECH_OID_LEN MECH_OID Mechanism OID, in DER 388 * 4 NAME_LEN Length of name 389 * NAME_LEN NAME Exported name; format defined in 390 * applicable mechanism draft. 391 *</pre> 392 * 393 * Note that it is not required to canonicalize a name before 394 * calling export(). i.e., the name need not be an MN. If it is 395 * not an MN, an implementation defined algorithm can be used for 396 * choosing the mechanism which should export this name. 397 * 398 * @return the flat name representation for this object 399 * @exception GSSException with major codes NAME_NOT_MN, BAD_NAME, 400 * BAD_NAME, FAILURE. 401 */ export()402 public byte[] export() throws GSSException { 403 404 if (mechElement == null) { 405 /* Use default mech */ 406 mechElement = getElement(ProviderList.DEFAULT_MECH_OID); 407 } 408 409 byte[] mechPortion = mechElement.export(); 410 byte[] oidBytes = null; 411 ObjectIdentifier oid = null; 412 413 try { 414 oid = new ObjectIdentifier 415 (mechElement.getMechanism().toString()); 416 } catch (IOException e) { 417 throw new GSSExceptionImpl(GSSException.FAILURE, 418 "Invalid OID String "); 419 } 420 DerOutputStream dout = new DerOutputStream(); 421 try { 422 dout.putOID(oid); 423 } catch (IOException e) { 424 throw new GSSExceptionImpl(GSSException.FAILURE, 425 "Could not ASN.1 Encode " 426 + oid.toString()); 427 } 428 oidBytes = dout.toByteArray(); 429 430 byte[] retVal = new byte[2 431 + 2 + oidBytes.length 432 + 4 + mechPortion.length]; 433 int pos = 0; 434 retVal[pos++] = 0x04; 435 retVal[pos++] = 0x01; 436 retVal[pos++] = (byte) (oidBytes.length>>>8); 437 retVal[pos++] = (byte) oidBytes.length; 438 System.arraycopy(oidBytes, 0, retVal, pos, oidBytes.length); 439 pos += oidBytes.length; 440 retVal[pos++] = (byte) (mechPortion.length>>>24); 441 retVal[pos++] = (byte) (mechPortion.length>>>16); 442 retVal[pos++] = (byte) (mechPortion.length>>>8); 443 retVal[pos++] = (byte) mechPortion.length; 444 System.arraycopy(mechPortion, 0, retVal, pos, mechPortion.length); 445 return retVal; 446 } 447 toString()448 public String toString() { 449 return printableName; 450 451 } 452 getStringNameType()453 public Oid getStringNameType() throws GSSException { 454 return printableNameType; 455 } 456 isAnonymous()457 public boolean isAnonymous() { 458 if (printableNameType == null) { 459 return false; 460 } else { 461 return GSSName.NT_ANONYMOUS.equals(printableNameType); 462 } 463 } 464 isMN()465 public boolean isMN() { 466 return true; // Since always canonicalized for some mech 467 } 468 getElement(Oid mechOid)469 public synchronized GSSNameSpi getElement(Oid mechOid) 470 throws GSSException { 471 472 GSSNameSpi retVal = elements.get(mechOid); 473 474 if (retVal == null) { 475 if (appNameStr != null) { 476 retVal = gssManager.getNameElement 477 (appNameStr, appNameType, mechOid); 478 } else { 479 retVal = gssManager.getNameElement 480 (appNameBytes, appNameType, mechOid); 481 } 482 elements.put(mechOid, retVal); 483 } 484 return retVal; 485 } 486 getElements()487 Set<GSSNameSpi> getElements() { 488 return new HashSet<GSSNameSpi>(elements.values()); 489 } 490 getNameTypeStr(Oid nameTypeOid)491 private static String getNameTypeStr(Oid nameTypeOid) { 492 493 if (nameTypeOid == null) 494 return "(NT is null)"; 495 496 if (nameTypeOid.equals(NT_USER_NAME)) 497 return "NT_USER_NAME"; 498 if (nameTypeOid.equals(NT_HOSTBASED_SERVICE)) 499 return "NT_HOSTBASED_SERVICE"; 500 if (nameTypeOid.equals(NT_EXPORT_NAME)) 501 return "NT_EXPORT_NAME"; 502 if (nameTypeOid.equals(GSSUtil.NT_GSS_KRB5_PRINCIPAL)) 503 return "NT_GSS_KRB5_PRINCIPAL"; 504 else 505 return "Unknown"; 506 } 507 } 508