Path: blob/master/src/java.security.jgss/share/classes/sun/security/krb5/Realm.java
41159 views
/*1* Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425/*26*27* (C) Copyright IBM Corp. 1999 All Rights Reserved.28* Copyright 1997 The Open Group Research Institute. All rights reserved.29*/3031package sun.security.krb5;3233import sun.security.action.GetBooleanAction;34import sun.security.krb5.internal.Krb5;35import sun.security.util.*;36import java.io.IOException;37import java.util.*;3839import sun.security.krb5.internal.util.KerberosString;4041/**42* Implements the ASN.1 Realm type.43*44* {@code Realm ::= GeneralString}45*46* This class is immutable.47*/48public class Realm implements Cloneable {4950public static final boolean AUTODEDUCEREALM = GetBooleanAction51.privilegedGetProperty("sun.security.krb5.autodeducerealm");5253private final String realm; // not null nor empty5455public Realm(String name) throws RealmException {56realm = parseRealm(name);57}5859public static Realm getDefault() throws RealmException {60try {61return new Realm(Config.getInstance().getDefaultRealm());62} catch (RealmException re) {63throw re;64} catch (KrbException ke) {65throw new RealmException(ke);66}67}6869// Immutable class, no need to clone70public Object clone() {71return this;72}7374public boolean equals(Object obj) {75if (this == obj) {76return true;77}7879if (!(obj instanceof Realm)) {80return false;81}8283Realm that = (Realm)obj;84return this.realm.equals(that.realm);85}8687public int hashCode() {88return realm.hashCode();89}9091/**92* Constructs a Realm object.93* @param encoding a Der-encoded data.94* @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data.95* @exception IOException if an I/O error occurs while reading encoded data.96* @exception RealmException if an error occurs while parsing a Realm object.97*/98public Realm(DerValue encoding)99throws Asn1Exception, RealmException, IOException {100if (encoding == null) {101throw new IllegalArgumentException("encoding can not be null");102}103realm = new KerberosString(encoding).toString();104if (realm == null || realm.length() == 0)105throw new RealmException(Krb5.REALM_NULL);106if (!isValidRealmString(realm))107throw new RealmException(Krb5.REALM_ILLCHAR);108}109110public String toString() {111return realm;112}113114// Extract realm from a string like dummy@REALM115public static String parseRealmAtSeparator(String name)116throws RealmException {117if (name == null) {118throw new IllegalArgumentException119("null input name is not allowed");120}121String temp = new String(name);122String result = null;123int i = 0;124while (i < temp.length()) {125if (temp.charAt(i) == PrincipalName.NAME_REALM_SEPARATOR) {126if (i == 0 || temp.charAt(i - 1) != '\\') {127if (i + 1 < temp.length()) {128result = temp.substring(i + 1, temp.length());129} else {130throw new IllegalArgumentException131("empty realm part not allowed");132}133break;134}135}136i++;137}138if (result != null) {139if (result.length() == 0)140throw new RealmException(Krb5.REALM_NULL);141if (!isValidRealmString(result))142throw new RealmException(Krb5.REALM_ILLCHAR);143}144return result;145}146147public static String parseRealmComponent(String name) {148if (name == null) {149throw new IllegalArgumentException150("null input name is not allowed");151}152String temp = new String(name);153String result = null;154int i = 0;155while (i < temp.length()) {156if (temp.charAt(i) == PrincipalName.REALM_COMPONENT_SEPARATOR) {157if (i == 0 || temp.charAt(i - 1) != '\\') {158if (i + 1 < temp.length())159result = temp.substring(i + 1, temp.length());160break;161}162}163i++;164}165return result;166}167168protected static String parseRealm(String name) throws RealmException {169String result = parseRealmAtSeparator(name);170if (result == null)171result = name;172if (result == null || result.length() == 0)173throw new RealmException(Krb5.REALM_NULL);174if (!isValidRealmString(result))175throw new RealmException(Krb5.REALM_ILLCHAR);176return result;177}178179// This is protected because the definition of a realm180// string is fixed181protected static boolean isValidRealmString(String name) {182if (name == null)183return false;184if (name.length() == 0)185return false;186for (int i = 0; i < name.length(); i++) {187if (name.charAt(i) == '/' ||188name.charAt(i) == '\0') {189return false;190}191}192return true;193}194195/**196* Encodes a Realm object.197* @return the byte array of encoded KrbCredInfo object.198* @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data.199* @exception IOException if an I/O error occurs while reading encoded data.200*201*/202public byte[] asn1Encode() throws Asn1Exception, IOException {203DerOutputStream out = new DerOutputStream();204out.putDerValue(new KerberosString(this.realm).toDerValue());205return out.toByteArray();206}207208209/**210* Parse (unmarshal) a realm from a DER input stream. This form211* parsing might be used when expanding a value which is part of212* a constructed sequence and uses explicitly tagged type.213*214* @exception Asn1Exception on error.215* @param data the Der input stream value, which contains one or more marshaled value.216* @param explicitTag tag number.217* @param optional indicate if this data field is optional218* @return an instance of Realm.219*220*/221public static Realm parse(DerInputStream data, byte explicitTag, boolean optional)222throws Asn1Exception, IOException, RealmException {223if ((optional) && (((byte)data.peekByte() & (byte)0x1F) != explicitTag)) {224return null;225}226DerValue der = data.getDerValue();227if (explicitTag != (der.getTag() & (byte)0x1F)) {228throw new Asn1Exception(Krb5.ASN1_BAD_ID);229} else {230DerValue subDer = der.getData().getDerValue();231return new Realm(subDer);232}233}234235/**236* Returns an array of realms that may be traversed to obtain237* a TGT from the initiating realm cRealm to the target realm238* sRealm.239* <br>240* This method would read [capaths] to create a path, or generate a241* hierarchical path if [capaths] does not contain a sub-stanza for cRealm242* or the sub-stanza does not contain a tag for sRealm.243* <br>244* The returned list would never be null, and it always contains245* cRealm as the head entry. sRealm is not included as the tail.246*247* @param cRealm the initiating realm, not null248* @param sRealm the target realm, not null, not equals to cRealm249* @return array of realms including at least cRealm as the first250* element251*/252public static String[] getRealmsList(String cRealm, String sRealm) {253try {254// Try [capaths]255return parseCapaths(cRealm, sRealm);256} catch (KrbException ke) {257// Now assume the realms are organized hierarchically.258return parseHierarchy(cRealm, sRealm);259}260}261262/**263* Parses the [capaths] stanza of the configuration file for a264* list of realms to traverse to obtain credentials from the265* initiating realm cRealm to the target realm sRealm.266*267* For a given client realm C there is a tag C in [capaths] whose268* subtag S has a value which is a (possibly partial) path from C269* to S. When the path is partial, it contains only the tail of the270* full path. Values of other subtags will be used to build the full271* path. The value "." means a direct path from C to S. If realm S272* does not appear as a subtag, there is no path defined here.273*274* The implementation ignores all values which equals to C or S, or275* a "." in multiple values, or any duplicated realm names.276*277* When a path value has more than two realms, they can be specified278* with multiple key-value pairs each having a single value, but the279* order must not change.280*281* For example:282*283* [capaths]284* TIVOLI.COM = {285* IBM.COM = IBM_LDAPCENTRAL.COM MOONLITE.ORG286* IBM_LDAPCENTRAL.COM = LDAPCENTRAL.NET287* LDAPCENTRAL.NET = .288* }289*290* TIVOLI.COM has a direct path to LDAPCENTRAL.NET, which has a direct291* path to IBM_LDAPCENTRAL.COM. It also has a partial path to IBM.COM292* being "IBM_LDAPCENTRAL.COM MOONLITE.ORG". Merging these info together,293* a full path from TIVOLI.COM to IBM.COM will be294*295* TIVOLI.COM -> LDAPCENTRAL.NET -> IBM_LDAPCENTRAL.COM296* -> IBM_LDAPCENTRAL.COM -> MOONLITE.ORG297*298* Please note the sRealm IBM.COM does not appear in the path.299*300* @param cRealm the initiating realm301* @param sRealm the target realm, not the same as cRealm302* @return array of realms including at least cRealm as the first303* element304* @throws KrbException if the config does not contain a sub-stanza305* for cRealm in [capaths] or the sub-stanza does not contain306* sRealm as a tag307*/308private static String[] parseCapaths(String cRealm, String sRealm)309throws KrbException {310311// This line could throw a KrbException312Config cfg = Config.getInstance();313314if (!cfg.exists("capaths", cRealm, sRealm)) {315throw new KrbException("No conf");316}317318LinkedList<String> path = new LinkedList<>();319320String head = sRealm;321while (true) {322String value = cfg.getAll("capaths", cRealm, head);323if (value == null) {324break;325}326String[] more = value.split("\\s+");327boolean changed = false;328for (int i=more.length-1; i>=0; i--) {329if (path.contains(more[i])330|| more[i].equals(".")331|| more[i].equals(cRealm)332|| more[i].equals(sRealm)333|| more[i].equals(head)) {334// Ignore invalid values335continue;336}337changed = true;338path.addFirst(more[i]);339}340if (!changed) break;341head = path.getFirst();342}343path.addFirst(cRealm);344return path.toArray(new String[path.size()]);345}346347/**348* Build a list of realm that can be traversed349* to obtain credentials from the initiating realm cRealm350* for a service in the target realm sRealm.351* @param cRealm the initiating realm352* @param sRealm the target realm, not the same as cRealm353* @return array of realms including cRealm as the first element354*/355private static String[] parseHierarchy(String cRealm, String sRealm) {356357String[] cComponents = cRealm.split("\\.");358String[] sComponents = sRealm.split("\\.");359360int cPos = cComponents.length;361int sPos = sComponents.length;362363boolean hasCommon = false;364for (sPos--, cPos--; sPos >=0 && cPos >= 0 &&365sComponents[sPos].equals(cComponents[cPos]);366sPos--, cPos--) {367hasCommon = true;368}369370// For those with common components:371// length pos372// SITES1.SALES.EXAMPLE.COM 4 1373// EVERYWHERE.EXAMPLE.COM 3 0374375// For those without common components:376// length pos377// DEVEL.EXAMPLE.COM 3 2378// PROD.EXAMPLE.ORG 3 2379380LinkedList<String> path = new LinkedList<>();381382// Un-common ones for client side383for (int i=0; i<=cPos; i++) {384path.addLast(subStringFrom(cComponents, i));385}386387// Common one388if (hasCommon) {389path.addLast(subStringFrom(cComponents, cPos+1));390}391392// Un-common ones for server side393for (int i=sPos; i>=0; i--) {394path.addLast(subStringFrom(sComponents, i));395}396397// Remove sRealm from path. Note that it might be added at last loop398// or as a common component, if sRealm is a parent of cRealm399path.removeLast();400401return path.toArray(new String[path.size()]);402}403404/**405* Creates a realm name using components from the given position.406* For example, subStringFrom({"A", "B", "C"}, 1) is "B.C".407*/408private static String subStringFrom(String[] components, int from) {409StringBuilder sb = new StringBuilder();410for (int i=from; i<components.length; i++) {411if (sb.length() != 0) sb.append('.');412sb.append(components[i]);413}414return sb.toString();415}416}417418419