Path: blob/master/src/java.naming/share/classes/com/sun/jndi/ldap/Obj.java
41161 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.Base64;41import java.util.Hashtable;42import java.util.Vector;43import java.util.StringTokenizer;4445import java.lang.reflect.Proxy;46import java.lang.reflect.Modifier;4748/**49* Class containing static methods and constants for dealing with50* encoding/decoding JNDI References and Serialized Objects51* in LDAP.52* @author Vincent Ryan53* @author Rosanna Lee54*/55final class Obj {5657private Obj () {}; // Make sure no one can create one5859// package private; used by Connection60static VersionHelper helper = VersionHelper.getVersionHelper();6162// LDAP attributes used to support Java objects.63static final String[] JAVA_ATTRIBUTES = {64"objectClass",65"javaSerializedData",66"javaClassName",67"javaFactory",68"javaCodeBase",69"javaReferenceAddress",70"javaClassNames",71"javaRemoteLocation" // Deprecated72};7374static final int OBJECT_CLASS = 0;75static final int SERIALIZED_DATA = 1;76static final int CLASSNAME = 2;77static final int FACTORY = 3;78static final int CODEBASE = 4;79static final int REF_ADDR = 5;80static final int TYPENAME = 6;81/**82* @deprecated83*/84@Deprecated85private static final int REMOTE_LOC = 7;8687// LDAP object classes to support Java objects88static final String[] JAVA_OBJECT_CLASSES = {89"javaContainer",90"javaObject",91"javaNamingReference",92"javaSerializedObject",93"javaMarshalledObject",94};9596static final String[] JAVA_OBJECT_CLASSES_LOWER = {97"javacontainer",98"javaobject",99"javanamingreference",100"javaserializedobject",101"javamarshalledobject",102};103104static final int STRUCTURAL = 0; // structural object class105static final int BASE_OBJECT = 1; // auxiliary java object class106static final int REF_OBJECT = 2; // auxiliary reference object class107static final int SER_OBJECT = 3; // auxiliary serialized object class108static final int MAR_OBJECT = 4; // auxiliary marshalled object class109110/**111* Encode an object in LDAP attributes.112* Supports binding Referenceable or Reference, Serializable,113* and DirContext.114*115* If the object supports the Referenceable interface then encode116* the reference to the object. See encodeReference() for details.117*<p>118* If the object is serializable, it is stored as follows:119* javaClassName120* value: Object.getClass();121* javaSerializedData122* value: serialized form of Object (in binary form).123* javaTypeName124* value: getTypeNames(Object.getClass());125*/126private static Attributes encodeObject(char separator,127Object obj, Attributes attrs,128Attribute objectClass, boolean cloned)129throws NamingException {130boolean structural =131(objectClass.size() == 0 ||132(objectClass.size() == 1 && objectClass.contains("top")));133134if (structural) {135objectClass.add(JAVA_OBJECT_CLASSES[STRUCTURAL]);136}137138// References139if (obj instanceof Referenceable) {140objectClass.add(JAVA_OBJECT_CLASSES[BASE_OBJECT]);141objectClass.add(JAVA_OBJECT_CLASSES[REF_OBJECT]);142if (!cloned) {143attrs = (Attributes)attrs.clone();144}145attrs.put(objectClass);146return (encodeReference(separator,147((Referenceable)obj).getReference(),148attrs, obj));149150} else if (obj instanceof Reference) {151objectClass.add(JAVA_OBJECT_CLASSES[BASE_OBJECT]);152objectClass.add(JAVA_OBJECT_CLASSES[REF_OBJECT]);153if (!cloned) {154attrs = (Attributes)attrs.clone();155}156attrs.put(objectClass);157return (encodeReference(separator, (Reference)obj, attrs, null));158159// Serializable Object160} else if (obj instanceof java.io.Serializable) {161objectClass.add(JAVA_OBJECT_CLASSES[BASE_OBJECT]);162if (!(objectClass.contains(JAVA_OBJECT_CLASSES[MAR_OBJECT]) ||163objectClass.contains(JAVA_OBJECT_CLASSES_LOWER[MAR_OBJECT]))) {164objectClass.add(JAVA_OBJECT_CLASSES[SER_OBJECT]);165}166if (!cloned) {167attrs = (Attributes)attrs.clone();168}169attrs.put(objectClass);170attrs.put(new BasicAttribute(JAVA_ATTRIBUTES[SERIALIZED_DATA],171serializeObject(obj)));172if (attrs.get(JAVA_ATTRIBUTES[CLASSNAME]) == null) {173attrs.put(JAVA_ATTRIBUTES[CLASSNAME],174obj.getClass().getName());175}176if (attrs.get(JAVA_ATTRIBUTES[TYPENAME]) == null) {177Attribute tAttr =178LdapCtxFactory.createTypeNameAttr(obj.getClass());179if (tAttr != null) {180attrs.put(tAttr);181}182}183// DirContext Object184} else if (obj instanceof DirContext) {185// do nothing186} else {187throw new IllegalArgumentException(188"can only bind Referenceable, Serializable, DirContext");189}190// System.err.println(attrs);191return attrs;192}193194/**195* Each value in javaCodebase contains a list of space-separated196* URLs. Each value is independent; we can pick any of the values197* so we just use the first one.198* @return an array of URL strings for the codebase199*/200private static String[] getCodebases(Attribute codebaseAttr) throws201NamingException {202if (codebaseAttr == null) {203return null;204} else {205StringTokenizer parser =206new StringTokenizer((String)codebaseAttr.get());207Vector<String> vec = new Vector<>(10);208while (parser.hasMoreTokens()) {209vec.addElement(parser.nextToken());210}211String[] answer = new String[vec.size()];212for (int i = 0; i < answer.length; i++) {213answer[i] = vec.elementAt(i);214}215return answer;216}217}218219/*220* Decode an object from LDAP attribute(s).221* The object may be a Reference, or a Serialized object.222*223* See encodeObject() and encodeReference() for details on formats224* expected.225*/226static Object decodeObject(Attributes attrs)227throws NamingException {228229Attribute attr;230231// Get codebase, which is used in all 3 cases.232String[] codebases = getCodebases(attrs.get(JAVA_ATTRIBUTES[CODEBASE]));233try {234if ((attr = attrs.get(JAVA_ATTRIBUTES[SERIALIZED_DATA])) != null) {235if (!VersionHelper.isSerialDataAllowed()) {236throw new NamingException("Object deserialization is not allowed");237}238ClassLoader cl = helper.getURLClassLoader(codebases);239return deserializeObject((byte[])attr.get(), cl);240} else if ((attr = attrs.get(JAVA_ATTRIBUTES[REMOTE_LOC])) != null) {241// For backward compatibility only242return decodeRmiObject(243(String)attrs.get(JAVA_ATTRIBUTES[CLASSNAME]).get(),244(String)attr.get(), codebases);245}246247attr = attrs.get(JAVA_ATTRIBUTES[OBJECT_CLASS]);248if (attr != null &&249(attr.contains(JAVA_OBJECT_CLASSES[REF_OBJECT]) ||250attr.contains(JAVA_OBJECT_CLASSES_LOWER[REF_OBJECT]))) {251return decodeReference(attrs, codebases);252}253return null;254} catch (IOException e) {255NamingException ne = new NamingException();256ne.setRootCause(e);257throw ne;258}259}260261/**262* Convert a Reference object into several LDAP attributes.263*264* A Reference is stored as into the following attributes:265* javaClassName266* value: Reference.getClassName();267* javaFactory268* value: Reference.getFactoryClassName();269* javaCodeBase270* value: Reference.getFactoryClassLocation();271* javaReferenceAddress272* value: #0#typeA#valA273* value: #1#typeB#valB274* value: #2#typeC##[serialized RefAddr C]275* value: #3#typeD#valD276*277* where278* - the first character denotes the separator279* - the number following the first separator denotes the position280* of the RefAddr within the Reference281* - "typeA" is RefAddr.getType()282* - ## denotes that the Base64-encoded form of the non-StringRefAddr283* is to follow; otherwise the value that follows is284* StringRefAddr.getContents()285*286* The default separator is the hash character (#).287* May provide property for this in future.288*/289290private static Attributes encodeReference(char separator,291Reference ref, Attributes attrs, Object orig)292throws NamingException {293294if (ref == null)295return attrs;296297String s;298299if ((s = ref.getClassName()) != null) {300attrs.put(new BasicAttribute(JAVA_ATTRIBUTES[CLASSNAME], s));301}302303if ((s = ref.getFactoryClassName()) != null) {304attrs.put(new BasicAttribute(JAVA_ATTRIBUTES[FACTORY], s));305}306307if ((s = ref.getFactoryClassLocation()) != null) {308attrs.put(new BasicAttribute(JAVA_ATTRIBUTES[CODEBASE], s));309}310311// Get original object's types if caller has not explicitly312// specified other type names313if (orig != null && attrs.get(JAVA_ATTRIBUTES[TYPENAME]) != null) {314Attribute tAttr =315LdapCtxFactory.createTypeNameAttr(orig.getClass());316if (tAttr != null) {317attrs.put(tAttr);318}319}320321int count = ref.size();322323if (count > 0) {324325Attribute refAttr = new BasicAttribute(JAVA_ATTRIBUTES[REF_ADDR]);326RefAddr refAddr;327Base64.Encoder encoder = null;328329for (int i = 0; i < count; i++) {330refAddr = ref.get(i);331332if (refAddr instanceof StringRefAddr) {333refAttr.add(""+ separator + i +334separator + refAddr.getType() +335separator + refAddr.getContent());336} else {337if (encoder == null)338encoder = Base64.getMimeEncoder();339340refAttr.add(""+ separator + i +341separator + refAddr.getType() +342separator + separator +343encoder.encodeToString(serializeObject(refAddr)));344}345}346attrs.put(refAttr);347}348return attrs;349}350351/*352* A RMI object is stored in the directory as353* javaClassName354* value: Object.getClass();355* javaRemoteLocation356* value: URL of RMI object (accessed through the RMI Registry)357* javaCodebase:358* value: URL of codebase of where to find classes for object359*360* Return the RMI Location URL itself. This will be turned into361* an RMI object when getObjectInstance() is called on it.362* %%% Ignore codebase for now. Depend on RMI registry to send code.-RL363* @deprecated For backward compatibility only364*/365private static Object decodeRmiObject(String className,366String rmiName, String[] codebases) throws NamingException {367return new Reference(className, new StringRefAddr("URL", rmiName));368}369370/*371* Restore a Reference object from several LDAP attributes372*/373private static Reference decodeReference(Attributes attrs,374String[] codebases) throws NamingException, IOException {375376Attribute attr;377String className;378String factory = null;379380if ((attr = attrs.get(JAVA_ATTRIBUTES[CLASSNAME])) != null) {381className = (String)attr.get();382} else {383throw new InvalidAttributesException(JAVA_ATTRIBUTES[CLASSNAME] +384" attribute is required");385}386387if ((attr = attrs.get(JAVA_ATTRIBUTES[FACTORY])) != null) {388factory = (String)attr.get();389}390391Reference ref = new Reference(className, factory,392(codebases != null? codebases[0] : null));393394/*395* string encoding of a RefAddr is either:396*397* #posn#<type>#<address>398* or399* #posn#<type>##<base64-encoded address>400*/401if ((attr = attrs.get(JAVA_ATTRIBUTES[REF_ADDR])) != null) {402403String val, posnStr, type;404char separator;405int start, sep, posn;406Base64.Decoder decoder = null;407408ClassLoader cl = helper.getURLClassLoader(codebases);409410/*411* Temporary Vector for decoded RefAddr addresses - used to ensure412* unordered addresses are correctly re-ordered.413*/414Vector<RefAddr> refAddrList = new Vector<>();415refAddrList.setSize(attr.size());416417for (NamingEnumeration<?> vals = attr.getAll(); vals.hasMore(); ) {418419val = (String)vals.next();420421if (val.length() == 0) {422throw new InvalidAttributeValueException(423"malformed " + JAVA_ATTRIBUTES[REF_ADDR] + " attribute - "+424"empty attribute value");425}426// first character denotes encoding separator427separator = val.charAt(0);428start = 1; // skip over separator429430// extract position within Reference431if ((sep = val.indexOf(separator, start)) < 0) {432throw new InvalidAttributeValueException(433"malformed " + JAVA_ATTRIBUTES[REF_ADDR] + " attribute - " +434"separator '" + separator + "'" + "not found");435}436if ((posnStr = val.substring(start, sep)) == null) {437throw new InvalidAttributeValueException(438"malformed " + JAVA_ATTRIBUTES[REF_ADDR] + " attribute - " +439"empty RefAddr position");440}441try {442posn = Integer.parseInt(posnStr);443} catch (NumberFormatException nfe) {444throw new InvalidAttributeValueException(445"malformed " + JAVA_ATTRIBUTES[REF_ADDR] + " attribute - " +446"RefAddr position not an integer");447}448start = sep + 1; // skip over position and trailing separator449450// extract type451if ((sep = val.indexOf(separator, start)) < 0) {452throw new InvalidAttributeValueException(453"malformed " + JAVA_ATTRIBUTES[REF_ADDR] + " attribute - " +454"RefAddr type not found");455}456if ((type = val.substring(start, sep)) == null) {457throw new InvalidAttributeValueException(458"malformed " + JAVA_ATTRIBUTES[REF_ADDR] + " attribute - " +459"empty RefAddr type");460}461start = sep + 1; // skip over type and trailing separator462463// extract content464if (start == val.length()) {465// Empty content466refAddrList.setElementAt(new StringRefAddr(type, null), posn);467} else if (val.charAt(start) == separator) {468// Double separators indicate a non-StringRefAddr469// Content is a Base64-encoded serialized RefAddr470471++start; // skip over consecutive separator472// %%% RL: exception if empty after double separator473474if (decoder == null)475decoder = Base64.getMimeDecoder();476477RefAddr ra = (RefAddr)478deserializeObject(479decoder.decode(val.substring(start).getBytes()),480cl);481482refAddrList.setElementAt(ra, posn);483} else {484// Single separator indicates a StringRefAddr485refAddrList.setElementAt(new StringRefAddr(type,486val.substring(start)), posn);487}488}489490// Copy to real reference491for (int i = 0; i < refAddrList.size(); i++) {492ref.add(refAddrList.elementAt(i));493}494}495496return (ref);497}498499/*500* Serialize an object into a byte array501*/502private static byte[] serializeObject(Object obj) throws NamingException {503504try {505ByteArrayOutputStream bytes = new ByteArrayOutputStream();506try (ObjectOutputStream serial = new ObjectOutputStream(bytes)) {507serial.writeObject(obj);508}509510return (bytes.toByteArray());511512} catch (IOException e) {513NamingException ne = new NamingException();514ne.setRootCause(e);515throw ne;516}517}518519/*520* Deserializes a byte array into an object.521*/522private static Object deserializeObject(byte[] obj, ClassLoader cl)523throws NamingException {524525try {526// Create ObjectInputStream for deserialization527ByteArrayInputStream bytes = new ByteArrayInputStream(obj);528try (ObjectInputStream deserial = cl == null ?529new ObjectInputStream(bytes) :530new LoaderInputStream(bytes, cl)) {531return deserial.readObject();532} catch (ClassNotFoundException e) {533NamingException ne = new NamingException();534ne.setRootCause(e);535throw ne;536}537} catch (IOException e) {538NamingException ne = new NamingException();539ne.setRootCause(e);540throw ne;541}542}543544/**545* Returns the attributes to bind given an object and its attributes.546*/547static Attributes determineBindAttrs(548char separator, Object obj, Attributes attrs, boolean cloned,549Name name, Context ctx, Hashtable<?,?> env)550throws NamingException {551552// Call state factories to convert object and attrs553DirStateFactory.Result res =554DirectoryManager.getStateToBind(obj, name, ctx, env, attrs);555obj = res.getObject();556attrs = res.getAttributes();557558// We're only storing attributes; no further processing required559if (obj == null) {560return attrs;561}562563//if object to be bound is a DirContext extract its attributes564if ((attrs == null) && (obj instanceof DirContext)) {565cloned = true;566attrs = ((DirContext)obj).getAttributes("");567}568569boolean ocNeedsCloning = false;570571// Create "objectClass" attribute572Attribute objectClass;573if (attrs == null || attrs.size() == 0) {574attrs = new BasicAttributes(LdapClient.caseIgnore);575cloned = true;576577// No objectclasses supplied, use "top" to start578objectClass = new BasicAttribute("objectClass", "top");579580} else {581// Get existing objectclass attribute582objectClass = attrs.get("objectClass");583if (objectClass == null && !attrs.isCaseIgnored()) {584// %%% workaround585objectClass = attrs.get("objectclass");586}587588// No objectclasses supplied, use "top" to start589if (objectClass == null) {590objectClass = new BasicAttribute("objectClass", "top");591} else if (ocNeedsCloning || !cloned) {592objectClass = (Attribute)objectClass.clone();593}594}595596// convert the supplied object into LDAP attributes597attrs = encodeObject(separator, obj, attrs, objectClass, cloned);598599// System.err.println("Determined: " + attrs);600return attrs;601}602603/**604* An ObjectInputStream that uses a class loader to find classes.605*/606private static final class LoaderInputStream extends ObjectInputStream {607private ClassLoader classLoader;608609LoaderInputStream(InputStream in, ClassLoader cl) throws IOException {610super(in);611classLoader = cl;612}613614protected Class<?> resolveClass(ObjectStreamClass desc) throws615IOException, ClassNotFoundException {616try {617// %%% Should use Class.forName(desc.getName(), false, classLoader);618// except we can't because that is only available on JDK1.2619return classLoader.loadClass(desc.getName());620} catch (ClassNotFoundException e) {621return super.resolveClass(desc);622}623}624625protected Class<?> resolveProxyClass(String[] interfaces) throws626IOException, ClassNotFoundException {627ClassLoader nonPublicLoader = null;628boolean hasNonPublicInterface = false;629630// define proxy in class loader of non-public interface(s), if any631Class<?>[] classObjs = new Class<?>[interfaces.length];632for (int i = 0; i < interfaces.length; i++) {633Class<?> cl = Class.forName(interfaces[i], false, classLoader);634if ((cl.getModifiers() & Modifier.PUBLIC) == 0) {635if (hasNonPublicInterface) {636if (nonPublicLoader != cl.getClassLoader()) {637throw new IllegalAccessError(638"conflicting non-public interface class loaders");639}640} else {641nonPublicLoader = cl.getClassLoader();642hasNonPublicInterface = true;643}644}645classObjs[i] = cl;646}647try {648@SuppressWarnings("deprecation")649Class<?> proxyClass = Proxy.getProxyClass(hasNonPublicInterface ?650nonPublicLoader : classLoader, classObjs);651return proxyClass;652} catch (IllegalArgumentException e) {653throw new ClassNotFoundException(null, e);654}655}656657}658}659660661