1 /* 2 * Copyright (c) 2002, 2016, 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 javax.management.remote.rmi; 27 28 import java.io.IOException; 29 import java.io.ObjectInputFilter; 30 import java.rmi.NoSuchObjectException; 31 import java.rmi.Remote; 32 import java.rmi.RemoteException; 33 import java.rmi.server.RMIClientSocketFactory; 34 import java.rmi.server.RMIServerSocketFactory; 35 import java.rmi.server.UnicastRemoteObject; 36 import java.rmi.server.RemoteObject; 37 import java.util.Map; 38 import java.util.Collections; 39 import javax.security.auth.Subject; 40 41 import com.sun.jmx.remote.internal.rmi.RMIExporter; 42 import com.sun.jmx.remote.util.EnvHelp; 43 import java.util.Arrays; 44 import java.util.Set; 45 import java.util.stream.Collectors; 46 import sun.reflect.misc.ReflectUtil; 47 import sun.rmi.server.UnicastServerRef; 48 import sun.rmi.server.UnicastServerRef2; 49 import sun.rmi.transport.LiveRef; 50 51 /** 52 * <p>An {@link RMIServer} object that is exported through JRMP and that 53 * creates client connections as RMI objects exported through JRMP. 54 * User code does not usually reference this class directly.</p> 55 * 56 * @see RMIServerImpl 57 * 58 * @since 1.5 59 */ 60 public class RMIJRMPServerImpl extends RMIServerImpl { 61 62 /** 63 * <p>Creates a new {@link RMIServer} object that will be exported 64 * on the given port using the given socket factories.</p> 65 * 66 * @param port the port on which this object and the {@link 67 * RMIConnectionImpl} objects it creates will be exported. Can be 68 * zero, to indicate any available port. 69 * 70 * @param csf the client socket factory for the created RMI 71 * objects. Can be null. 72 * 73 * @param ssf the server socket factory for the created RMI 74 * objects. Can be null. 75 * 76 * @param env the environment map. Can be null. 77 * 78 * @exception IOException if the {@link RMIServer} object 79 * cannot be created. 80 * 81 * @exception IllegalArgumentException if <code>port</code> is 82 * negative. 83 */ RMIJRMPServerImpl(int port, RMIClientSocketFactory csf, RMIServerSocketFactory ssf, Map<String,?> env)84 public RMIJRMPServerImpl(int port, 85 RMIClientSocketFactory csf, 86 RMIServerSocketFactory ssf, 87 Map<String,?> env) 88 throws IOException { 89 90 super(env); 91 92 if (port < 0) 93 throw new IllegalArgumentException("Negative port: " + port); 94 95 this.port = port; 96 this.csf = csf; 97 this.ssf = ssf; 98 this.env = (env == null) ? Collections.<String, Object>emptyMap() : env; 99 100 // This attribute was represented by RMIConnectorServer.CREDENTIALS_TYPES. 101 // This attribute is superceded by 102 // RMIConnectorServer.CREDENTIALS_FILTER_PATTERN. 103 // Retaining this for backward compatibility. 104 String[] credentialsTypes 105 = (String[]) this.env.get("jmx.remote.rmi.server.credential.types"); 106 107 String credentialsFilter 108 = (String) this.env.get(RMIConnectorServer.CREDENTIALS_FILTER_PATTERN); 109 110 // It is impossible for both attributes to be specified 111 if(credentialsTypes != null && credentialsFilter != null) 112 throw new IllegalArgumentException("Cannot specify both \"" 113 + "jmx.remote.rmi.server.credential.types" + "\" and \"" 114 + RMIConnectorServer.CREDENTIALS_FILTER_PATTERN + "\""); 115 else if(credentialsFilter != null){ 116 cFilter = ObjectInputFilter.Config.createFilter(credentialsFilter); 117 allowedTypes = null; 118 } 119 else if (credentialsTypes != null) { 120 allowedTypes = Arrays.stream(credentialsTypes).filter( 121 s -> s!= null).collect(Collectors.toSet()); 122 allowedTypes.stream().forEach(ReflectUtil::checkPackageAccess); 123 cFilter = this::newClientCheckInput; 124 } else { 125 allowedTypes = null; 126 cFilter = null; 127 } 128 129 String userJmxFilter = 130 (String) this.env.get(RMIConnectorServer.SERIAL_FILTER_PATTERN); 131 if(userJmxFilter != null && !userJmxFilter.isEmpty()) 132 jmxRmiFilter = ObjectInputFilter.Config.createFilter(userJmxFilter); 133 else 134 jmxRmiFilter = null; 135 } 136 export()137 protected void export() throws IOException { 138 export(this, cFilter); 139 } 140 export(Remote obj, ObjectInputFilter typeFilter)141 private void export(Remote obj, ObjectInputFilter typeFilter) throws RemoteException { 142 final RMIExporter exporter = 143 (RMIExporter) env.get(RMIExporter.EXPORTER_ATTRIBUTE); 144 final boolean daemon = EnvHelp.isServerDaemon(env); 145 146 if (daemon && exporter != null) { 147 throw new IllegalArgumentException("If "+EnvHelp.JMX_SERVER_DAEMON+ 148 " is specified as true, "+RMIExporter.EXPORTER_ATTRIBUTE+ 149 " cannot be used to specify an exporter!"); 150 } 151 152 if (exporter != null) { 153 exporter.exportObject(obj, port, csf, ssf, typeFilter); 154 } else { 155 if (csf == null && ssf == null) { 156 new UnicastServerRef(new LiveRef(port), typeFilter).exportObject(obj, null, daemon); 157 } else { 158 new UnicastServerRef2(port, csf, ssf, typeFilter).exportObject(obj, null, daemon); 159 } 160 } 161 } 162 unexport(Remote obj, boolean force)163 private void unexport(Remote obj, boolean force) 164 throws NoSuchObjectException { 165 RMIExporter exporter = 166 (RMIExporter) env.get(RMIExporter.EXPORTER_ATTRIBUTE); 167 if (exporter == null) 168 UnicastRemoteObject.unexportObject(obj, force); 169 else 170 exporter.unexportObject(obj, force); 171 } 172 getProtocol()173 protected String getProtocol() { 174 return "rmi"; 175 } 176 177 /** 178 * <p>Returns a serializable stub for this {@link RMIServer} object.</p> 179 * 180 * @return a serializable stub. 181 * 182 * @exception IOException if the stub cannot be obtained - e.g the 183 * RMIJRMPServerImpl has not been exported yet. 184 */ toStub()185 public Remote toStub() throws IOException { 186 return RemoteObject.toStub(this); 187 } 188 189 /** 190 * <p>Creates a new client connection as an RMI object exported 191 * through JRMP. The port and socket factories for the new 192 * {@link RMIConnection} object are the ones supplied 193 * to the <code>RMIJRMPServerImpl</code> constructor.</p> 194 * 195 * @param connectionId the ID of the new connection. Every 196 * connection opened by this connector server will have a 197 * different id. The behavior is unspecified if this parameter is 198 * null. 199 * 200 * @param subject the authenticated subject. Can be null. 201 * 202 * @return the newly-created <code>RMIConnection</code>. 203 * 204 * @exception IOException if the new {@link RMIConnection} 205 * object cannot be created or exported. 206 */ makeClient(String connectionId, Subject subject)207 protected RMIConnection makeClient(String connectionId, Subject subject) 208 throws IOException { 209 210 if (connectionId == null) 211 throw new NullPointerException("Null connectionId"); 212 213 RMIConnection client = 214 new RMIConnectionImpl(this, connectionId, getDefaultClassLoader(), 215 subject, env); 216 export(client, jmxRmiFilter); 217 return client; 218 } 219 closeClient(RMIConnection client)220 protected void closeClient(RMIConnection client) throws IOException { 221 unexport(client, true); 222 } 223 224 /** 225 * <p>Called by {@link #close()} to close the connector server by 226 * unexporting this object. After returning from this method, the 227 * connector server must not accept any new connections.</p> 228 * 229 * @exception IOException if the attempt to close the connector 230 * server failed. 231 */ closeServer()232 protected void closeServer() throws IOException { 233 unexport(this, true); 234 } 235 236 /** 237 * Check that a type in the remote invocation of {@link RMIServerImpl#newClient} 238 * is one of the {@code allowedTypes}. 239 * 240 * @param clazz the class; may be null 241 * @param size the size for arrays, otherwise is 0 242 * @param nObjectRefs the current number of object references 243 * @param depth the current depth 244 * @param streamBytes the current number of bytes consumed 245 * @return {@code ObjectInputFilter.Status.ALLOWED} if the class is allowed, 246 * otherwise {@code ObjectInputFilter.Status.REJECTED} 247 */ newClientCheckInput(ObjectInputFilter.FilterInfo filterInfo)248 ObjectInputFilter.Status newClientCheckInput(ObjectInputFilter.FilterInfo filterInfo) { 249 ObjectInputFilter.Status status = ObjectInputFilter.Status.UNDECIDED; 250 if (allowedTypes != null && filterInfo.serialClass() != null) { 251 // If enabled, check type 252 String type = filterInfo.serialClass().getName(); 253 if (allowedTypes.contains(type)) 254 status = ObjectInputFilter.Status.ALLOWED; 255 else 256 status = ObjectInputFilter.Status.REJECTED; 257 } 258 return status; 259 } 260 261 private final int port; 262 private final RMIClientSocketFactory csf; 263 private final RMIServerSocketFactory ssf; 264 private final Map<String, ?> env; 265 private final Set<String> allowedTypes; 266 private final ObjectInputFilter jmxRmiFilter; 267 private final ObjectInputFilter cFilter; 268 } 269