1 /*
   2  * Copyright (c) 2000, 2019, 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 /*
  27  *
  28  *  (C) Copyright IBM Corp. 1999 All Rights Reserved.
  29  *  Copyright 1997 The Open Group Research Institute.  All rights reserved.
  30  */
  31 
  32 package sun.security.krb5;
  33 
  34 import sun.security.krb5.internal.*;
  35 import sun.security.util.*;
  36 import java.net.*;
  37 import java.util.Vector;
  38 import java.util.Locale;
  39 import java.io.IOException;
  40 import java.math.BigInteger;
  41 import java.util.Arrays;
  42 import sun.security.krb5.internal.ccache.CCacheOutputStream;
  43 import sun.security.krb5.internal.util.KerberosString;
  44 
  45 
  46 /**
  47  * Implements the ASN.1 PrincipalName type and its realm in a single class.
  48  * <pre>{@code
  49  *    Realm           ::= KerberosString
  50  *
  51  *    PrincipalName   ::= SEQUENCE {
  52  *            name-type       [0] Int32,
  53  *            name-string     [1] SEQUENCE OF KerberosString
  54  *    }
  55  * }</pre>
  56  * This class is immutable.
  57  * @see Realm
  58  */
  59 public class PrincipalName implements Cloneable {
  60 
  61     //name types
  62 
  63     /**
  64      * Name type not known
  65      */
  66     public static final int KRB_NT_UNKNOWN =   0;
  67 
  68     /**
  69      * Just the name of the principal as in DCE, or for users
  70      */
  71     public static final int KRB_NT_PRINCIPAL = 1;
  72 
  73     /**
  74      * Service and other unique instance (krbtgt)
  75      */
  76     public static final int KRB_NT_SRV_INST =  2;
  77 
  78     /**
  79      * Service with host name as instance (telnet, rcommands)
  80      */
  81     public static final int KRB_NT_SRV_HST =   3;
  82 
  83     /**
  84      * Service with host as remaining components
  85      */
  86     public static final int KRB_NT_SRV_XHST =  4;
  87 
  88     /**
  89      * Unique ID
  90      */
  91     public static final int KRB_NT_UID = 5;
  92 
  93     /**
  94      * Enterprise name (alias)
  95      */
  96     public static final int KRB_NT_ENTERPRISE = 10;
  97 
  98     /**
  99      * TGS Name
 100      */
 101     public static final String TGS_DEFAULT_SRV_NAME = "krbtgt";
 102     public static final int TGS_DEFAULT_NT = KRB_NT_SRV_INST;
 103 
 104     public static final char NAME_COMPONENT_SEPARATOR = '/';
 105     public static final char NAME_REALM_SEPARATOR = '@';
 106     public static final char REALM_COMPONENT_SEPARATOR = '.';
 107 
 108     public static final String NAME_COMPONENT_SEPARATOR_STR = "/";
 109     public static final String NAME_REALM_SEPARATOR_STR = "@";
 110     public static final String REALM_COMPONENT_SEPARATOR_STR = ".";
 111 
 112     // Instance fields.
 113 
 114     /**
 115      * The name type, from PrincipalName's name-type field.
 116      */
 117     private final int nameType;
 118 
 119     /**
 120      * The name strings, from PrincipalName's name-strings field. This field
 121      * must be neither null nor empty. Each entry of it must also be neither
 122      * null nor empty. Make sure to clone the field when it's passed in or out.
 123      */
 124     private final String[] nameStrings;
 125 
 126     /**
 127      * The realm this principal belongs to.
 128      */
 129     private final Realm nameRealm;      // not null
 130 
 131 
 132     /**
 133      * When constructing a PrincipalName, whether the realm is included in
 134      * the input, or deduced from default realm or domain-realm mapping.
 135      */
 136     private final boolean realmDeduced;
 137 
 138     // cached default salt, not used in clone
 139     private transient String salt = null;
 140 
 141     // There are 3 basic constructors. All other constructors must call them.
 142     // All basic constructors must call validateNameStrings.
 143     // 1. From name components
 144     // 2. From name
 145     // 3. From DER encoding
 146 
 147     /**
 148      * Creates a PrincipalName.
 149      */
 150     public PrincipalName(int nameType, String[] nameStrings, Realm nameRealm) {
 151         if (nameRealm == null) {
 152             throw new IllegalArgumentException("Null realm not allowed");
 153         }
 154         validateNameStrings(nameStrings);
 155         this.nameType = nameType;
 156         this.nameStrings = nameStrings.clone();
 157         this.nameRealm = nameRealm;
 158         this.realmDeduced = false;
 159     }
 160 
 161     // This method is called by Windows NativeCred.c
 162     public PrincipalName(String[] nameParts, String realm) throws RealmException {
 163         this(KRB_NT_UNKNOWN, nameParts, new Realm(realm));
 164     }
 165 
 166     // Validate a nameStrings argument
 167     private static void validateNameStrings(String[] ns) {
 168         if (ns == null) {
 169             throw new IllegalArgumentException("Null nameStrings not allowed");
 170         }
 171         if (ns.length == 0) {
 172             throw new IllegalArgumentException("Empty nameStrings not allowed");
 173         }
 174         for (String s: ns) {
 175             if (s == null) {
 176                 throw new IllegalArgumentException("Null nameString not allowed");
 177             }
 178             if (s.isEmpty()) {
 179                 throw new IllegalArgumentException("Empty nameString not allowed");
 180             }
 181         }
 182     }
 183 
 184     public Object clone() {
 185         try {
 186             PrincipalName pName = (PrincipalName) super.clone();
 187             UNSAFE.putReference(this, NAME_STRINGS_OFFSET, nameStrings.clone());
 188             return pName;
 189         } catch (CloneNotSupportedException ex) {
 190             throw new AssertionError("Should never happen");
 191         }
 192     }
 193 
 194     private static final long NAME_STRINGS_OFFSET;
 195     private static final jdk.internal.misc.Unsafe UNSAFE;
 196     static {
 197         try {
 198             jdk.internal.misc.Unsafe unsafe = jdk.internal.misc.Unsafe.getUnsafe();
 199             NAME_STRINGS_OFFSET = unsafe.objectFieldOffset(
 200                     PrincipalName.class.getDeclaredField("nameStrings"));
 201             UNSAFE = unsafe;
 202         } catch (ReflectiveOperationException e) {
 203             throw new Error(e);
 204         }
 205     }
 206 
 207     @Override
 208     public boolean equals(Object o) {
 209         if (this == o) {
 210             return true;
 211         }
 212         if (o instanceof PrincipalName) {
 213             PrincipalName other = (PrincipalName)o;
 214             return nameRealm.equals(other.nameRealm) &&
 215                     Arrays.equals(nameStrings, other.nameStrings);
 216         }
 217         return false;
 218     }
 219 
 220     /**
 221      * Returns the ASN.1 encoding of the
 222      * <pre>{@code
 223      * PrincipalName    ::= SEQUENCE {
 224      *          name-type       [0] Int32,
 225      *          name-string     [1] SEQUENCE OF KerberosString
 226      * }
 227      *
 228      * KerberosString   ::= GeneralString (IA5String)
 229      * }</pre>
 230      *
 231      * <p>
 232      * This definition reflects the Network Working Group RFC 4120
 233      * specification available at
 234      * <a href="http://www.ietf.org/rfc/rfc4120.txt">
 235      * http://www.ietf.org/rfc/rfc4120.txt</a>.
 236      *
 237      * @param encoding DER-encoded PrincipalName (without Realm)
 238      * @param realm the realm for this name
 239      * @exception Asn1Exception if an error occurs while decoding
 240      * an ASN1 encoded data.
 241      * @exception Asn1Exception if there is an ASN1 encoding error
 242      * @exception IOException if an I/O error occurs
 243      * @exception IllegalArgumentException if encoding is null
 244      * reading encoded data.
 245      */
 246     public PrincipalName(DerValue encoding, Realm realm)
 247             throws Asn1Exception, IOException {
 248         if (realm == null) {
 249             throw new IllegalArgumentException("Null realm not allowed");
 250         }
 251         realmDeduced = false;
 252         nameRealm = realm;
 253         DerValue der;
 254         if (encoding == null) {
 255             throw new IllegalArgumentException("Null encoding not allowed");
 256         }
 257         if (encoding.getTag() != DerValue.tag_Sequence) {
 258             throw new Asn1Exception(Krb5.ASN1_BAD_ID);
 259         }
 260         der = encoding.getData().getDerValue();
 261         if ((der.getTag() & 0x1F) == 0x00) {
 262             BigInteger bint = der.getData().getBigInteger();
 263             nameType = bint.intValue();
 264         } else {
 265             throw new Asn1Exception(Krb5.ASN1_BAD_ID);
 266         }
 267         der = encoding.getData().getDerValue();
 268         if ((der.getTag() & 0x01F) == 0x01) {
 269             DerValue subDer = der.getData().getDerValue();
 270             if (subDer.getTag() != DerValue.tag_SequenceOf) {
 271                 throw new Asn1Exception(Krb5.ASN1_BAD_ID);
 272             }
 273             Vector<String> v = new Vector<>();
 274             DerValue subSubDer;
 275             while(subDer.getData().available() > 0) {
 276                 subSubDer = subDer.getData().getDerValue();
 277                 String namePart = new KerberosString(subSubDer).toString();
 278                 v.addElement(namePart);
 279             }
 280             nameStrings = new String[v.size()];
 281             v.copyInto(nameStrings);
 282             validateNameStrings(nameStrings);
 283         } else  {
 284             throw new Asn1Exception(Krb5.ASN1_BAD_ID);
 285         }
 286     }
 287 
 288     /**
 289      * Parse (unmarshal) a <code>PrincipalName</code> from a DER
 290      * input stream.  This form
 291      * parsing might be used when expanding a value which is part of
 292      * a constructed sequence and uses explicitly tagged type.
 293      *
 294      * @exception Asn1Exception on error.
 295      * @param data the Der input stream value, which contains one or
 296      * more marshaled value.
 297      * @param explicitTag tag number.
 298      * @param optional indicate if this data field is optional
 299      * @param realm the realm for the name
 300      * @return an instance of <code>PrincipalName</code>, or null if the
 301      * field is optional and missing.
 302      */
 303     public static PrincipalName parse(DerInputStream data,
 304                                       byte explicitTag, boolean
 305                                       optional,
 306                                       Realm realm)
 307         throws Asn1Exception, IOException, RealmException {
 308 
 309         if ((optional) && (((byte)data.peekByte() & (byte)0x1F) !=
 310                            explicitTag))
 311             return null;
 312         DerValue der = data.getDerValue();
 313         if (explicitTag != (der.getTag() & (byte)0x1F)) {
 314             throw new Asn1Exception(Krb5.ASN1_BAD_ID);
 315         } else {
 316             DerValue subDer = der.getData().getDerValue();
 317             if (realm == null) {
 318                 realm = Realm.getDefault();
 319             }
 320             return new PrincipalName(subDer, realm);
 321         }
 322     }
 323 
 324 
 325     // XXX Error checkin consistent with MIT krb5_parse_name
 326     // Code repetition, realm parsed again by class Realm
 327     private static String[] parseName(String name) {
 328 
 329         Vector<String> tempStrings = new Vector<>();
 330         String temp = name;
 331         int i = 0;
 332         int componentStart = 0;
 333         String component;
 334 
 335         while (i < temp.length()) {
 336             if (temp.charAt(i) == NAME_COMPONENT_SEPARATOR) {
 337                 /*
 338                  * If this separator is escaped then don't treat it
 339                  * as a separator
 340                  */
 341                 if (i > 0 && temp.charAt(i - 1) == '\\') {
 342                     temp = temp.substring(0, i - 1) +
 343                         temp.substring(i, temp.length());
 344                     continue;
 345                 }
 346                 else {
 347                     if (componentStart <= i) {
 348                         component = temp.substring(componentStart, i);
 349                         tempStrings.addElement(component);
 350                     }
 351                     componentStart = i + 1;
 352                 }
 353             } else {
 354                 if (temp.charAt(i) == NAME_REALM_SEPARATOR) {
 355                     /*
 356                      * If this separator is escaped then don't treat it
 357                      * as a separator
 358                      */
 359                     if (i > 0 && temp.charAt(i - 1) == '\\') {
 360                         temp = temp.substring(0, i - 1) +
 361                             temp.substring(i, temp.length());
 362                         continue;
 363                     } else {
 364                         if (componentStart < i) {
 365                             component = temp.substring(componentStart, i);
 366                             tempStrings.addElement(component);
 367                         }
 368                         componentStart = i + 1;
 369                         break;
 370                     }
 371                 }
 372             }
 373             i++;
 374         }
 375 
 376         if (i == temp.length()) {
 377             component = temp.substring(componentStart, i);
 378             tempStrings.addElement(component);
 379         }
 380 
 381         String[] result = new String[tempStrings.size()];
 382         tempStrings.copyInto(result);
 383         return result;
 384     }
 385 
 386     /**
 387      * Constructs a PrincipalName from a string.
 388      * @param name the name
 389      * @param type the type
 390      * @param realm the realm, null if not known. Note that when realm is not
 391      * null, it will be always used even if there is a realm part in name. When
 392      * realm is null, will read realm part from name, or try to map a realm
 393      * (for KRB_NT_SRV_HST), or use the default realm, or fail
 394      * @throws RealmException
 395      */
 396     public PrincipalName(String name, int type, String realm)
 397             throws RealmException {
 398         if (name == null) {
 399             throw new IllegalArgumentException("Null name not allowed");
 400         }
 401         String[] nameParts = parseName(name);
 402         validateNameStrings(nameParts);
 403         if (realm == null) {
 404             realm = Realm.parseRealmAtSeparator(name);
 405         }
 406 
 407         // No realm info from parameter and string, must deduce later
 408         realmDeduced = realm == null;
 409 
 410         switch (type) {
 411         case KRB_NT_SRV_HST:
 412             if (nameParts.length >= 2) {
 413                 String hostName = nameParts[1];
 414                 Boolean option;
 415                 try {
 416                     // If true, try canonicalizing and accept it if it starts
 417                     // with the short name. Otherwise, never. Default true.
 418                     option = Config.getInstance().getBooleanObject(
 419                             "libdefaults", "dns_canonicalize_hostname");
 420                 } catch (KrbException e) {
 421                     option = null;
 422                 }
 423                 if (option != Boolean.FALSE) {
 424                     try {
 425                         // RFC4120 does not recommend canonicalizing a hostname.
 426                         // However, for compatibility reason, we will try
 427                         // canonicalizing it and see if the output looks better.
 428 
 429                         String canonicalized = (InetAddress.getByName(hostName)).
 430                                 getCanonicalHostName();
 431 
 432                         // Looks if canonicalized is a longer format of hostName,
 433                         // we accept cases like
 434                         //     bunny -> bunny.rabbit.hole
 435                         if (canonicalized.toLowerCase(Locale.ENGLISH).startsWith(
 436                                 hostName.toLowerCase(Locale.ENGLISH) + ".")) {
 437                             hostName = canonicalized;
 438                         }
 439                     } catch (UnknownHostException | SecurityException e) {
 440                         // not canonicalized or no permission to do so, use old
 441                     }
 442                     if (hostName.endsWith(".")) {
 443                         hostName = hostName.substring(0, hostName.length() - 1);
 444                     }
 445                 }
 446                 nameParts[1] = hostName.toLowerCase(Locale.ENGLISH);
 447             }
 448             nameStrings = nameParts;
 449             nameType = type;
 450 
 451             if (realm != null) {
 452                 nameRealm = new Realm(realm);
 453             } else {
 454                 // We will try to get realm name from the mapping in
 455                 // the configuration. If it is not specified
 456                 // we will use the default realm. This nametype does
 457                 // not allow a realm to be specified. The name string must of
 458                 // the form service@host and this is internally changed into
 459                 // service/host by Kerberos
 460                 String mapRealm =  mapHostToRealm(nameParts[1]);
 461                 if (mapRealm != null) {
 462                     nameRealm = new Realm(mapRealm);
 463                 } else {
 464                     nameRealm = Realm.getDefault();
 465                 }
 466             }
 467             break;
 468         case KRB_NT_UNKNOWN:
 469         case KRB_NT_PRINCIPAL:
 470         case KRB_NT_SRV_INST:
 471         case KRB_NT_SRV_XHST:
 472         case KRB_NT_UID:
 473         case KRB_NT_ENTERPRISE:
 474             nameStrings = nameParts;
 475             nameType = type;
 476             if (realm != null) {
 477                 nameRealm = new Realm(realm);
 478             } else {
 479                 nameRealm = Realm.getDefault();
 480             }
 481             break;
 482         default:
 483             throw new IllegalArgumentException("Illegal name type");
 484         }
 485     }
 486 
 487     public PrincipalName(String name, int type) throws RealmException {
 488         this(name, type, (String)null);
 489     }
 490 
 491     public PrincipalName(String name) throws RealmException {
 492         this(name, KRB_NT_UNKNOWN);
 493     }
 494 
 495     public PrincipalName(String name, String realm) throws RealmException {
 496         this(name, KRB_NT_UNKNOWN, realm);
 497     }
 498 
 499     public static PrincipalName tgsService(String r1, String r2)
 500             throws KrbException {
 501         return new PrincipalName(PrincipalName.KRB_NT_SRV_INST,
 502                 new String[] {PrincipalName.TGS_DEFAULT_SRV_NAME, r1},
 503                 new Realm(r2));
 504     }
 505 
 506     public String getRealmAsString() {
 507         return getRealmString();
 508     }
 509 
 510     public String getPrincipalNameAsString() {
 511         StringBuilder temp = new StringBuilder(nameStrings[0]);
 512         for (int i = 1; i < nameStrings.length; i++)
 513             temp.append(nameStrings[i]);
 514         return temp.toString();
 515     }
 516 
 517     public int hashCode() {
 518         return toString().hashCode();
 519     }
 520 
 521     public String getName() {
 522         return toString();
 523     }
 524 
 525     public int getNameType() {
 526         return nameType;
 527     }
 528 
 529     public String[] getNameStrings() {
 530         return nameStrings.clone();
 531     }
 532 
 533     public byte[][] toByteArray() {
 534         byte[][] result = new byte[nameStrings.length][];
 535         for (int i = 0; i < nameStrings.length; i++) {
 536             result[i] = new byte[nameStrings[i].length()];
 537             result[i] = nameStrings[i].getBytes();
 538         }
 539         return result;
 540     }
 541 
 542     public String getRealmString() {
 543         return nameRealm.toString();
 544     }
 545 
 546     public Realm getRealm() {
 547         return nameRealm;
 548     }
 549 
 550     public String getSalt() {
 551         if (salt == null) {
 552             StringBuilder salt = new StringBuilder();
 553             salt.append(nameRealm.toString());
 554             for (int i = 0; i < nameStrings.length; i++) {
 555                 salt.append(nameStrings[i]);
 556             }
 557             return salt.toString();
 558         }
 559         return salt;
 560     }
 561 
 562     public String toString() {
 563         StringBuilder str = new StringBuilder();
 564         for (int i = 0; i < nameStrings.length; i++) {
 565             if (i > 0)
 566                 str.append("/");
 567             str.append(nameStrings[i]);
 568         }
 569         str.append("@");
 570         str.append(nameRealm.toString());
 571         return str.toString();
 572     }
 573 
 574     public String getNameString() {
 575         StringBuilder str = new StringBuilder();
 576         for (int i = 0; i < nameStrings.length; i++) {
 577             if (i > 0)
 578                 str.append("/");
 579             str.append(nameStrings[i]);
 580         }
 581         return str.toString();
 582     }
 583 
 584     /**
 585      * Encodes a <code>PrincipalName</code> object. Note that only the type and
 586      * names are encoded. To encode the realm, call getRealm().asn1Encode().
 587      * @return the byte array of the encoded PrncipalName object.
 588      * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data.
 589      * @exception IOException if an I/O error occurs while reading encoded data.
 590      *
 591      */
 592     public byte[] asn1Encode() throws Asn1Exception, IOException {
 593         DerOutputStream bytes = new DerOutputStream();
 594         DerOutputStream temp = new DerOutputStream();
 595         BigInteger bint = BigInteger.valueOf(this.nameType);
 596         temp.putInteger(bint);
 597         bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x00), temp);
 598         temp = new DerOutputStream();
 599         DerValue[] der = new DerValue[nameStrings.length];
 600         for (int i = 0; i < nameStrings.length; i++) {
 601             der[i] = new KerberosString(nameStrings[i]).toDerValue();
 602         }
 603         temp.putSequence(der);
 604         bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x01), temp);
 605         temp = new DerOutputStream();
 606         temp.write(DerValue.tag_Sequence, bytes);
 607         return temp.toByteArray();
 608     }
 609 
 610 
 611     /**
 612      * Checks if two <code>PrincipalName</code> objects have identical values in their corresponding data fields.
 613      *
 614      * @param pname the other <code>PrincipalName</code> object.
 615      * @return true if two have identical values, otherwise, return false.
 616      */
 617     // It is used in <code>sun.security.krb5.internal.ccache</code> package.
 618     public boolean match(PrincipalName pname) {
 619         boolean matched = true;
 620         //name type is just a hint, no two names can be the same ignoring name type.
 621         // if (this.nameType != pname.nameType) {
 622         //      matched = false;
 623         // }
 624         if ((this.nameRealm != null) && (pname.nameRealm != null)) {
 625             if (!(this.nameRealm.toString().equalsIgnoreCase(pname.nameRealm.toString()))) {
 626                 matched = false;
 627             }
 628         }
 629         if (this.nameStrings.length != pname.nameStrings.length) {
 630             matched = false;
 631         } else {
 632             for (int i = 0; i < this.nameStrings.length; i++) {
 633                 if (!(this.nameStrings[i].equalsIgnoreCase(pname.nameStrings[i]))) {
 634                     matched = false;
 635                 }
 636             }
 637         }
 638         return matched;
 639     }
 640 
 641     /**
 642      * Writes data field values of <code>PrincipalName</code> in FCC format to an output stream.
 643      *
 644      * @param cos a <code>CCacheOutputStream</code> for writing data.
 645      * @exception IOException if an I/O exception occurs.
 646      * @see sun.security.krb5.internal.ccache.CCacheOutputStream
 647      */
 648     public void writePrincipal(CCacheOutputStream cos) throws IOException {
 649         cos.write32(nameType);
 650         cos.write32(nameStrings.length);
 651         byte[] realmBytes = null;
 652         realmBytes = nameRealm.toString().getBytes();
 653         cos.write32(realmBytes.length);
 654         cos.write(realmBytes, 0, realmBytes.length);
 655         byte[] bytes = null;
 656         for (int i = 0; i < nameStrings.length; i++) {
 657             bytes = nameStrings[i].getBytes();
 658             cos.write32(bytes.length);
 659             cos.write(bytes, 0, bytes.length);
 660         }
 661     }
 662 
 663     /**
 664      * Returns the instance component of a name.
 665      * In a multi-component name such as a KRB_NT_SRV_INST
 666      * name, the second component is returned.
 667      * Null is returned if there are not two or more
 668      * components in the name.
 669      *
 670      * @return instance component of a multi-component name.
 671      */
 672     public String getInstanceComponent()
 673     {
 674         if (nameStrings != null && nameStrings.length >= 2)
 675             {
 676                 return new String(nameStrings[1]);
 677             }
 678 
 679         return null;
 680     }
 681 
 682     static String mapHostToRealm(String name) {
 683         String result = null;
 684         try {
 685             String subname = null;
 686             Config c = Config.getInstance();
 687             if ((result = c.get("domain_realm", name)) != null)
 688                 return result;
 689             else {
 690                 for (int i = 1; i < name.length(); i++) {
 691                     if ((name.charAt(i) == '.') && (i != name.length() - 1)) { //mapping could be .ibm.com = AUSTIN.IBM.COM
 692                         subname = name.substring(i);
 693                         result = c.get("domain_realm", subname);
 694                         if (result != null) {
 695                             break;
 696                         }
 697                         else {
 698                             subname = name.substring(i + 1);      //or mapping could be ibm.com = AUSTIN.IBM.COM
 699                             result = c.get("domain_realm", subname);
 700                             if (result != null) {
 701                                 break;
 702                             }
 703                         }
 704                     }
 705                 }
 706             }
 707         } catch (KrbException e) {
 708         }
 709         return result;
 710     }
 711 
 712     public boolean isRealmDeduced() {
 713         return realmDeduced;
 714     }
 715 }