Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/rmi/registry/RegistryImpl.java
38831 views
/*1* Copyright (c) 1996, 2017, 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 sun.rmi.registry;2627import java.util.Enumeration;28import java.util.Hashtable;29import java.util.MissingResourceException;30import java.util.ResourceBundle;31import java.io.FilePermission;32import java.net.*;33import java.rmi.*;34import java.rmi.server.ObjID;35import java.rmi.server.ServerNotActiveException;36import java.rmi.registry.Registry;37import java.rmi.server.RMIClientSocketFactory;38import java.rmi.server.RMIServerSocketFactory;39import java.security.AccessControlContext;40import java.security.AccessController;41import java.security.CodeSource;42import java.security.Policy;43import java.security.PrivilegedActionException;44import java.security.PrivilegedExceptionAction;45import java.security.PermissionCollection;46import java.security.Permissions;47import java.security.PrivilegedAction;48import java.security.ProtectionDomain;49import java.security.Security;50import java.text.MessageFormat;5152import sun.misc.ObjectInputFilter;5354import sun.rmi.runtime.Log;55import sun.rmi.server.UnicastRef;56import sun.rmi.server.UnicastServerRef;57import sun.rmi.server.UnicastServerRef2;58import sun.rmi.transport.LiveRef;5960/**61* A "registry" exists on every node that allows RMI connections to62* servers on that node. The registry on a particular node contains a63* transient database that maps names to remote objects. When the64* node boots, the registry database is empty. The names stored in the65* registry are pure and are not parsed. A service storing itself in66* the registry may want to prefix its name of the service by a package67* name (although not required), to reduce name collisions in the68* registry.69*70* The LocateRegistry class is used to obtain registry for different hosts.71* <p>72* The default RegistryImpl exported restricts access to clients on the local host73* for the methods {@link #bind}, {@link #rebind}, {@link #unbind} by checking74* the client host in the skeleton.75*76* @see java.rmi.registry.LocateRegistry77*/78public class RegistryImpl extends java.rmi.server.RemoteServer79implements Registry80{8182/* indicate compatibility with JDK 1.1.x version of class */83private static final long serialVersionUID = 4666870661827494597L;84private Hashtable<String, Remote> bindings85= new Hashtable<>(101);86private static Hashtable<InetAddress, InetAddress> allowedAccessCache87= new Hashtable<>(3);88private static RegistryImpl registry;89private static ObjID id = new ObjID(ObjID.REGISTRY_ID);9091private static ResourceBundle resources = null;9293/**94* Property name of the RMI Registry serial filter to augment95* the built-in list of allowed types.96* Setting the property in the {@code lib/security/java.security} file97* will enable the augmented filter.98*/99private static final String REGISTRY_FILTER_PROPNAME = "sun.rmi.registry.registryFilter";100101/** Registry max depth of remote invocations. **/102private static final int REGISTRY_MAX_DEPTH = 20;103104/** Registry maximum array size in remote invocations. **/105private static final int REGISTRY_MAX_ARRAY_SIZE = 1_000_000;106107/**108* The registryFilter created from the value of the {@code "sun.rmi.registry.registryFilter"}109* property.110*/111private static final ObjectInputFilter registryFilter =112AccessController.doPrivileged((PrivilegedAction<ObjectInputFilter>)RegistryImpl::initRegistryFilter);113114/**115* Initialize the registryFilter from the security properties or system property; if any116* @return an ObjectInputFilter, or null117*/118private static ObjectInputFilter initRegistryFilter() {119ObjectInputFilter filter = null;120String props = System.getProperty(REGISTRY_FILTER_PROPNAME);121if (props == null) {122props = Security.getProperty(REGISTRY_FILTER_PROPNAME);123}124if (props != null) {125filter = ObjectInputFilter.Config.createFilter2(props);126Log regLog = Log.getLog("sun.rmi.registry", "registry", -1);127if (regLog.isLoggable(Log.BRIEF)) {128regLog.log(Log.BRIEF, "registryFilter = " + filter);129}130}131return filter;132}133134/**135* Construct a new RegistryImpl on the specified port with the136* given custom socket factory pair.137*/138public RegistryImpl(int port,139RMIClientSocketFactory csf,140RMIServerSocketFactory ssf)141throws RemoteException142{143this(port, csf, ssf, RegistryImpl::registryFilter);144}145146147/**148* Construct a new RegistryImpl on the specified port with the149* given custom socket factory pair and ObjectInputFilter.150*/151public RegistryImpl(int port,152RMIClientSocketFactory csf,153RMIServerSocketFactory ssf,154ObjectInputFilter serialFilter)155throws RemoteException156{157if (port == Registry.REGISTRY_PORT && System.getSecurityManager() != null) {158// grant permission for default port only.159try {160AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {161public Void run() throws RemoteException {162LiveRef lref = new LiveRef(id, port, csf, ssf);163setup(new UnicastServerRef2(lref, serialFilter));164return null;165}166}, null, new SocketPermission("localhost:"+port, "listen,accept"));167} catch (PrivilegedActionException pae) {168throw (RemoteException)pae.getException();169}170} else {171LiveRef lref = new LiveRef(id, port, csf, ssf);172setup(new UnicastServerRef2(lref, serialFilter));173}174}175176/**177* Construct a new RegistryImpl on the specified port.178*/179public RegistryImpl(int port)180throws RemoteException181{182if (port == Registry.REGISTRY_PORT && System.getSecurityManager() != null) {183// grant permission for default port only.184try {185AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {186public Void run() throws RemoteException {187LiveRef lref = new LiveRef(id, port);188setup(new UnicastServerRef(lref, RegistryImpl::registryFilter));189return null;190}191}, null, new SocketPermission("localhost:"+port, "listen,accept"));192} catch (PrivilegedActionException pae) {193throw (RemoteException)pae.getException();194}195} else {196LiveRef lref = new LiveRef(id, port);197setup(new UnicastServerRef(lref, RegistryImpl::registryFilter));198}199}200201/*202* Create the export the object using the parameter203* <code>uref</code>204*/205private void setup(UnicastServerRef uref)206throws RemoteException207{208/* Server ref must be created and assigned before remote209* object 'this' can be exported.210*/211ref = uref;212uref.exportObject(this, null, true);213}214215/**216* Returns the remote object for specified name in the registry.217* @exception RemoteException If remote operation failed.218* @exception NotBoundException If name is not currently bound.219*/220public Remote lookup(String name)221throws RemoteException, NotBoundException222{223synchronized (bindings) {224Remote obj = bindings.get(name);225if (obj == null)226throw new NotBoundException(name);227return obj;228}229}230231/**232* Binds the name to the specified remote object.233* @exception RemoteException If remote operation failed.234* @exception AlreadyBoundException If name is already bound.235*/236public void bind(String name, Remote obj)237throws RemoteException, AlreadyBoundException, AccessException238{239// The access check preventing remote access is done in the skeleton240// and is not applicable to local access.241synchronized (bindings) {242Remote curr = bindings.get(name);243if (curr != null)244throw new AlreadyBoundException(name);245bindings.put(name, obj);246}247}248249/**250* Unbind the name.251* @exception RemoteException If remote operation failed.252* @exception NotBoundException If name is not currently bound.253*/254public void unbind(String name)255throws RemoteException, NotBoundException, AccessException256{257// The access check preventing remote access is done in the skeleton258// and is not applicable to local access.259synchronized (bindings) {260Remote obj = bindings.get(name);261if (obj == null)262throw new NotBoundException(name);263bindings.remove(name);264}265}266267/**268* Rebind the name to a new object, replaces any existing binding.269* @exception RemoteException If remote operation failed.270*/271public void rebind(String name, Remote obj)272throws RemoteException, AccessException273{274// The access check preventing remote access is done in the skeleton275// and is not applicable to local access.276bindings.put(name, obj);277}278279/**280* Returns an enumeration of the names in the registry.281* @exception RemoteException If remote operation failed.282*/283public String[] list()284throws RemoteException285{286String[] names;287synchronized (bindings) {288int i = bindings.size();289names = new String[i];290Enumeration<String> enum_ = bindings.keys();291while ((--i) >= 0)292names[i] = enum_.nextElement();293}294return names;295}296297/**298* Check that the caller has access to perform indicated operation.299* The client must be on same the same host as this server.300*/301public static void checkAccess(String op) throws AccessException {302try {303/*304* Get client host that this registry operation was made from.305*/306final String clientHostName = getClientHost();307InetAddress clientHost;308309try {310clientHost = java.security.AccessController.doPrivileged(311new java.security.PrivilegedExceptionAction<InetAddress>() {312public InetAddress run()313throws java.net.UnknownHostException314{315return InetAddress.getByName(clientHostName);316}317});318} catch (PrivilegedActionException pae) {319throw (java.net.UnknownHostException) pae.getException();320}321322// if client not yet seen, make sure client allowed access323if (allowedAccessCache.get(clientHost) == null) {324325if (clientHost.isAnyLocalAddress()) {326throw new AccessException(327op + " disallowed; origin unknown");328}329330try {331final InetAddress finalClientHost = clientHost;332333java.security.AccessController.doPrivileged(334new java.security.PrivilegedExceptionAction<Void>() {335public Void run() throws java.io.IOException {336/*337* if a ServerSocket can be bound to the client's338* address then that address must be local339*/340(new ServerSocket(0, 10, finalClientHost)).close();341allowedAccessCache.put(finalClientHost,342finalClientHost);343return null;344}345});346} catch (PrivilegedActionException pae) {347// must have been an IOException348349throw new AccessException(350op + " disallowed; origin " +351clientHost + " is non-local host");352}353}354} catch (ServerNotActiveException ex) {355/*356* Local call from this VM: allow access.357*/358} catch (java.net.UnknownHostException ex) {359throw new AccessException(op + " disallowed; origin is unknown host");360}361}362363public static ObjID getID() {364return id;365}366367/**368* Retrieves text resources from the locale-specific properties file.369*/370private static String getTextResource(String key) {371if (resources == null) {372try {373resources = ResourceBundle.getBundle(374"sun.rmi.registry.resources.rmiregistry");375} catch (MissingResourceException mre) {376}377if (resources == null) {378// throwing an Error is a bit extreme, methinks379return ("[missing resource file: " + key + "]");380}381}382383String val = null;384try {385val = resources.getString(key);386} catch (MissingResourceException mre) {387}388389if (val == null) {390return ("[missing resource: " + key + "]");391} else {392return (val);393}394}395396/**397* ObjectInputFilter to filter Registry input objects.398* The list of acceptable classes is limited to classes normally399* stored in a registry.400*401* @param filterInfo access to the class, array length, etc.402* @return {@link ObjectInputFilter.Status#ALLOWED} if allowed,403* {@link ObjectInputFilter.Status#REJECTED} if rejected,404* otherwise {@link ObjectInputFilter.Status#UNDECIDED}405*/406private static ObjectInputFilter.Status registryFilter(ObjectInputFilter.FilterInfo filterInfo) {407if (registryFilter != null) {408ObjectInputFilter.Status status = registryFilter.checkInput(filterInfo);409if (status != ObjectInputFilter.Status.UNDECIDED) {410// The Registry filter can override the built-in white-list411return status;412}413}414415if (filterInfo.depth() > REGISTRY_MAX_DEPTH) {416return ObjectInputFilter.Status.REJECTED;417}418Class<?> clazz = filterInfo.serialClass();419if (clazz != null) {420if (clazz.isArray()) {421// Arrays are REJECTED only if they exceed the limit422return (filterInfo.arrayLength() >= 0 && filterInfo.arrayLength() > REGISTRY_MAX_ARRAY_SIZE)423? ObjectInputFilter.Status.REJECTED424: ObjectInputFilter.Status.UNDECIDED;425}426if (String.class == clazz427|| java.lang.Number.class.isAssignableFrom(clazz)428|| Remote.class.isAssignableFrom(clazz)429|| java.lang.reflect.Proxy.class.isAssignableFrom(clazz)430|| UnicastRef.class.isAssignableFrom(clazz)431|| RMIClientSocketFactory.class.isAssignableFrom(clazz)432|| RMIServerSocketFactory.class.isAssignableFrom(clazz)433|| java.rmi.activation.ActivationID.class.isAssignableFrom(clazz)434|| java.rmi.server.UID.class.isAssignableFrom(clazz)) {435return ObjectInputFilter.Status.ALLOWED;436} else {437return ObjectInputFilter.Status.REJECTED;438}439}440return ObjectInputFilter.Status.UNDECIDED;441}442443/**444* Main program to start a registry. <br>445* The port number can be specified on the command line.446*/447public static void main(String args[])448{449// Create and install the security manager if one is not installed450// already.451if (System.getSecurityManager() == null) {452System.setSecurityManager(new RMISecurityManager());453}454455try {456/*457* Fix bugid 4147561: When JDK tools are executed, the value of458* the CLASSPATH environment variable for the shell in which they459* were invoked is no longer incorporated into the application460* class path; CLASSPATH's only effect is to be the value of the461* system property "env.class.path". To preserve the previous462* (JDK1.1 and JDK1.2beta3) behavior of this tool, however, its463* CLASSPATH should still be considered when resolving classes464* being unmarshalled. To effect this old behavior, a class465* loader that loads from the file path specified in the466* "env.class.path" property is created and set to be the context467* class loader before the remote object is exported.468*/469String envcp = System.getProperty("env.class.path");470if (envcp == null) {471envcp = "."; // preserve old default behavior472}473URL[] urls = sun.misc.URLClassPath.pathToURLs(envcp);474ClassLoader cl = new URLClassLoader(urls);475476/*477* Fix bugid 4242317: Classes defined by this class loader should478* be annotated with the value of the "java.rmi.server.codebase"479* property, not the "file:" URLs for the CLASSPATH elements.480*/481sun.rmi.server.LoaderHandler.registerCodebaseLoader(cl);482483Thread.currentThread().setContextClassLoader(cl);484485final int regPort = (args.length >= 1) ? Integer.parseInt(args[0])486: Registry.REGISTRY_PORT;487try {488registry = AccessController.doPrivileged(489new PrivilegedExceptionAction<RegistryImpl>() {490public RegistryImpl run() throws RemoteException {491return new RegistryImpl(regPort);492}493}, getAccessControlContext(regPort));494} catch (PrivilegedActionException ex) {495throw (RemoteException) ex.getException();496}497498// prevent registry from exiting499while (true) {500try {501Thread.sleep(Long.MAX_VALUE);502} catch (InterruptedException e) {503}504}505} catch (NumberFormatException e) {506System.err.println(MessageFormat.format(507getTextResource("rmiregistry.port.badnumber"),508args[0] ));509System.err.println(MessageFormat.format(510getTextResource("rmiregistry.usage"),511"rmiregistry" ));512} catch (Exception e) {513e.printStackTrace();514}515System.exit(1);516}517518/**519* Generates an AccessControlContext with minimal permissions.520* The approach used here is taken from the similar method521* getAccessControlContext() in the sun.applet.AppletPanel class.522*/523private static AccessControlContext getAccessControlContext(int port) {524// begin with permissions granted to all code in current policy525PermissionCollection perms = AccessController.doPrivileged(526new java.security.PrivilegedAction<PermissionCollection>() {527public PermissionCollection run() {528CodeSource codesource = new CodeSource(null,529(java.security.cert.Certificate[]) null);530Policy p = java.security.Policy.getPolicy();531if (p != null) {532return p.getPermissions(codesource);533} else {534return new Permissions();535}536}537});538539/*540* Anyone can connect to the registry and the registry can connect541* to and possibly download stubs from anywhere. Downloaded stubs and542* related classes themselves are more tightly limited by RMI.543*/544perms.add(new SocketPermission("*", "connect,accept"));545perms.add(new SocketPermission("localhost:"+port, "listen,accept"));546547perms.add(new RuntimePermission("accessClassInPackage.sun.jvmstat.*"));548perms.add(new RuntimePermission("accessClassInPackage.sun.jvm.hotspot.*"));549550perms.add(new FilePermission("<<ALL FILES>>", "read"));551552/*553* Create an AccessControlContext that consists of a single554* protection domain with only the permissions calculated above.555*/556ProtectionDomain pd = new ProtectionDomain(557new CodeSource(null,558(java.security.cert.Certificate[]) null), perms);559return new AccessControlContext(new ProtectionDomain[] { pd });560}561}562563564