1 /*
   2  * Copyright (c) 1996, 2014, 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.util.Enumeration;
  29 import java.util.Hashtable;
  30 import java.util.MissingResourceException;
  31 import java.util.ResourceBundle;
  32 import java.io.FilePermission;
  33 import java.io.IOException;
  34 import java.net.*;
  35 import java.rmi.*;
  36 import java.rmi.server.ObjID;
  37 import java.rmi.server.RemoteServer;
  38 import java.rmi.server.ServerNotActiveException;
  39 import java.rmi.registry.Registry;
  40 import java.rmi.server.RMIClientSocketFactory;
  41 import java.rmi.server.RMIServerSocketFactory;
  42 import java.security.AccessControlContext;
  43 import java.security.AccessController;
  44 import java.security.CodeSource;
  45 import java.security.Policy;
  46 import java.security.PrivilegedActionException;
  47 import java.security.PrivilegedExceptionAction;
  48 import java.security.PermissionCollection;
  49 import java.security.Permissions;
  50 import java.security.ProtectionDomain;
  51 import java.text.MessageFormat;
  52 import sun.rmi.server.LoaderHandler;
  53 import sun.rmi.server.UnicastServerRef;
  54 import sun.rmi.server.UnicastServerRef2;
  55 import sun.rmi.transport.LiveRef;
  56 import sun.rmi.transport.ObjectTable;
  57 import sun.rmi.transport.Target;
  58 
  59 /**
  60  * A "registry" exists on every node that allows RMI connections to
  61  * servers on that node.  The registry on a particular node contains a
  62  * transient database that maps names to remote objects.  When the
  63  * node boots, the registry database is empty.  The names stored in the
  64  * registry are pure and are not parsed.  A service storing itself in
  65  * the registry may want to prefix its name of the service by a package
  66  * name (although not required), to reduce name collisions in the
  67  * registry.
  68  *
  69  * The LocateRegistry class is used to obtain registry for different hosts.
  70  *
  71  * @see java.rmi.registry.LocateRegistry
  72  */
  73 public class RegistryImpl extends java.rmi.server.RemoteServer
  74         implements Registry
  75 {
  76 
  77     /* indicate compatibility with JDK 1.1.x version of class */
  78     private static final long serialVersionUID = 4666870661827494597L;
  79     private Hashtable<String, Remote> bindings
  80         = new Hashtable<>(101);
  81     private static Hashtable<InetAddress, InetAddress> allowedAccessCache
  82         = new Hashtable<>(3);
  83     private static RegistryImpl registry;
  84     private static ObjID id = new ObjID(ObjID.REGISTRY_ID);
  85 
  86     private static ResourceBundle resources = null;
  87 
  88     /**
  89      * Construct a new RegistryImpl on the specified port with the
  90      * given custom socket factory pair.
  91      */
  92     public RegistryImpl(int port,
  93                         RMIClientSocketFactory csf,
  94                         RMIServerSocketFactory ssf)
  95         throws RemoteException
  96     {
  97         if (port == Registry.REGISTRY_PORT && System.getSecurityManager() != null) {
  98             // grant permission for default port only.
  99             try {
 100                 AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
 101                     public Void run() throws RemoteException {
 102                         LiveRef lref = new LiveRef(id, port, csf, ssf);
 103                         setup(new UnicastServerRef2(lref));
 104                         return null;
 105                     }
 106                 }, null, new SocketPermission("localhost:"+port, "listen,accept"));
 107             } catch (PrivilegedActionException pae) {
 108                 throw (RemoteException)pae.getException();
 109             }
 110         } else {
 111             LiveRef lref = new LiveRef(id, port, csf, ssf);
 112             setup(new UnicastServerRef2(lref));
 113         }
 114     }
 115 
 116     /**
 117      * Construct a new RegistryImpl on the specified port.
 118      */
 119     public RegistryImpl(int port)
 120         throws RemoteException
 121     {
 122         if (port == Registry.REGISTRY_PORT && System.getSecurityManager() != null) {
 123             // grant permission for default port only.
 124             try {
 125                 AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
 126                     public Void run() throws RemoteException {
 127                         LiveRef lref = new LiveRef(id, port);
 128                         setup(new UnicastServerRef(lref));
 129                         return null;
 130                     }
 131                 }, null, new SocketPermission("localhost:"+port, "listen,accept"));
 132             } catch (PrivilegedActionException pae) {
 133                 throw (RemoteException)pae.getException();
 134             }
 135         } else {
 136             LiveRef lref = new LiveRef(id, port);
 137             setup(new UnicastServerRef(lref));
 138         }
 139     }
 140 
 141     /*
 142      * Create the export the object using the parameter
 143      * <code>uref</code>
 144      */
 145     private void setup(UnicastServerRef uref)
 146         throws RemoteException
 147     {
 148         /* Server ref must be created and assigned before remote
 149          * object 'this' can be exported.
 150          */
 151         ref = uref;
 152         uref.exportObject(this, null, true);
 153     }
 154 
 155     /**
 156      * Returns the remote object for specified name in the registry.
 157      * @exception RemoteException If remote operation failed.
 158      * @exception NotBound If name is not currently bound.
 159      */
 160     public Remote lookup(String name)
 161         throws RemoteException, NotBoundException
 162     {
 163         synchronized (bindings) {
 164             Remote obj = bindings.get(name);
 165             if (obj == null)
 166                 throw new NotBoundException(name);
 167             return obj;
 168         }
 169     }
 170 
 171     /**
 172      * Binds the name to the specified remote object.
 173      * @exception RemoteException If remote operation failed.
 174      * @exception AlreadyBoundException If name is already bound.
 175      */
 176     public void bind(String name, Remote obj)
 177         throws RemoteException, AlreadyBoundException, AccessException
 178     {
 179         checkAccess("Registry.bind");
 180         synchronized (bindings) {
 181             Remote curr = bindings.get(name);
 182             if (curr != null)
 183                 throw new AlreadyBoundException(name);
 184             bindings.put(name, obj);
 185         }
 186     }
 187 
 188     /**
 189      * Unbind the name.
 190      * @exception RemoteException If remote operation failed.
 191      * @exception NotBound If name is not currently bound.
 192      */
 193     public void unbind(String name)
 194         throws RemoteException, NotBoundException, AccessException
 195     {
 196         checkAccess("Registry.unbind");
 197         synchronized (bindings) {
 198             Remote obj = bindings.get(name);
 199             if (obj == null)
 200                 throw new NotBoundException(name);
 201             bindings.remove(name);
 202         }
 203     }
 204 
 205     /**
 206      * Rebind the name to a new object, replaces any existing binding.
 207      * @exception RemoteException If remote operation failed.
 208      */
 209     public void rebind(String name, Remote obj)
 210         throws RemoteException, AccessException
 211     {
 212         checkAccess("Registry.rebind");
 213         bindings.put(name, obj);
 214     }
 215 
 216     /**
 217      * Returns an enumeration of the names in the registry.
 218      * @exception RemoteException If remote operation failed.
 219      */
 220     public String[] list()
 221         throws RemoteException
 222     {
 223         String[] names;
 224         synchronized (bindings) {
 225             int i = bindings.size();
 226             names = new String[i];
 227             Enumeration<String> enum_ = bindings.keys();
 228             while ((--i) >= 0)
 229                 names[i] = enum_.nextElement();
 230         }
 231         return names;
 232     }
 233 
 234     /**
 235      * Check that the caller has access to perform indicated operation.
 236      * The client must be on same the same host as this server.
 237      */
 238     public static void checkAccess(String op) throws AccessException {
 239 
 240         try {
 241             /*
 242              * Get client host that this registry operation was made from.
 243              */
 244             final String clientHostName = getClientHost();
 245             InetAddress clientHost;
 246 
 247             try {
 248                 clientHost = java.security.AccessController.doPrivileged(
 249                     new java.security.PrivilegedExceptionAction<InetAddress>() {
 250                         public InetAddress run()
 251                             throws java.net.UnknownHostException
 252                         {
 253                             return InetAddress.getByName(clientHostName);
 254                         }
 255                     });
 256             } catch (PrivilegedActionException pae) {
 257                 throw (java.net.UnknownHostException) pae.getException();
 258             }
 259 
 260             // if client not yet seen, make sure client allowed access
 261             if (allowedAccessCache.get(clientHost) == null) {
 262 
 263                 if (clientHost.isAnyLocalAddress()) {
 264                     throw new AccessException(
 265                         "Registry." + op + " disallowed; origin unknown");
 266                 }
 267 
 268                 try {
 269                     final InetAddress finalClientHost = clientHost;
 270 
 271                     java.security.AccessController.doPrivileged(
 272                         new java.security.PrivilegedExceptionAction<Void>() {
 273                             public Void run() throws java.io.IOException {
 274                                 /*
 275                                  * if a ServerSocket can be bound to the client's
 276                                  * address then that address must be local
 277                                  */
 278                                 (new ServerSocket(0, 10, finalClientHost)).close();
 279                                 allowedAccessCache.put(finalClientHost,
 280                                                        finalClientHost);
 281                                 return null;
 282                             }
 283                     });
 284                 } catch (PrivilegedActionException pae) {
 285                     // must have been an IOException
 286 
 287                     throw new AccessException(
 288                         "Registry." + op + " disallowed; origin " +
 289                         clientHost + " is non-local host");
 290                 }
 291             }
 292         } catch (ServerNotActiveException ex) {
 293             /*
 294              * Local call from this VM: allow access.
 295              */
 296         } catch (java.net.UnknownHostException ex) {
 297             throw new AccessException("Registry." + op +
 298                                       " disallowed; origin is unknown host");
 299         }
 300     }
 301 
 302     public static ObjID getID() {
 303         return id;
 304     }
 305 
 306     /**
 307      * Retrieves text resources from the locale-specific properties file.
 308      */
 309     private static String getTextResource(String key) {
 310         if (resources == null) {
 311             try {
 312                 resources = ResourceBundle.getBundle(
 313                     "sun.rmi.registry.resources.rmiregistry");
 314             } catch (MissingResourceException mre) {
 315             }
 316             if (resources == null) {
 317                 // throwing an Error is a bit extreme, methinks
 318                 return ("[missing resource file: " + key + "]");
 319             }
 320         }
 321 
 322         String val = null;
 323         try {
 324             val = resources.getString(key);
 325         } catch (MissingResourceException mre) {
 326         }
 327 
 328         if (val == null) {
 329             return ("[missing resource: " + key + "]");
 330         } else {
 331             return (val);
 332         }
 333     }
 334 
 335     /**
 336      * Main program to start a registry. <br>
 337      * The port number can be specified on the command line.
 338      */
 339     public static void main(String args[])
 340     {
 341         // Create and install the security manager if one is not installed
 342         // already.
 343         if (System.getSecurityManager() == null) {
 344             System.setSecurityManager(new SecurityManager());
 345         }
 346 
 347         try {
 348             /*
 349              * Fix bugid 4147561: When JDK tools are executed, the value of
 350              * the CLASSPATH environment variable for the shell in which they
 351              * were invoked is no longer incorporated into the application
 352              * class path; CLASSPATH's only effect is to be the value of the
 353              * system property "env.class.path".  To preserve the previous
 354              * (JDK1.1 and JDK1.2beta3) behavior of this tool, however, its
 355              * CLASSPATH should still be considered when resolving classes
 356              * being unmarshalled.  To effect this old behavior, a class
 357              * loader that loads from the file path specified in the
 358              * "env.class.path" property is created and set to be the context
 359              * class loader before the remote object is exported.
 360              */
 361             String envcp = System.getProperty("env.class.path");
 362             if (envcp == null) {
 363                 envcp = ".";            // preserve old default behavior
 364             }
 365             URL[] urls = sun.misc.URLClassPath.pathToURLs(envcp);
 366             ClassLoader cl = new URLClassLoader(urls);
 367 
 368             /*
 369              * Fix bugid 4242317: Classes defined by this class loader should
 370              * be annotated with the value of the "java.rmi.server.codebase"
 371              * property, not the "file:" URLs for the CLASSPATH elements.
 372              */
 373             sun.rmi.server.LoaderHandler.registerCodebaseLoader(cl);
 374 
 375             Thread.currentThread().setContextClassLoader(cl);
 376 
 377             final int regPort = (args.length >= 1) ? Integer.parseInt(args[0])
 378                                                    : Registry.REGISTRY_PORT;
 379             try {
 380                 registry = AccessController.doPrivileged(
 381                     new PrivilegedExceptionAction<RegistryImpl>() {
 382                         public RegistryImpl run() throws RemoteException {
 383                             return new RegistryImpl(regPort);
 384                         }
 385                     }, getAccessControlContext(regPort));
 386             } catch (PrivilegedActionException ex) {
 387                 throw (RemoteException) ex.getException();
 388             }
 389 
 390             // prevent registry from exiting
 391             while (true) {
 392                 try {
 393                     Thread.sleep(Long.MAX_VALUE);
 394                 } catch (InterruptedException e) {
 395                 }
 396             }
 397         } catch (NumberFormatException e) {
 398             System.err.println(MessageFormat.format(
 399                 getTextResource("rmiregistry.port.badnumber"),
 400                 args[0] ));
 401             System.err.println(MessageFormat.format(
 402                 getTextResource("rmiregistry.usage"),
 403                 "rmiregistry" ));
 404         } catch (Exception e) {
 405             e.printStackTrace();
 406         }
 407         System.exit(1);
 408     }
 409 
 410     /**
 411      * Generates an AccessControlContext with minimal permissions.
 412      * The approach used here is taken from the similar method
 413      * getAccessControlContext() in the sun.applet.AppletPanel class.
 414      */
 415     private static AccessControlContext getAccessControlContext(int port) {
 416         // begin with permissions granted to all code in current policy
 417         PermissionCollection perms = AccessController.doPrivileged(
 418             new java.security.PrivilegedAction<PermissionCollection>() {
 419                 public PermissionCollection run() {
 420                     CodeSource codesource = new CodeSource(null,
 421                         (java.security.cert.Certificate[]) null);
 422                     Policy p = java.security.Policy.getPolicy();
 423                     if (p != null) {
 424                         return p.getPermissions(codesource);
 425                     } else {
 426                         return new Permissions();
 427                     }
 428                 }
 429             });
 430 
 431         /*
 432          * Anyone can connect to the registry and the registry can connect
 433          * to and possibly download stubs from anywhere. Downloaded stubs and
 434          * related classes themselves are more tightly limited by RMI.
 435          */
 436         perms.add(new SocketPermission("*", "connect,accept"));
 437         perms.add(new SocketPermission("localhost:"+port, "listen,accept"));
 438 
 439         perms.add(new RuntimePermission("accessClassInPackage.sun.jvmstat.*"));
 440         perms.add(new RuntimePermission("accessClassInPackage.sun.jvm.hotspot.*"));
 441 
 442         perms.add(new FilePermission("<<ALL FILES>>", "read"));
 443 
 444         /*
 445          * Create an AccessControlContext that consists of a single
 446          * protection domain with only the permissions calculated above.
 447          */
 448         ProtectionDomain pd = new ProtectionDomain(
 449             new CodeSource(null,
 450                 (java.security.cert.Certificate[]) null), perms);
 451         return new AccessControlContext(new ProtectionDomain[] { pd });
 452     }
 453 }