jdk/src/share/classes/sun/security/krb5/Realm.java
changeset 2 90ce3da70b43
child 2064 d690c8a2acea
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/security/krb5/Realm.java	Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,673 @@
+/*
+ * Portions Copyright 2000-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/*
+ *
+ *  (C) Copyright IBM Corp. 1999 All Rights Reserved.
+ *  Copyright 1997 The Open Group Research Institute.  All rights reserved.
+ */
+
+package sun.security.krb5;
+
+import sun.security.krb5.Config;
+import sun.security.krb5.PrincipalName;
+import sun.security.krb5.KrbException;
+import sun.security.krb5.Asn1Exception;
+import sun.security.krb5.RealmException;
+import sun.security.krb5.internal.Krb5;
+import sun.security.util.*;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.StringTokenizer;
+import java.util.Vector;
+import java.util.Stack;
+import java.util.EmptyStackException;
+
+/**
+ * Implements the ASN.1 Realm type.
+ *
+ * <xmp>
+ * Realm ::= GeneralString
+ * </xmp>
+ */
+public class Realm implements Cloneable {
+    private String realm;
+    private static boolean DEBUG = Krb5.DEBUG;
+
+    private Realm() {
+    }
+
+    public Realm(String name) throws RealmException {
+        realm = parseRealm(name);
+    }
+
+    public Object clone() {
+        Realm new_realm = new Realm();
+        if (realm != null) {
+            new_realm.realm = new String(realm);
+        }
+        return new_realm;
+    }
+
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (!(obj instanceof Realm)) {
+            return false;
+        }
+
+        Realm that = (Realm)obj;
+        if (this.realm != null && that.realm != null ) {
+            return this.realm.equals(that.realm);
+        } else {
+            return (this.realm == null && that.realm == null);
+        }
+    }
+
+    public int hashCode() {
+        int result = 17 ;
+
+        if( realm != null ) {
+            result = 37 * result + realm.hashCode();
+        }
+
+        return result;
+    }
+
+    /**
+     * Constructs a Realm object.
+     * @param encoding a Der-encoded data.
+     * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data.
+     * @exception IOException if an I/O error occurs while reading encoded data.
+     * @exception RealmException if an error occurs while parsing a Realm object.
+     */
+    public Realm(DerValue encoding)
+        throws Asn1Exception, RealmException, IOException {
+        if (encoding == null) {
+            throw new IllegalArgumentException("encoding can not be null");
+        }
+        realm = encoding.getGeneralString();
+        if (realm == null || realm.length() == 0)
+            throw new RealmException(Krb5.REALM_NULL);
+        if (!isValidRealmString(realm))
+            throw new RealmException(Krb5.REALM_ILLCHAR);
+    }
+
+    public String toString() {
+        return realm;
+    }
+
+    public static String parseRealmAtSeparator(String name)
+        throws RealmException {
+        if (name == null) {
+            throw new IllegalArgumentException
+                ("null input name is not allowed");
+        }
+        String temp = new String(name);
+        String result = null;
+        int i = 0;
+        while (i < temp.length()) {
+            if (temp.charAt(i) == PrincipalName.NAME_REALM_SEPARATOR) {
+                if (i == 0 || temp.charAt(i - 1) != '\\') {
+                    if (i + 1 < temp.length())
+                        result = temp.substring(i + 1, temp.length());
+                    break;
+                }
+            }
+            i++;
+        }
+        if (result != null) {
+            if (result.length() == 0)
+                throw new RealmException(Krb5.REALM_NULL);
+            if (!isValidRealmString(result))
+                throw new RealmException(Krb5.REALM_ILLCHAR);
+        }
+        return result;
+    }
+
+    public static String parseRealmComponent(String name) {
+        if (name == null) {
+            throw new IllegalArgumentException
+                ("null input name is not allowed");
+        }
+        String temp = new String(name);
+        String result = null;
+        int i = 0;
+        while (i < temp.length()) {
+            if (temp.charAt(i) == PrincipalName.REALM_COMPONENT_SEPARATOR) {
+                if (i == 0 || temp.charAt(i - 1) != '\\') {
+                    if (i + 1 < temp.length())
+                        result = temp.substring(i + 1, temp.length());
+                    break;
+                }
+            }
+            i++;
+        }
+        return result;
+    }
+
+    protected static String parseRealm(String name) throws RealmException {
+        String result = parseRealmAtSeparator(name);
+        if (result == null)
+            result = name;
+        if (result == null || result.length() == 0)
+            throw new RealmException(Krb5.REALM_NULL);
+        if (!isValidRealmString(result))
+            throw new RealmException(Krb5.REALM_ILLCHAR);
+        return result;
+    }
+
+    // This is protected because the definition of a realm
+    // string is fixed
+    protected static boolean isValidRealmString(String name) {
+        if (name == null)
+            return false;
+        if (name.length() == 0)
+            return false;
+        for (int i = 0; i < name.length(); i++) {
+            if (name.charAt(i) == '/' ||
+                name.charAt(i) == ':' ||
+                name.charAt(i) == '\0') {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Encodes a Realm object.
+     * @return the byte array of encoded KrbCredInfo object.
+     * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data.
+     * @exception IOException if an I/O error occurs while reading encoded data.
+     *
+     */
+    public byte[] asn1Encode() throws Asn1Exception, IOException {
+        DerOutputStream out = new DerOutputStream();
+        out.putGeneralString(this.realm);
+        return out.toByteArray();
+    }
+
+
+    /**
+     * Parse (unmarshal) a realm from a DER input stream.  This form
+     * parsing might be used when expanding a value which is part of
+     * a constructed sequence and uses explicitly tagged type.
+     *
+     * @exception Asn1Exception on error.
+     * @param data the Der input stream value, which contains one or more marshaled value.
+     * @param explicitTag tag number.
+     * @param optional indicate if this data field is optional
+     * @return an instance of Realm.
+     *
+     */
+    public static Realm parse(DerInputStream data, byte explicitTag, boolean optional) throws Asn1Exception, IOException, RealmException {
+        if ((optional) && (((byte)data.peekByte() & (byte)0x1F) != explicitTag)) {
+            return null;
+        }
+        DerValue der = data.getDerValue();
+        if (explicitTag != (der.getTag() & (byte)0x1F))  {
+            throw new Asn1Exception(Krb5.ASN1_BAD_ID);
+        } else {
+            DerValue subDer = der.getData().getDerValue();
+            return new Realm(subDer);
+        }
+    }
+
+    /*
+     * First leg of realms parsing. Used by getRealmsList.
+     */
+    private static String[] doInitialParse(String cRealm, String sRealm)
+        throws KrbException {
+            if (cRealm == null || sRealm == null){
+                throw new KrbException(Krb5.API_INVALID_ARG);
+            }
+            if (DEBUG) {
+                System.out.println(">>> Realm doInitialParse: cRealm=["
+                                   + cRealm + "], sRealm=[" +sRealm + "]");
+            }
+            if (cRealm.equals(sRealm)) {
+                String[] retList = null;
+                retList = new String[1];
+                retList[0] = new String(cRealm);
+
+                if (DEBUG) {
+                    System.out.println(">>> Realm doInitialParse: "
+                                       + retList[0]);
+                }
+                return retList;
+            }
+            return null;
+        }
+
+    /**
+     * Returns an array of realms that may be traversed to obtain
+     * a TGT from the initiating realm cRealm to the target realm
+     * sRealm.
+     * <br>
+     * There may be an arbitrary number of intermediate realms
+     * between cRealm and sRealm. The realms may be organized
+     * organized hierarchically, or the paths between them may be
+     * specified in the [capaths] stanza of the caller's
+     * Kerberos configuration file. The configuration file is consulted
+     * first. Then a hirarchical organization is assumed if no realms
+     * are found in the configuration file.
+     * <br>
+     * The returned list, if not null, contains cRealm as the first
+     * entry. sRealm is not included unless it is mistakenly listed
+     * in the configuration file as an intermediary realm.
+     *
+     * @param cRealm the initiating realm
+     * @param sRealm the target realm
+     * @returns array of realms
+     * @thows KrbException
+     */
+    public static String[] getRealmsList(String cRealm, String sRealm)
+        throws KrbException {
+            String[] retList = doInitialParse(cRealm, sRealm);
+            if (retList != null && retList.length != 0) {
+                return retList;
+            }
+            /*
+             * Try [capaths].
+             */
+            retList = parseCapaths(cRealm, sRealm);
+            if (retList != null && retList.length != 0) {
+                return retList;
+            }
+            /*
+             * Now assume the realms are organized hierarchically.
+             */
+            retList = parseHierarchy(cRealm, sRealm);
+            return retList;
+        }
+
+    /**
+     * Parses the [capaths] stanza of the configuration file
+     * for a list of realms to traverse
+     * to obtain credentials from the initiating realm cRealm to
+     * the target realm sRealm.
+     * @param cRealm the initiating realm
+     * @param sRealm the target realm
+     * @returns array of realms
+     * @ throws KrbException
+     */
+
+    /*
+     * parseCapaths works for a capaths organized such that
+     * for a given client realm C there is a tag C that
+     * contains subtags Ci ... Cn that completely define intermediate
+     * realms from C to target T. For example:
+     *
+     * [capaths]
+     *    TIVOLI.COM = {
+     *        IBM.COM = IBM_LDAPCENTRAL.COM MOONLITE.ORG
+     *        IBM_LDAPCENTRAL.COM = LDAPCENTRAL.NET
+     *        LDAPCENTRAL.NET = .
+     *    }
+     *
+     * The tag TIVOLI.COM contains subtags IBM.COM, IBM_LDAPCENTRAL.COM
+     * and LDAPCENTRAL.NET that completely define the path from TIVOLI.COM
+     * to IBM.COM (TIVOLI.COM->LADAPCENTRAL.NET->IBM_LDAPCENTRAL.COM->IBM
+     * or TIVOLI.COM->MOONLITE.ORG->IBM.COM).
+     *
+     * A direct path is assumed for an intermediary whose entry is not
+     * "closed" by a "." In the above example, TIVOLI.COM is assumed
+     * to have a direct path to MOONLITE.ORG and MOONLITE.COM
+     * in turn to IBM.COM.
+     */
+
+    private static String[] parseCapaths(String cRealm, String sRealm) throws KrbException {
+        String[] retList = null;
+
+        Config cfg = null;
+        try {
+            cfg = Config.getInstance();
+        } catch (Exception exc) {
+            if (DEBUG) {
+                System.out.println ("Configuration information can not be " +
+                                    "obtained " + exc.getMessage());
+            }
+            return null;
+        }
+
+        String intermediaries = cfg.getDefault(sRealm, cRealm);
+
+        if (intermediaries == null) {
+            if (DEBUG) {
+                System.out.println(">>> Realm parseCapaths: no cfg entry");
+            }
+            return null;
+        }
+
+        String tempTarget = null, tempRealm = null;
+        StringTokenizer strTok = null;
+        Stack<String> iStack = new Stack<String> ();
+
+        /*
+         * I don't expect any more than a handful of intermediaries.
+         */
+        Vector<String> tempList = new Vector<String> (8, 8);
+
+        /*
+         * The initiator at first location.
+         */
+        tempList.add(cRealm);
+
+        int count = 0; // For debug only
+        if (DEBUG) {
+            tempTarget = sRealm;
+        }
+
+        do {
+            if (DEBUG) {
+                count++;
+                System.out.println(">>> Realm parseCapaths: loop " +
+                                   count + ": target=" + tempTarget);
+            }
+
+            if (intermediaries != null &&
+                !intermediaries.equals(PrincipalName.REALM_COMPONENT_SEPARATOR_STR))
+            {
+                if (DEBUG) {
+                    System.out.println(">>> Realm parseCapaths: loop " +
+                                       count + ": intermediaries=[" +
+                                       intermediaries + "]");
+                }
+
+                /*
+                 * We have one or more space-separated intermediary realms.
+                 * Stack them.
+                 */
+                strTok = new StringTokenizer(intermediaries, " ");
+                while (strTok.hasMoreTokens())
+                {
+                    tempRealm = strTok.nextToken();
+                    if (!tempRealm.equals(PrincipalName.
+                                          REALM_COMPONENT_SEPARATOR_STR) &&
+                        !iStack.contains(tempRealm)) {
+                        iStack.push(tempRealm);
+                        if (DEBUG) {
+                            System.out.println(">>> Realm parseCapaths: loop " +
+                                               count +
+                                               ": pushed realm on to stack: " +
+                                               tempRealm);
+                        }
+                    } else if (DEBUG) {
+                        System.out.println(">>> Realm parseCapaths: loop " +
+
+                                           count +
+                                           ": ignoring realm: [" +
+                                           tempRealm + "]");
+                    }
+                }
+            } else if (DEBUG) {
+                System.out.println(">>> Realm parseCapaths: loop " +
+                                   count +
+                                   ": no intermediaries");
+            }
+
+            /*
+             * Get next intermediary realm from the stack
+             */
+
+            try {
+                tempTarget = iStack.pop();
+            } catch (EmptyStackException exc) {
+                tempTarget = null;
+            }
+
+            if (tempTarget == null) {
+                /*
+                 * No more intermediaries. We're done.
+                 */
+                break;
+            }
+
+            tempList.add(tempTarget);
+
+            if (DEBUG) {
+                System.out.println(">>> Realm parseCapaths: loop " + count +
+                                   ": added intermediary to list: " +
+                                   tempTarget);
+            }
+
+            intermediaries = cfg.getDefault(tempTarget, cRealm);
+
+        } while (true);
+
+        retList = new String[tempList.size()];
+        try {
+            retList = tempList.toArray(retList);
+        } catch (ArrayStoreException exc) {
+            retList = null;
+        }
+
+        if (DEBUG && retList != null) {
+            for (int i = 0; i < retList.length; i++) {
+                System.out.println(">>> Realm parseCapaths [" + i +
+                                   "]=" + retList[i]);
+            }
+        }
+
+        return retList;
+    }
+
+    /**
+     * Build a list of realm that can be traversed
+     * to obtain credentials from the initiating realm cRealm
+     * for a service in the target realm sRealm.
+     * @param cRealm the initiating realm
+     * @param sRealm the target realm
+     * @returns array of realms
+     * @throws KrbException
+     */
+    private static String[] parseHierarchy(String cRealm, String sRealm)
+        throws KrbException
+    {
+        String[] retList = null;
+
+        // Parse the components and determine common part, if any.
+
+        String[] cComponents = null;
+        String[] sComponents = null;
+
+        StringTokenizer strTok =
+        new StringTokenizer(cRealm,
+                            PrincipalName.REALM_COMPONENT_SEPARATOR_STR);
+
+        // Parse cRealm
+
+        int cCount = strTok.countTokens();
+        cComponents = new String[cCount];
+
+        for (cCount = 0; strTok.hasMoreTokens(); cCount++) {
+            cComponents[cCount] = strTok.nextToken();
+        }
+
+        if (DEBUG) {
+            System.out.println(">>> Realm parseHierarchy: cRealm has " +
+                               cCount + " components:");
+            int j = 0;
+            while (j < cCount) {
+                System.out.println(">>> Realm parseHierarchy: " +
+                                   "cComponents["+j+"]=" + cComponents[j++]);
+            }
+        }
+
+        // Parse sRealm
+
+        strTok = new StringTokenizer(sRealm,
+                                     PrincipalName.REALM_COMPONENT_SEPARATOR_STR);
+
+        int sCount = strTok.countTokens();
+        sComponents = new String[sCount];
+
+        for (sCount = 0; strTok.hasMoreTokens(); sCount++) {
+            sComponents[sCount] = strTok.nextToken();
+        }
+
+        if (DEBUG) {
+            System.out.println(">>> Realm parseHierarchy: sRealm has " +
+                               sCount + " components:");
+            int j = 0;
+            while (j < sCount) {
+                System.out.println(">>> Realm parseHierarchy: sComponents["+j+
+                                   "]=" + sComponents[j++]);
+            }
+        }
+
+        // Determine common components, if any.
+
+        int commonComponents = 0;
+
+        //while (sCount > 0 && cCount > 0 &&
+        //          sComponents[--sCount].equals(cComponents[--cCount]))
+
+        for (sCount--, cCount--; sCount >=0 && cCount >= 0 &&
+                 sComponents[sCount].equals(cComponents[cCount]);
+             sCount--, cCount--) {
+            commonComponents++;
+        }
+
+        int cCommonStart = -1;
+        int sCommonStart = -1;
+
+        int links = 0;
+
+        if (commonComponents > 0) {
+            sCommonStart = sCount+1;
+            cCommonStart = cCount+1;
+
+            // components from common to ancestors
+            links += sCommonStart;
+            links += cCommonStart;
+        } else {
+            links++;
+        }
+
+        if (DEBUG) {
+            if (commonComponents > 0) {
+                System.out.println(">>> Realm parseHierarchy: " +
+                                   commonComponents + " common component" +
+                                   (commonComponents > 1 ? "s" : " "));
+
+                System.out.println(">>> Realm parseHierarchy: common part "
+                                   +
+                                   "in cRealm (starts at index " +
+                                   cCommonStart + ")");
+                System.out.println(">>> Realm parseHierarchy: common part in sRealm (starts at index " +
+                                   sCommonStart + ")");
+
+
+                String commonPart = substring(cRealm, cCommonStart);
+                System.out.println(">>> Realm parseHierarchy: common part in cRealm=" +
+                                   commonPart);
+
+                commonPart = substring(sRealm, sCommonStart);
+                System.out.println(">>> Realm parseHierarchy: common part in sRealm=" +
+                                   commonPart);
+
+            } else
+            System.out.println(">>> Realm parseHierarchy: no common part");
+        }
+
+        if (DEBUG) {
+            System.out.println(">>> Realm parseHierarchy: total links=" + links);
+        }
+
+        retList = new String[links];
+
+        retList[0] = new String(cRealm);
+
+        if (DEBUG) {
+            System.out.println(">>> Realm parseHierarchy A: retList[0]=" +
+                               retList[0]);
+        }
+
+        // For an initiator realm A.B.C.D.COM,
+        // build a list krbtgt/B.C.D.COM@A.B.C.D.COM up to the common part,
+        // ie the issuer realm is the immediate descendant
+        // of the target realm.
+
+        String cTemp = null, sTemp = null;
+        int i;
+        for (i = 1, cCount = 0; i < links && cCount < cCommonStart; cCount++) {
+            sTemp = substring(cRealm, cCount+1);
+            //cTemp = substring(cRealm, cCount);
+            retList[i++] = new String(sTemp);
+
+            if (DEBUG) {
+                System.out.println(">>> Realm parseHierarchy B: retList[" +
+                                   (i-1) +"]="+retList[i-1]);
+            }
+        }
+
+
+        for (sCount = sCommonStart; i < links && sCount - 1 > 0; sCount--) {
+            sTemp = substring(sRealm, sCount-1);
+            //cTemp = substring(sRealm, sCount);
+            retList[i++] = new String(sTemp);
+            if (DEBUG) {
+                System.out.println(">>> Realm parseHierarchy D: retList[" +
+                                   (i-1) +"]="+retList[i-1]);
+            }
+        }
+
+        return retList;
+    }
+
+    private static String substring(String realm, int componentIndex)
+    {
+        int i = 0 , j = 0, len = realm.length();
+
+        while(i < len && j != componentIndex) {
+            if (realm.charAt(i++) != PrincipalName.REALM_COMPONENT_SEPARATOR)
+                continue;
+            j++;
+        }
+
+        return realm.substring(i);
+    }
+
+    static int getRandIndex(int arraySize) {
+        return (int)(Math.random() * 16384.0) % arraySize;
+    }
+
+    static void printNames(String[] names) {
+        if (names == null || names.length == 0)
+            return;
+
+        int len = names.length;
+        int i = 0;
+        System.out.println("List length = " + len);
+        while (i < names.length) {
+            System.out.println("["+ i +"]=" + names[i]);
+            i++;
+        }
+    }
+
+}