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 26package sun.rmi.registry; 27 28import java.io.ObjectInputFilter; 29import java.nio.file.Path; 30import java.nio.file.Paths; 31import java.rmi.server.LogStream; 32import java.security.PrivilegedAction; 33import java.security.Security; 34import java.util.ArrayList; 35import java.util.Enumeration; 36import java.util.Hashtable; 37import java.util.List; 38import java.util.MissingResourceException; 39import java.util.ResourceBundle; 40import java.io.File; 41import java.io.FilePermission; 42import java.io.IOException; 43import java.net.*; 44import java.rmi.*; 45import java.rmi.server.ObjID; 46import java.rmi.server.ServerNotActiveException; 47import java.rmi.registry.Registry; 48import java.rmi.server.RMIClientSocketFactory; 49import java.rmi.server.RMIServerSocketFactory; 50import java.security.AccessControlContext; 51import java.security.AccessController; 52import java.security.CodeSource; 53import java.security.Policy; 54import java.security.PrivilegedActionException; 55import java.security.PrivilegedExceptionAction; 56import java.security.PermissionCollection; 57import java.security.Permissions; 58import java.security.ProtectionDomain; 59import java.text.MessageFormat; 60 61import sun.rmi.runtime.Log; 62import sun.rmi.server.UnicastRef; 63import sun.rmi.server.UnicastServerRef; 64import sun.rmi.server.UnicastServerRef2; 65import 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 */ 85public 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 = 10000; 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") 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 = ObjectInputFilter.Config.createFilter(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 */ 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 */ 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, RegistryImpl::registryFilter)); 181 } 182 } 183 184 /** 185 * Construct a new RegistryImpl on the specified port. 186 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 372 public static ObjID getID() { 373 return id; 374 } 375 376 /** 377 * Retrieves text resources from the locale-specific properties file. 378 */ 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 */ 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 */ 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 if (filterInfo.arrayLength() >= 0 && filterInfo.arrayLength() > REGISTRY_MAX_ARRAY_SIZE) { 455 return ObjectInputFilter.Status.REJECTED; 456 } 457 do { 458 // Arrays are allowed depending on the component type 459 clazz = clazz.getComponentType(); 460 } while (clazz.isArray()); 461 } 462 if (clazz.isPrimitive()) { 463 // Arrays of primitives are allowed 464 return ObjectInputFilter.Status.ALLOWED; 465 } 466 if (String.class == clazz 467 || java.lang.Number.class.isAssignableFrom(clazz) 468 || Remote.class.isAssignableFrom(clazz) 469 || java.lang.reflect.Proxy.class.isAssignableFrom(clazz) 470 || UnicastRef.class.isAssignableFrom(clazz) 471 || RMIClientSocketFactory.class.isAssignableFrom(clazz) 472 || RMIServerSocketFactory.class.isAssignableFrom(clazz) 473 || java.rmi.activation.ActivationID.class.isAssignableFrom(clazz) 474 || java.rmi.server.UID.class.isAssignableFrom(clazz)) { 475 return ObjectInputFilter.Status.ALLOWED; 476 } else { 477 return ObjectInputFilter.Status.REJECTED; 478 } 479 } 480 return ObjectInputFilter.Status.UNDECIDED; 481 } 482 483 /** 484 * Return a new RegistryImpl on the requested port and export it to serve 485 * registry requests. A classloader is initialized from the system property 486 * "env.class.path" and a security manager is set unless one is already set. 487 * <p> 488 * The returned Registry is fully functional within the current process and 489 * is usable for internal and testing purposes. 490 * 491 * @param regPort port on which the rmiregistry accepts requests; 492 * if 0, an implementation specific port is assigned 493 * @return a RegistryImpl instance 494 * @exception RemoteException If remote operation failed. 495 * @since 9 496 */ 497 public static RegistryImpl createRegistry(int regPort) throws RemoteException { 498 // Create and install the security manager if one is not installed 499 // already. 500 if (System.getSecurityManager() == null) { 501 System.setSecurityManager(new SecurityManager()); 502 } 503 504 /* 505 * Fix bugid 4147561: When JDK tools are executed, the value of 506 * the CLASSPATH environment variable for the shell in which they 507 * were invoked is no longer incorporated into the application 508 * class path; CLASSPATH's only effect is to be the value of the 509 * system property "env.class.path". To preserve the previous 510 * (JDK1.1 and JDK1.2beta3) behavior of this tool, however, its 511 * CLASSPATH should still be considered when resolving classes 512 * being unmarshalled. To effect this old behavior, a class 513 * loader that loads from the file path specified in the 514 * "env.class.path" property is created and set to be the context 515 * class loader before the remote object is exported. 516 */ 517 String envcp = System.getProperty("env.class.path"); 518 if (envcp == null) { 519 envcp = "."; // preserve old default behavior 520 } 521 URL[] urls = pathToURLs(envcp); 522 ClassLoader cl = new URLClassLoader(urls); 523 524 /* 525 * Fix bugid 4242317: Classes defined by this class loader should 526 * be annotated with the value of the "java.rmi.server.codebase" 527 * property, not the "file:" URLs for the CLASSPATH elements. 528 */ 529 sun.rmi.server.LoaderHandler.registerCodebaseLoader(cl); 530 531 Thread.currentThread().setContextClassLoader(cl); 532 533 RegistryImpl registryImpl = null; 534 try { 535 registryImpl = AccessController.doPrivileged( 536 new PrivilegedExceptionAction<RegistryImpl>() { 537 public RegistryImpl run() throws RemoteException { 538 return new RegistryImpl(regPort); 539 } 540 }, getAccessControlContext(regPort)); 541 } catch (PrivilegedActionException ex) { 542 throw (RemoteException) ex.getException(); 543 } 544 545 return registryImpl; 546 } 547 548 /** 549 * Main program to start a registry. <br> 550 * The port number can be specified on the command line. 551 */ 552 public static void main(String args[]) 553 { 554 try { 555 final int regPort = (args.length >= 1) ? Integer.parseInt(args[0]) 556 : Registry.REGISTRY_PORT; 557 558 registry = createRegistry(regPort); 559 560 // prevent registry from exiting 561 while (true) { 562 try { 563 Thread.sleep(Long.MAX_VALUE); 564 } catch (InterruptedException e) { 565 } 566 } 567 } catch (NumberFormatException e) { 568 System.err.println(MessageFormat.format( 569 getTextResource("rmiregistry.port.badnumber"), 570 args[0] )); 571 System.err.println(MessageFormat.format( 572 getTextResource("rmiregistry.usage"), 573 "rmiregistry" )); 574 } catch (Exception e) { 575 e.printStackTrace(); 576 } 577 System.exit(1); 578 } 579 580 /** 581 * Generates an AccessControlContext with minimal permissions. 582 * The approach used here is taken from the similar method 583 * getAccessControlContext() in the sun.applet.AppletPanel class. 584 */ 585 private static AccessControlContext getAccessControlContext(int port) { 586 // begin with permissions granted to all code in current policy 587 PermissionCollection perms = AccessController.doPrivileged( 588 new java.security.PrivilegedAction<PermissionCollection>() { 589 public PermissionCollection run() { 590 CodeSource codesource = new CodeSource(null, 591 (java.security.cert.Certificate[]) null); 592 Policy p = java.security.Policy.getPolicy(); 593 if (p != null) { 594 return p.getPermissions(codesource); 595 } else { 596 return new Permissions(); 597 } 598 } 599 }); 600 601 /* 602 * Anyone can connect to the registry and the registry can connect 603 * to and possibly download stubs from anywhere. Downloaded stubs and 604 * related classes themselves are more tightly limited by RMI. 605 */ 606 perms.add(new SocketPermission("*", "connect,accept")); 607 perms.add(new SocketPermission("localhost:"+port, "listen,accept")); 608 609 perms.add(new RuntimePermission("accessClassInPackage.sun.jvmstat.*")); 610 perms.add(new RuntimePermission("accessClassInPackage.sun.jvm.hotspot.*")); 611 612 perms.add(new FilePermission("<<ALL FILES>>", "read")); 613 614 /* 615 * Create an AccessControlContext that consists of a single 616 * protection domain with only the permissions calculated above. 617 */ 618 ProtectionDomain pd = new ProtectionDomain( 619 new CodeSource(null, 620 (java.security.cert.Certificate[]) null), perms); 621 return new AccessControlContext(new ProtectionDomain[] { pd }); 622 } 623} 624