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