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