1 /*
2  * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package sun.rmi.registry;
27 
28 import java.io.ObjectInputFilter;
29 import java.nio.file.Path;
30 import java.nio.file.Paths;
31 import java.security.PrivilegedAction;
32 import java.security.Security;
33 import java.util.ArrayList;
34 import java.util.Enumeration;
35 import java.util.Hashtable;
36 import java.util.List;
37 import java.util.MissingResourceException;
38 import java.util.ResourceBundle;
39 import java.io.File;
40 import java.io.FilePermission;
41 import java.io.IOException;
42 import java.net.*;
43 import java.rmi.*;
44 import java.rmi.server.ObjID;
45 import java.rmi.server.ServerNotActiveException;
46 import java.rmi.registry.Registry;
47 import java.rmi.server.RMIClientSocketFactory;
48 import java.rmi.server.RMIServerSocketFactory;
49 import java.security.AccessControlContext;
50 import java.security.AccessController;
51 import java.security.CodeSource;
52 import java.security.Policy;
53 import java.security.PrivilegedActionException;
54 import java.security.PrivilegedExceptionAction;
55 import java.security.PermissionCollection;
56 import java.security.Permissions;
57 import java.security.ProtectionDomain;
58 import java.text.MessageFormat;
59 
60 import jdk.internal.access.SharedSecrets;
61 import sun.rmi.runtime.Log;
62 import sun.rmi.server.UnicastRef;
63 import sun.rmi.server.UnicastServerRef;
64 import sun.rmi.server.UnicastServerRef2;
65 import sun.rmi.transport.LiveRef;
66 
67 /**
68  * A "registry" exists on every node that allows RMI connections to
69  * servers on that node.  The registry on a particular node contains a
70  * transient database that maps names to remote objects.  When the
71  * node boots, the registry database is empty.  The names stored in the
72  * registry are pure and are not parsed.  A service storing itself in
73  * the registry may want to prefix its name of the service by a package
74  * name (although not required), to reduce name collisions in the
75  * registry.
76  *
77  * The LocateRegistry class is used to obtain registry for different hosts.
78  * <p>
79  * The default RegistryImpl exported restricts access to clients on the local host
80  * for the methods {@link #bind}, {@link #rebind}, {@link #unbind} by checking
81  * the client host in the skeleton.
82  *
83  * @see java.rmi.registry.LocateRegistry
84  */
85 public class RegistryImpl extends java.rmi.server.RemoteServer
86         implements Registry
87 {
88 
89     /* indicate compatibility with JDK 1.1.x version of class */
90     private static final long serialVersionUID = 4666870661827494597L;
91     private Hashtable<String, Remote> bindings
92         = new Hashtable<>(101);
93     private static Hashtable<InetAddress, InetAddress> allowedAccessCache
94         = new Hashtable<>(3);
95     private static RegistryImpl registry;
96     private static ObjID id = new ObjID(ObjID.REGISTRY_ID);
97 
98     private static ResourceBundle resources = null;
99 
100     /**
101      * Property name of the RMI Registry serial filter to augment
102      * the built-in list of allowed types.
103      * Setting the property in the {@code conf/security/java.security} file
104      * will enable the augmented filter.
105      */
106     private static final String REGISTRY_FILTER_PROPNAME = "sun.rmi.registry.registryFilter";
107 
108     /** Registry max depth of remote invocations. **/
109     private static final int REGISTRY_MAX_DEPTH = 20;
110 
111     /** Registry maximum array size in remote invocations. **/
112     private static final int REGISTRY_MAX_ARRAY_SIZE = 1_000_000;
113 
114     /**
115      * The registryFilter created from the value of the {@code "sun.rmi.registry.registryFilter"}
116      * property.
117      */
118     private static final ObjectInputFilter registryFilter =
119             AccessController.doPrivileged((PrivilegedAction<ObjectInputFilter>)RegistryImpl::initRegistryFilter);
120 
121     /**
122      * Initialize the registryFilter from the security properties or system property; if any
123      * @return an ObjectInputFilter, or null
124      */
125     @SuppressWarnings("deprecation")
initRegistryFilter()126     private static ObjectInputFilter initRegistryFilter() {
127         ObjectInputFilter filter = null;
128         String props = System.getProperty(REGISTRY_FILTER_PROPNAME);
129         if (props == null) {
130             props = Security.getProperty(REGISTRY_FILTER_PROPNAME);
131         }
132         if (props != null) {
133             filter = SharedSecrets.getJavaObjectInputFilterAccess().createFilter2(props);
134             Log regLog = Log.getLog("sun.rmi.registry", "registry", -1);
135             if (regLog.isLoggable(Log.BRIEF)) {
136                 regLog.log(Log.BRIEF, "registryFilter = " + filter);
137             }
138         }
139         return filter;
140     }
141 
142     /**
143      * Construct a new RegistryImpl on the specified port with the
144      * given custom socket factory pair.
145      */
RegistryImpl(int port, RMIClientSocketFactory csf, RMIServerSocketFactory ssf)146     public RegistryImpl(int port,
147                         RMIClientSocketFactory csf,
148                         RMIServerSocketFactory ssf)
149         throws RemoteException
150     {
151         this(port, csf, ssf, RegistryImpl::registryFilter);
152     }
153 
154 
155     /**
156      * Construct a new RegistryImpl on the specified port with the
157      * given custom socket factory pair and ObjectInputFilter.
158      */
RegistryImpl(int port, RMIClientSocketFactory csf, RMIServerSocketFactory ssf, ObjectInputFilter serialFilter)159     public RegistryImpl(int port,
160                         RMIClientSocketFactory csf,
161                         RMIServerSocketFactory ssf,
162                         ObjectInputFilter serialFilter)
163         throws RemoteException
164     {
165         if (port == Registry.REGISTRY_PORT && System.getSecurityManager() != null) {
166             // grant permission for default port only.
167             try {
168                 AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
169                     public Void run() throws RemoteException {
170                         LiveRef lref = new LiveRef(id, port, csf, ssf);
171                         setup(new UnicastServerRef2(lref, serialFilter));
172                         return null;
173                     }
174                 }, null, new SocketPermission("localhost:"+port, "listen,accept"));
175             } catch (PrivilegedActionException pae) {
176                 throw (RemoteException)pae.getException();
177             }
178         } else {
179             LiveRef lref = new LiveRef(id, port, csf, ssf);
180             setup(new UnicastServerRef2(lref, serialFilter));
181         }
182     }
183 
184     /**
185      * Construct a new RegistryImpl on the specified port.
186      */
RegistryImpl(int port)187     public RegistryImpl(int port)
188         throws RemoteException
189     {
190         if (port == Registry.REGISTRY_PORT && System.getSecurityManager() != null) {
191             // grant permission for default port only.
192             try {
193                 AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
194                     public Void run() throws RemoteException {
195                         LiveRef lref = new LiveRef(id, port);
196                         setup(new UnicastServerRef(lref, RegistryImpl::registryFilter));
197                         return null;
198                     }
199                 }, null, new SocketPermission("localhost:"+port, "listen,accept"));
200             } catch (PrivilegedActionException pae) {
201                 throw (RemoteException)pae.getException();
202             }
203         } else {
204             LiveRef lref = new LiveRef(id, port);
205             setup(new UnicastServerRef(lref, RegistryImpl::registryFilter));
206         }
207     }
208 
209     /*
210      * Create the export the object using the parameter
211      * <code>uref</code>
212      */
setup(UnicastServerRef uref)213     private void setup(UnicastServerRef uref)
214         throws RemoteException
215     {
216         /* Server ref must be created and assigned before remote
217          * object 'this' can be exported.
218          */
219         ref = uref;
220         uref.exportObject(this, null, true);
221     }
222 
223     /**
224      * Returns the remote object for specified name in the registry.
225      * @exception RemoteException If remote operation failed.
226      * @exception NotBoundException If name is not currently bound.
227      */
lookup(String name)228     public Remote lookup(String name)
229         throws RemoteException, NotBoundException
230     {
231         synchronized (bindings) {
232             Remote obj = bindings.get(name);
233             if (obj == null)
234                 throw new NotBoundException(name);
235             return obj;
236         }
237     }
238 
239     /**
240      * Binds the name to the specified remote object.
241      * @exception RemoteException If remote operation failed.
242      * @exception AlreadyBoundException If name is already bound.
243      */
bind(String name, Remote obj)244     public void bind(String name, Remote obj)
245         throws RemoteException, AlreadyBoundException, AccessException
246     {
247         // The access check preventing remote access is done in the skeleton
248         // and is not applicable to local access.
249         synchronized (bindings) {
250             Remote curr = bindings.get(name);
251             if (curr != null)
252                 throw new AlreadyBoundException(name);
253             bindings.put(name, obj);
254         }
255     }
256 
257     /**
258      * Unbind the name.
259      * @exception RemoteException If remote operation failed.
260      * @exception NotBoundException If name is not currently bound.
261      */
unbind(String name)262     public void unbind(String name)
263         throws RemoteException, NotBoundException, AccessException
264     {
265         // The access check preventing remote access is done in the skeleton
266         // and is not applicable to local access.
267         synchronized (bindings) {
268             Remote obj = bindings.get(name);
269             if (obj == null)
270                 throw new NotBoundException(name);
271             bindings.remove(name);
272         }
273     }
274 
275     /**
276      * Rebind the name to a new object, replaces any existing binding.
277      * @exception RemoteException If remote operation failed.
278      */
rebind(String name, Remote obj)279     public void rebind(String name, Remote obj)
280         throws RemoteException, AccessException
281     {
282         // The access check preventing remote access is done in the skeleton
283         // and is not applicable to local access.
284         bindings.put(name, obj);
285     }
286 
287     /**
288      * Returns an enumeration of the names in the registry.
289      * @exception RemoteException If remote operation failed.
290      */
list()291     public String[] list()
292         throws RemoteException
293     {
294         String[] names;
295         synchronized (bindings) {
296             int i = bindings.size();
297             names = new String[i];
298             Enumeration<String> enum_ = bindings.keys();
299             while ((--i) >= 0)
300                 names[i] = enum_.nextElement();
301         }
302         return names;
303     }
304 
305     /**
306      * Check that the caller has access to perform indicated operation.
307      * The client must be on same the same host as this server.
308      */
checkAccess(String op)309     public static void checkAccess(String op) throws AccessException {
310 
311         try {
312             /*
313              * Get client host that this registry operation was made from.
314              */
315             final String clientHostName = getClientHost();
316             InetAddress clientHost;
317 
318             try {
319                 clientHost = java.security.AccessController.doPrivileged(
320                     new java.security.PrivilegedExceptionAction<InetAddress>() {
321                         public InetAddress run()
322                             throws java.net.UnknownHostException
323                         {
324                             return InetAddress.getByName(clientHostName);
325                         }
326                     });
327             } catch (PrivilegedActionException pae) {
328                 throw (java.net.UnknownHostException) pae.getException();
329             }
330 
331             // if client not yet seen, make sure client allowed access
332             if (allowedAccessCache.get(clientHost) == null) {
333 
334                 if (clientHost.isAnyLocalAddress()) {
335                     throw new AccessException(
336                         op + " disallowed; origin unknown");
337                 }
338 
339                 try {
340                     final InetAddress finalClientHost = clientHost;
341 
342                     java.security.AccessController.doPrivileged(
343                         new java.security.PrivilegedExceptionAction<Void>() {
344                             public Void run() throws java.io.IOException {
345                                 /*
346                                  * if a ServerSocket can be bound to the client's
347                                  * address then that address must be local
348                                  */
349                                 (new ServerSocket(0, 10, finalClientHost)).close();
350                                 allowedAccessCache.put(finalClientHost,
351                                                        finalClientHost);
352                                 return null;
353                             }
354                     });
355                 } catch (PrivilegedActionException pae) {
356                     // must have been an IOException
357 
358                     throw new AccessException(
359                         op + " disallowed; origin " +
360                         clientHost + " is non-local host");
361                 }
362             }
363         } catch (ServerNotActiveException ex) {
364             /*
365              * Local call from this VM: allow access.
366              */
367         } catch (java.net.UnknownHostException ex) {
368             throw new AccessException(op + " disallowed; origin is unknown host");
369         }
370     }
371 
getID()372     public static ObjID getID() {
373         return id;
374     }
375 
376     /**
377      * Retrieves text resources from the locale-specific properties file.
378      */
getTextResource(String key)379     private static String getTextResource(String key) {
380         if (resources == null) {
381             try {
382                 resources = ResourceBundle.getBundle(
383                     "sun.rmi.registry.resources.rmiregistry");
384             } catch (MissingResourceException mre) {
385             }
386             if (resources == null) {
387                 // throwing an Error is a bit extreme, methinks
388                 return ("[missing resource file: " + key + "]");
389             }
390         }
391 
392         String val = null;
393         try {
394             val = resources.getString(key);
395         } catch (MissingResourceException mre) {
396         }
397 
398         if (val == null) {
399             return ("[missing resource: " + key + "]");
400         } else {
401             return (val);
402         }
403     }
404 
405     /**
406      * Convert class path specification into an array of file URLs.
407      *
408      * The path of the file is converted to a URI then into URL
409      * form so that reserved characters can safely appear in the path.
410      */
pathToURLs(String path)411     private static URL[] pathToURLs(String path) {
412         List<URL> paths = new ArrayList<>();
413         for (String entry: path.split(File.pathSeparator)) {
414             Path p = Paths.get(entry);
415             try {
416                 p = p.toRealPath();
417             } catch (IOException x) {
418                 p = p.toAbsolutePath();
419             }
420             try {
421                 paths.add(p.toUri().toURL());
422             } catch (MalformedURLException e) {
423                 //ignore / skip entry
424             }
425         }
426         return paths.toArray(new URL[0]);
427     }
428 
429     /**
430      * ObjectInputFilter to filter Registry input objects.
431      * The list of acceptable classes is limited to classes normally
432      * stored in a registry.
433      *
434      * @param filterInfo access to the class, array length, etc.
435      * @return  {@link ObjectInputFilter.Status#ALLOWED} if allowed,
436      *          {@link ObjectInputFilter.Status#REJECTED} if rejected,
437      *          otherwise {@link ObjectInputFilter.Status#UNDECIDED}
438      */
registryFilter(ObjectInputFilter.FilterInfo filterInfo)439     private static ObjectInputFilter.Status registryFilter(ObjectInputFilter.FilterInfo filterInfo) {
440         if (registryFilter != null) {
441             ObjectInputFilter.Status status = registryFilter.checkInput(filterInfo);
442             if (status != ObjectInputFilter.Status.UNDECIDED) {
443                 // The Registry filter can override the built-in white-list
444                 return status;
445             }
446         }
447 
448         if (filterInfo.depth() > REGISTRY_MAX_DEPTH) {
449             return ObjectInputFilter.Status.REJECTED;
450         }
451         Class<?> clazz = filterInfo.serialClass();
452         if (clazz != null) {
453             if (clazz.isArray()) {
454                 // Arrays are REJECTED only if they exceed the limit
455                 return (filterInfo.arrayLength() >= 0 && filterInfo.arrayLength() > REGISTRY_MAX_ARRAY_SIZE)
456                     ? ObjectInputFilter.Status.REJECTED
457                     : ObjectInputFilter.Status.UNDECIDED;
458             }
459             if (String.class == clazz
460                     || java.lang.Number.class.isAssignableFrom(clazz)
461                     || Remote.class.isAssignableFrom(clazz)
462                     || java.lang.reflect.Proxy.class.isAssignableFrom(clazz)
463                     || UnicastRef.class.isAssignableFrom(clazz)
464                     || RMIClientSocketFactory.class.isAssignableFrom(clazz)
465                     || RMIServerSocketFactory.class.isAssignableFrom(clazz)
466                     || java.rmi.activation.ActivationID.class.isAssignableFrom(clazz)
467                     || java.rmi.server.UID.class.isAssignableFrom(clazz)) {
468                 return ObjectInputFilter.Status.ALLOWED;
469             } else {
470                 return ObjectInputFilter.Status.REJECTED;
471             }
472         }
473         return ObjectInputFilter.Status.UNDECIDED;
474     }
475 
476     /**
477      * Return a new RegistryImpl on the requested port and export it to serve
478      * registry requests. A classloader is initialized from the system property
479      * "env.class.path" and a security manager is set unless one is already set.
480      * <p>
481      * The returned Registry is fully functional within the current process and
482      * is usable for internal and testing purposes.
483      *
484      * @param regPort port on which the rmiregistry accepts requests;
485      *                if 0, an implementation specific port is assigned
486      * @return a RegistryImpl instance
487      * @exception RemoteException If remote operation failed.
488      * @since 9
489      */
createRegistry(int regPort)490     public static RegistryImpl createRegistry(int regPort) throws RemoteException {
491         // Create and install the security manager if one is not installed
492         // already.
493         if (System.getSecurityManager() == null) {
494             System.setSecurityManager(new SecurityManager());
495         }
496 
497         /*
498          * Fix bugid 4147561: When JDK tools are executed, the value of
499          * the CLASSPATH environment variable for the shell in which they
500          * were invoked is no longer incorporated into the application
501          * class path; CLASSPATH's only effect is to be the value of the
502          * system property "env.class.path".  To preserve the previous
503          * (JDK1.1 and JDK1.2beta3) behavior of this tool, however, its
504          * CLASSPATH should still be considered when resolving classes
505          * being unmarshalled.  To effect this old behavior, a class
506          * loader that loads from the file path specified in the
507          * "env.class.path" property is created and set to be the context
508          * class loader before the remote object is exported.
509          */
510         String envcp = System.getProperty("env.class.path");
511         if (envcp == null) {
512             envcp = ".";            // preserve old default behavior
513         }
514         URL[] urls = pathToURLs(envcp);
515         ClassLoader cl = new URLClassLoader(urls);
516 
517         /*
518          * Fix bugid 4242317: Classes defined by this class loader should
519          * be annotated with the value of the "java.rmi.server.codebase"
520          * property, not the "file:" URLs for the CLASSPATH elements.
521          */
522         sun.rmi.server.LoaderHandler.registerCodebaseLoader(cl);
523 
524         Thread.currentThread().setContextClassLoader(cl);
525 
526         RegistryImpl registryImpl = null;
527         try {
528             registryImpl = AccessController.doPrivileged(
529                 new PrivilegedExceptionAction<RegistryImpl>() {
530                     public RegistryImpl run() throws RemoteException {
531                         return new RegistryImpl(regPort);
532                     }
533                 }, getAccessControlContext(regPort));
534         } catch (PrivilegedActionException ex) {
535             throw (RemoteException) ex.getException();
536         }
537 
538         return registryImpl;
539     }
540 
541     /**
542      * Main program to start a registry. <br>
543      * The port number can be specified on the command line.
544      */
main(String args[])545     public static void main(String args[])
546     {
547         try {
548             final int regPort = (args.length >= 1) ? Integer.parseInt(args[0])
549                                                    : Registry.REGISTRY_PORT;
550 
551             registry = createRegistry(regPort);
552 
553             // prevent registry from exiting
554             while (true) {
555                 try {
556                     Thread.sleep(Long.MAX_VALUE);
557                 } catch (InterruptedException e) {
558                 }
559             }
560         } catch (NumberFormatException e) {
561             System.err.println(MessageFormat.format(
562                 getTextResource("rmiregistry.port.badnumber"),
563                 args[0] ));
564             System.err.println(MessageFormat.format(
565                 getTextResource("rmiregistry.usage"),
566                 "rmiregistry" ));
567         } catch (Exception e) {
568             e.printStackTrace();
569         }
570         System.exit(1);
571     }
572 
573     /**
574      * Generates an AccessControlContext with minimal permissions.
575      * The approach used here is taken from the similar method
576      * getAccessControlContext() in the sun.applet.AppletPanel class.
577      */
getAccessControlContext(int port)578     private static AccessControlContext getAccessControlContext(int port) {
579         // begin with permissions granted to all code in current policy
580         PermissionCollection perms = AccessController.doPrivileged(
581             new java.security.PrivilegedAction<PermissionCollection>() {
582                 public PermissionCollection run() {
583                     CodeSource codesource = new CodeSource(null,
584                         (java.security.cert.Certificate[]) null);
585                     Policy p = java.security.Policy.getPolicy();
586                     if (p != null) {
587                         return p.getPermissions(codesource);
588                     } else {
589                         return new Permissions();
590                     }
591                 }
592             });
593 
594         /*
595          * Anyone can connect to the registry and the registry can connect
596          * to and possibly download stubs from anywhere. Downloaded stubs and
597          * related classes themselves are more tightly limited by RMI.
598          */
599         perms.add(new SocketPermission("*", "connect,accept"));
600         perms.add(new SocketPermission("localhost:"+port, "listen,accept"));
601 
602         perms.add(new RuntimePermission("accessClassInPackage.sun.jvmstat.*"));
603         perms.add(new RuntimePermission("accessClassInPackage.sun.jvm.hotspot.*"));
604 
605         perms.add(new FilePermission("<<ALL FILES>>", "read"));
606 
607         /*
608          * Create an AccessControlContext that consists of a single
609          * protection domain with only the permissions calculated above.
610          */
611         ProtectionDomain pd = new ProtectionDomain(
612             new CodeSource(null,
613                 (java.security.cert.Certificate[]) null), perms);
614         return new AccessControlContext(new ProtectionDomain[] { pd });
615     }
616 }
617