importjava.rmi.registry.Registry;importjava.rmi.registry.LocateRegistry;importjava.rmi.RemoteException;importjava.rmi.server.UnicastRemoteObject;publicclassServerimplementsHello{publicServer(){}publicStringsayHello(){return"Hello, world!";}publicstaticvoidmain(Stringargs[]){try{Serverobj=newServer();Hellostub=(Hello)UnicastRemoteObject.exportObject(obj,1098);// Bind the remote object's stub in the registry
Registryregistry=LocateRegistry.getRegistry(1099);registry.bind("Hello",stub);System.err.println("Server ready");}catch(Exceptione){System.err.println("Server exception: "+e.toString());e.printStackTrace();}}}
@defer.inlineCallbacksdefonConnect(client):# The following arguments may be also specified as unicode strings# but it is recommended to use byte strings for ldaptor objectsbasedn=b"dc=example,dc=org"binddn=b"cn=bob,ou=people,dc=example,dc=org"bindpw=b"secret"query=b"(cn=bob)"try:yieldclient.bind(binddn,bindpw)exceptExceptionasex:print(ex)raiseo=LDAPEntry(client,basedn)results=yieldo.search(filterText=query)forentryinresults:print(entry.getLDIF())
上述指定的过滤项称为属性,LDAP 中常见的属性定义如下:
String X.500 AttributeType
------------------------------
CN commonName
L localityName
ST stateOrProvinceName
O organizationName
OU organizationalUnitName
C countryName
STREET streetAddress
DC domainComponent
UID userid
DistinguishedName ::= RDNSequence
RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
RelativeDistinguishedName ::= SET SIZE (1..MAX) OF
AttributeTypeAndValue
AttributeTypeAndValue ::= SEQUENCE {
type AttributeType,
value AttributeValue }
这也是前文所说的,属性 type 和 value 使用等号分隔,每个属性使用逗号分隔。至于其他属性可以根据开发者的设计自行添加,比如对于企业人员的记录可以添加工号、邮箱等属性。
// HelloServer.java
importHelloApp.*;importorg.omg.CosNaming.*;importorg.omg.CosNaming.NamingContextPackage.*;importorg.omg.CORBA.*;importorg.omg.PortableServer.*;importorg.omg.PortableServer.POA;importjava.util.Properties;classHelloImplextendsHelloPOA{publicStringsayHello(){return"Hello from server";}publicvoidshutdown(){System.out.println("shutdown");}}publicclassHelloServer{publicstaticvoidmain(Stringargs[]){try{// create and initialize the ORB
ORBorb=ORB.init(args,null);// get reference to rootpoa & activate the POAManager
POArootpoa=POAHelper.narrow(orb.resolve_initial_references("RootPOA"));rootpoa.the_POAManager().activate();// create servant
HelloImplhelloImpl=newHelloImpl();// get object reference from the servant
org.omg.CORBA.Objectref=rootpoa.servant_to_reference(helloImpl);Hellohref=HelloHelper.narrow(ref);// get the root naming context
// NameService invokes the name service
org.omg.CORBA.ObjectobjRef=orb.resolve_initial_references("NameService");// Use NamingContextExt which is part of the Interoperable
// Naming Service (INS) specification.
NamingContextExtncRef=NamingContextExtHelper.narrow(objRef);// bind the Object Reference in Naming
Stringname="Hello";NameComponentpath[]=ncRef.to_name(name);ncRef.rebind(path,href);System.out.println("HelloServer ready and waiting ...");// wait for invocations from clients
orb.run();}catch(Exceptione){System.err.println("ERROR: "+e);e.printStackTrace(System.out);}System.out.println("HelloServer Exiting ...");}}
importHelloApp.*;importorg.omg.CosNaming.*;importorg.omg.CosNaming.NamingContextPackage.*;importorg.omg.CORBA.*;publicclassHelloClient{staticHellohelloImpl;publicstaticvoidmain(Stringargs[]){try{// create and initialize the ORB
ORBorb=ORB.init(args,null);// get the root naming context
org.omg.CORBA.ObjectobjRef=orb.resolve_initial_references("NameService");// Use NamingContextExt instead of NamingContext. This is
// part of the Interoperable naming Service.
NamingContextExtncRef=NamingContextExtHelper.narrow(objRef);// resolve the Object Reference in Naming
Stringname="Hello";helloImpl=HelloHelper.narrow(ncRef.resolve_str(name));System.out.println("Obtained a handle on server object: "+helloImpl);System.out.println(helloImpl.sayHello());helloImpl.shutdown();}catch(Exceptione){System.out.println("ERROR : "+e);e.printStackTrace(System.out);}}}
Obtained a handle on server object: IOR:000000000000001749444c3a48656c6c6f4170702f48656c6c6f3a312e300000000000010000000000000082000102000000000a3132372e302e302e3100e4d000000031afabcb0000000020b296da9800000001000000000000000100000008526f6f74504f410000000008000000010000000014000000000000020000000100000020000000000001000100000002050100010001002000010109000000010001010000000026000000020002
Hello from server
$ java JNDIDynamic "ldap://localhost:8080/cn=evilpan"
javax.naming.NameNotFoundException: [LDAP: error code 32 - No Such Object]; remaining name 'cn=evilpan'
at java.naming/com.sun.jndi.ldap.LdapCtx.mapErrorCode(LdapCtx.java:3183)
at java.naming/com.sun.jndi.ldap.LdapCtx.processReturnCode(LdapCtx.java:3104)
at java.naming/com.sun.jndi.ldap.LdapCtx.processReturnCode(LdapCtx.java:2895)
at java.naming/com.sun.jndi.ldap.LdapCtx.c_lookup(LdapCtx.java:1034)
at java.naming/com.sun.jndi.toolkit.ctx.ComponentContext.p_lookup(ComponentContext.java:542)
at java.naming/com.sun.jndi.toolkit.ctx.PartialCompositeContext.lookup(PartialCompositeContext.java:177)
at java.naming/com.sun.jndi.toolkit.url.GenericURLContext.lookup(GenericURLContext.java:207)
at java.naming/com.sun.jndi.url.ldap.ldapURLContext.lookup(ldapURLContext.java:94)
at java.naming/javax.naming.InitialContext.lookup(InitialContext.java:409)
at JNDIDynamic.main(JNDIDynamic.java:18)
// com/sun/jndi/rmi/registry/RegistryContext.java
privateObjectdecodeObject(Remoter,Namename)throwsNamingException{try{Objectobj=(rinstanceofRemoteReference)?((RemoteReference)r).getReference():(Object)r;/*
* Classes may only be loaded from an arbitrary URL codebase when
* the system property com.sun.jndi.rmi.object.trustURLCodebase
* has been set to "true".
*/// Use reference if possible
Referenceref=null;if(objinstanceofReference){ref=(Reference)obj;}elseif(objinstanceofReferenceable){ref=((Referenceable)(obj)).getReference();}if(ref!=null&&ref.getFactoryClassLocation()!=null&&!trustURLCodebase){thrownewConfigurationException("The object factory is untrusted. Set the system property"+" 'com.sun.jndi.rmi.object.trustURLCodebase' to 'true'.");}returnNamingManager.getObjectInstance(obj,name,this,environment);// } catch (NamingException e) { ...
}
根据注释所言,如果要解码的对象 r 是远程引用,就需要先解引用然后再调用 NamingManager.getObjectInstance,其中会实例化对应的 ObjectFactory 类并调用其 getObjectInstance 方法,这也符合我们前面打印的 EvilClass 的执行顺序。
// javax/naming/spi/NamingManager.java
publicstaticObjectgetObjectInstance(ObjectrefInfo,Namename,ContextnameCtx,Hashtable<?,?>environment)throwsException{// ...
if(ref!=null){Stringf=ref.getFactoryClassName();if(f!=null){// if reference identifies a factory, use exclusively
factory=getObjectFactoryFromReference(ref,f);if(factory!=null){returnfactory.getObjectInstance(ref,name,nameCtx,environment);}// No factory found, so return original refInfo.
// Will reach this point if factory class is not in
// class path and reference does not contain a URL for it
returnrefInfo;}else{// if reference has no factory, check for addresses
// containing URLs
answer=processURLAddrs(ref,name,nameCtx,environment);if(answer!=null){returnanswer;}}}// try using any specified factories
answer=createObjectFromFactories(refInfo,name,nameCtx,environment);return(answer!=null)?answer:refInfo;}
// java/org/apache/naming/ResourceRef.java
/**
* Resource Reference.
*
* @param resourceClass Resource class
* @param description Description of the resource
* @param scope Resource scope
* @param auth Resource authentication
* @param singleton Is this resource a singleton (every lookup should return
* the same instance rather than a new instance)?
* @param factory The possibly null class name of the object's factory.
* @param factoryLocation The possibly null location from which to load the
* factory (e.g. URL)
*/publicResourceRef(StringresourceClass,Stringdescription,Stringscope,Stringauth,booleansingleton,Stringfactory,StringfactoryLocation){
其中我们指定了资源的实际类为 javax.el.ELProcessor,工厂类为 apache.naming.factory.BeanFactory。x=eval 令上述代码实际执行的是 ELProcessor.eval 函数,其第一个参数是属性 x 的值,这里指定的是(MacOS)弹计算器。