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