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 NotBoundException 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 NotBoundException 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 = jdk.internal.loader.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 }