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