Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/com/sun/jndi/ldap/Obj.java
38924 views
/*1* Copyright (c) 1999, 2020, 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*/2425package com.sun.jndi.ldap;2627import javax.naming.*;28import javax.naming.directory.*;29import javax.naming.spi.DirectoryManager;30import javax.naming.spi.DirStateFactory;3132import java.io.IOException;33import java.io.ByteArrayInputStream;34import java.io.ByteArrayOutputStream;35import java.io.ObjectInputStream;36import java.io.ObjectOutputStream;37import java.io.ObjectStreamClass;38import java.io.InputStream;3940import java.util.Hashtable;41import java.util.Vector;42import java.util.StringTokenizer;4344import sun.misc.BASE64Encoder;45import sun.misc.BASE64Decoder;4647import java.lang.reflect.Proxy;48import java.lang.reflect.Modifier;4950/**51* Class containing static methods and constants for dealing with52* encoding/decoding JNDI References and Serialized Objects53* in LDAP.54* @author Vincent Ryan55* @author Rosanna Lee56*/57final class Obj {5859private Obj () {}; // Make sure no one can create one6061// package private; used by Connection62static VersionHelper helper = VersionHelper.getVersionHelper();6364// LDAP attributes used to support Java objects.65static final String[] JAVA_ATTRIBUTES = {66"objectClass",67"javaSerializedData",68"javaClassName",69"javaFactory",70"javaCodeBase",71"javaReferenceAddress",72"javaClassNames",73"javaRemoteLocation" // Deprecated74};7576static final int OBJECT_CLASS = 0;77static final int SERIALIZED_DATA = 1;78static final int CLASSNAME = 2;79static final int FACTORY = 3;80static final int CODEBASE = 4;81static final int REF_ADDR = 5;82static final int TYPENAME = 6;83/**84* @deprecated85*/86@Deprecated87private static final int REMOTE_LOC = 7;8889// LDAP object classes to support Java objects90static final String[] JAVA_OBJECT_CLASSES = {91"javaContainer",92"javaObject",93"javaNamingReference",94"javaSerializedObject",95"javaMarshalledObject",96};9798static final String[] JAVA_OBJECT_CLASSES_LOWER = {99"javacontainer",100"javaobject",101"javanamingreference",102"javaserializedobject",103"javamarshalledobject",104};105106static final int STRUCTURAL = 0; // structural object class107static final int BASE_OBJECT = 1; // auxiliary java object class108static final int REF_OBJECT = 2; // auxiliary reference object class109static final int SER_OBJECT = 3; // auxiliary serialized object class110static final int MAR_OBJECT = 4; // auxiliary marshalled object class111112/**113* Encode an object in LDAP attributes.114* Supports binding Referenceable or Reference, Serializable,115* and DirContext.116*117* If the object supports the Referenceable interface then encode118* the reference to the object. See encodeReference() for details.119*<p>120* If the object is serializable, it is stored as follows:121* javaClassName122* value: Object.getClass();123* javaSerializedData124* value: serialized form of Object (in binary form).125* javaTypeName126* value: getTypeNames(Object.getClass());127*/128private static Attributes encodeObject(char separator,129Object obj, Attributes attrs,130Attribute objectClass, boolean cloned)131throws NamingException {132boolean structural =133(objectClass.size() == 0 ||134(objectClass.size() == 1 && objectClass.contains("top")));135136if (structural) {137objectClass.add(JAVA_OBJECT_CLASSES[STRUCTURAL]);138}139140// References141if (obj instanceof Referenceable) {142objectClass.add(JAVA_OBJECT_CLASSES[BASE_OBJECT]);143objectClass.add(JAVA_OBJECT_CLASSES[REF_OBJECT]);144if (!cloned) {145attrs = (Attributes)attrs.clone();146}147attrs.put(objectClass);148return (encodeReference(separator,149((Referenceable)obj).getReference(),150attrs, obj));151152} else if (obj instanceof Reference) {153objectClass.add(JAVA_OBJECT_CLASSES[BASE_OBJECT]);154objectClass.add(JAVA_OBJECT_CLASSES[REF_OBJECT]);155if (!cloned) {156attrs = (Attributes)attrs.clone();157}158attrs.put(objectClass);159return (encodeReference(separator, (Reference)obj, attrs, null));160161// Serializable Object162} else if (obj instanceof java.io.Serializable) {163objectClass.add(JAVA_OBJECT_CLASSES[BASE_OBJECT]);164if (!(objectClass.contains(JAVA_OBJECT_CLASSES[MAR_OBJECT]) ||165objectClass.contains(JAVA_OBJECT_CLASSES_LOWER[MAR_OBJECT]))) {166objectClass.add(JAVA_OBJECT_CLASSES[SER_OBJECT]);167}168if (!cloned) {169attrs = (Attributes)attrs.clone();170}171attrs.put(objectClass);172attrs.put(new BasicAttribute(JAVA_ATTRIBUTES[SERIALIZED_DATA],173serializeObject(obj)));174if (attrs.get(JAVA_ATTRIBUTES[CLASSNAME]) == null) {175attrs.put(JAVA_ATTRIBUTES[CLASSNAME],176obj.getClass().getName());177}178if (attrs.get(JAVA_ATTRIBUTES[TYPENAME]) == null) {179Attribute tAttr =180LdapCtxFactory.createTypeNameAttr(obj.getClass());181if (tAttr != null) {182attrs.put(tAttr);183}184}185// DirContext Object186} else if (obj instanceof DirContext) {187// do nothing188} else {189throw new IllegalArgumentException(190"can only bind Referenceable, Serializable, DirContext");191}192// System.err.println(attrs);193return attrs;194}195196/**197* Each value in javaCodebase contains a list of space-separated198* URLs. Each value is independent; we can pick any of the values199* so we just use the first one.200* @return an array of URL strings for the codebase201*/202private static String[] getCodebases(Attribute codebaseAttr) throws203NamingException {204if (codebaseAttr == null) {205return null;206} else {207StringTokenizer parser =208new StringTokenizer((String)codebaseAttr.get());209Vector<String> vec = new Vector<>(10);210while (parser.hasMoreTokens()) {211vec.addElement(parser.nextToken());212}213String[] answer = new String[vec.size()];214for (int i = 0; i < answer.length; i++) {215answer[i] = vec.elementAt(i);216}217return answer;218}219}220221/*222* Decode an object from LDAP attribute(s).223* The object may be a Reference, or a Serialized object.224*225* See encodeObject() and encodeReference() for details on formats226* expected.227*/228static Object decodeObject(Attributes attrs)229throws NamingException {230231Attribute attr;232233// Get codebase, which is used in all 3 cases.234String[] codebases = getCodebases(attrs.get(JAVA_ATTRIBUTES[CODEBASE]));235try {236if ((attr = attrs.get(JAVA_ATTRIBUTES[SERIALIZED_DATA])) != null) {237if (!VersionHelper12.isSerialDataAllowed()) {238throw new NamingException("Object deserialization is not allowed");239}240ClassLoader cl = helper.getURLClassLoader(codebases);241return deserializeObject((byte[])attr.get(), cl);242} else if ((attr = attrs.get(JAVA_ATTRIBUTES[REMOTE_LOC])) != null) {243// For backward compatibility only244return decodeRmiObject(245(String)attrs.get(JAVA_ATTRIBUTES[CLASSNAME]).get(),246(String)attr.get(), codebases);247}248249attr = attrs.get(JAVA_ATTRIBUTES[OBJECT_CLASS]);250if (attr != null &&251(attr.contains(JAVA_OBJECT_CLASSES[REF_OBJECT]) ||252attr.contains(JAVA_OBJECT_CLASSES_LOWER[REF_OBJECT]))) {253return decodeReference(attrs, codebases);254}255return null;256} catch (IOException e) {257NamingException ne = new NamingException();258ne.setRootCause(e);259throw ne;260}261}262263/**264* Convert a Reference object into several LDAP attributes.265*266* A Reference is stored as into the following attributes:267* javaClassName268* value: Reference.getClassName();269* javaFactory270* value: Reference.getFactoryClassName();271* javaCodeBase272* value: Reference.getFactoryClassLocation();273* javaReferenceAddress274* value: #0#typeA#valA275* value: #1#typeB#valB276* value: #2#typeC##[serialized RefAddr C]277* value: #3#typeD#valD278*279* where280* - the first character denotes the separator281* - the number following the first separator denotes the position282* of the RefAddr within the Reference283* - "typeA" is RefAddr.getType()284* - ## denotes that the Base64-encoded form of the non-StringRefAddr285* is to follow; otherwise the value that follows is286* StringRefAddr.getContents()287*288* The default separator is the hash character (#).289* May provide property for this in future.290*/291292private static Attributes encodeReference(char separator,293Reference ref, Attributes attrs, Object orig)294throws NamingException {295296if (ref == null)297return attrs;298299String s;300301if ((s = ref.getClassName()) != null) {302attrs.put(new BasicAttribute(JAVA_ATTRIBUTES[CLASSNAME], s));303}304305if ((s = ref.getFactoryClassName()) != null) {306attrs.put(new BasicAttribute(JAVA_ATTRIBUTES[FACTORY], s));307}308309if ((s = ref.getFactoryClassLocation()) != null) {310attrs.put(new BasicAttribute(JAVA_ATTRIBUTES[CODEBASE], s));311}312313// Get original object's types if caller has not explicitly314// specified other type names315if (orig != null && attrs.get(JAVA_ATTRIBUTES[TYPENAME]) != null) {316Attribute tAttr =317LdapCtxFactory.createTypeNameAttr(orig.getClass());318if (tAttr != null) {319attrs.put(tAttr);320}321}322323int count = ref.size();324325if (count > 0) {326327Attribute refAttr = new BasicAttribute(JAVA_ATTRIBUTES[REF_ADDR]);328RefAddr refAddr;329BASE64Encoder encoder = null;330331for (int i = 0; i < count; i++) {332refAddr = ref.get(i);333334if (refAddr instanceof StringRefAddr) {335refAttr.add(""+ separator + i +336separator + refAddr.getType() +337separator + refAddr.getContent());338} else {339if (encoder == null)340encoder = new BASE64Encoder();341342refAttr.add(""+ separator + i +343separator + refAddr.getType() +344separator + separator +345encoder.encodeBuffer(serializeObject(refAddr)));346}347}348attrs.put(refAttr);349}350return attrs;351}352353/*354* A RMI object is stored in the directory as355* javaClassName356* value: Object.getClass();357* javaRemoteLocation358* value: URL of RMI object (accessed through the RMI Registry)359* javaCodebase:360* value: URL of codebase of where to find classes for object361*362* Return the RMI Location URL itself. This will be turned into363* an RMI object when getObjectInstance() is called on it.364* %%% Ignore codebase for now. Depend on RMI registry to send code.-RL365* @deprecated For backward compatibility only366*/367private static Object decodeRmiObject(String className,368String rmiName, String[] codebases) throws NamingException {369return new Reference(className, new StringRefAddr("URL", rmiName));370}371372/*373* Restore a Reference object from several LDAP attributes374*/375private static Reference decodeReference(Attributes attrs,376String[] codebases) throws NamingException, IOException {377378Attribute attr;379String className;380String factory = null;381382if ((attr = attrs.get(JAVA_ATTRIBUTES[CLASSNAME])) != null) {383className = (String)attr.get();384} else {385throw new InvalidAttributesException(JAVA_ATTRIBUTES[CLASSNAME] +386" attribute is required");387}388389if ((attr = attrs.get(JAVA_ATTRIBUTES[FACTORY])) != null) {390factory = (String)attr.get();391}392393Reference ref = new Reference(className, factory,394(codebases != null? codebases[0] : null));395396/*397* string encoding of a RefAddr is either:398*399* #posn#<type>#<address>400* or401* #posn#<type>##<base64-encoded address>402*/403if ((attr = attrs.get(JAVA_ATTRIBUTES[REF_ADDR])) != null) {404405String val, posnStr, type;406char separator;407int start, sep, posn;408BASE64Decoder decoder = null;409410ClassLoader cl = helper.getURLClassLoader(codebases);411412/*413* Temporary Vector for decoded RefAddr addresses - used to ensure414* unordered addresses are correctly re-ordered.415*/416Vector<RefAddr> refAddrList = new Vector<>();417refAddrList.setSize(attr.size());418419for (NamingEnumeration<?> vals = attr.getAll(); vals.hasMore(); ) {420421val = (String)vals.next();422423if (val.length() == 0) {424throw new InvalidAttributeValueException(425"malformed " + JAVA_ATTRIBUTES[REF_ADDR] + " attribute - "+426"empty attribute value");427}428// first character denotes encoding separator429separator = val.charAt(0);430start = 1; // skip over separator431432// extract position within Reference433if ((sep = val.indexOf(separator, start)) < 0) {434throw new InvalidAttributeValueException(435"malformed " + JAVA_ATTRIBUTES[REF_ADDR] + " attribute - " +436"separator '" + separator + "'" + "not found");437}438if ((posnStr = val.substring(start, sep)) == null) {439throw new InvalidAttributeValueException(440"malformed " + JAVA_ATTRIBUTES[REF_ADDR] + " attribute - " +441"empty RefAddr position");442}443try {444posn = Integer.parseInt(posnStr);445} catch (NumberFormatException nfe) {446throw new InvalidAttributeValueException(447"malformed " + JAVA_ATTRIBUTES[REF_ADDR] + " attribute - " +448"RefAddr position not an integer");449}450start = sep + 1; // skip over position and trailing separator451452// extract type453if ((sep = val.indexOf(separator, start)) < 0) {454throw new InvalidAttributeValueException(455"malformed " + JAVA_ATTRIBUTES[REF_ADDR] + " attribute - " +456"RefAddr type not found");457}458if ((type = val.substring(start, sep)) == null) {459throw new InvalidAttributeValueException(460"malformed " + JAVA_ATTRIBUTES[REF_ADDR] + " attribute - " +461"empty RefAddr type");462}463start = sep + 1; // skip over type and trailing separator464465// extract content466if (start == val.length()) {467// Empty content468refAddrList.setElementAt(new StringRefAddr(type, null), posn);469} else if (val.charAt(start) == separator) {470// Double separators indicate a non-StringRefAddr471// Content is a Base64-encoded serialized RefAddr472473++start; // skip over consecutive separator474// %%% RL: exception if empty after double separator475476if (decoder == null)477decoder = new BASE64Decoder();478479RefAddr ra = (RefAddr)480deserializeObject(481decoder.decodeBuffer(val.substring(start)),482cl);483484refAddrList.setElementAt(ra, posn);485} else {486// Single separator indicates a StringRefAddr487refAddrList.setElementAt(new StringRefAddr(type,488val.substring(start)), posn);489}490}491492// Copy to real reference493for (int i = 0; i < refAddrList.size(); i++) {494ref.add(refAddrList.elementAt(i));495}496}497498return (ref);499}500501/*502* Serialize an object into a byte array503*/504private static byte[] serializeObject(Object obj) throws NamingException {505506try {507ByteArrayOutputStream bytes = new ByteArrayOutputStream();508try (ObjectOutputStream serial = new ObjectOutputStream(bytes)) {509serial.writeObject(obj);510}511512return (bytes.toByteArray());513514} catch (IOException e) {515NamingException ne = new NamingException();516ne.setRootCause(e);517throw ne;518}519}520521/*522* Deserializes a byte array into an object.523*/524private static Object deserializeObject(byte[] obj, ClassLoader cl)525throws NamingException {526527try {528// Create ObjectInputStream for deserialization529ByteArrayInputStream bytes = new ByteArrayInputStream(obj);530try (ObjectInputStream deserial = cl == null ?531new ObjectInputStream(bytes) :532new LoaderInputStream(bytes, cl)) {533return deserial.readObject();534} catch (ClassNotFoundException e) {535NamingException ne = new NamingException();536ne.setRootCause(e);537throw ne;538}539} catch (IOException e) {540NamingException ne = new NamingException();541ne.setRootCause(e);542throw ne;543}544}545546/**547* Returns the attributes to bind given an object and its attributes.548*/549static Attributes determineBindAttrs(550char separator, Object obj, Attributes attrs, boolean cloned,551Name name, Context ctx, Hashtable<?,?> env)552throws NamingException {553554// Call state factories to convert object and attrs555DirStateFactory.Result res =556DirectoryManager.getStateToBind(obj, name, ctx, env, attrs);557obj = res.getObject();558attrs = res.getAttributes();559560// We're only storing attributes; no further processing required561if (obj == null) {562return attrs;563}564565//if object to be bound is a DirContext extract its attributes566if ((attrs == null) && (obj instanceof DirContext)) {567cloned = true;568attrs = ((DirContext)obj).getAttributes("");569}570571boolean ocNeedsCloning = false;572573// Create "objectClass" attribute574Attribute objectClass;575if (attrs == null || attrs.size() == 0) {576attrs = new BasicAttributes(LdapClient.caseIgnore);577cloned = true;578579// No objectclasses supplied, use "top" to start580objectClass = new BasicAttribute("objectClass", "top");581582} else {583// Get existing objectclass attribute584objectClass = attrs.get("objectClass");585if (objectClass == null && !attrs.isCaseIgnored()) {586// %%% workaround587objectClass = attrs.get("objectclass");588}589590// No objectclasses supplied, use "top" to start591if (objectClass == null) {592objectClass = new BasicAttribute("objectClass", "top");593} else if (ocNeedsCloning || !cloned) {594objectClass = (Attribute)objectClass.clone();595}596}597598// convert the supplied object into LDAP attributes599attrs = encodeObject(separator, obj, attrs, objectClass, cloned);600601// System.err.println("Determined: " + attrs);602return attrs;603}604605/**606* An ObjectInputStream that uses a class loader to find classes.607*/608private static final class LoaderInputStream extends ObjectInputStream {609private ClassLoader classLoader;610611LoaderInputStream(InputStream in, ClassLoader cl) throws IOException {612super(in);613classLoader = cl;614}615616protected Class<?> resolveClass(ObjectStreamClass desc) throws617IOException, ClassNotFoundException {618try {619// %%% Should use Class.forName(desc.getName(), false, classLoader);620// except we can't because that is only available on JDK1.2621return classLoader.loadClass(desc.getName());622} catch (ClassNotFoundException e) {623return super.resolveClass(desc);624}625}626627protected Class<?> resolveProxyClass(String[] interfaces) throws628IOException, ClassNotFoundException {629ClassLoader nonPublicLoader = null;630boolean hasNonPublicInterface = false;631632// define proxy in class loader of non-public interface(s), if any633Class<?>[] classObjs = new Class<?>[interfaces.length];634for (int i = 0; i < interfaces.length; i++) {635Class<?> cl = Class.forName(interfaces[i], false, classLoader);636if ((cl.getModifiers() & Modifier.PUBLIC) == 0) {637if (hasNonPublicInterface) {638if (nonPublicLoader != cl.getClassLoader()) {639throw new IllegalAccessError(640"conflicting non-public interface class loaders");641}642} else {643nonPublicLoader = cl.getClassLoader();644hasNonPublicInterface = true;645}646}647classObjs[i] = cl;648}649try {650return Proxy.getProxyClass(hasNonPublicInterface ?651nonPublicLoader : classLoader, classObjs);652} catch (IllegalArgumentException e) {653throw new ClassNotFoundException(null, e);654}655}656657}658}659660661