Skip to content

Commit 4ddf455

Browse files
smowtonTimo Mueller
authored andcommitted
Merged simplified query
1 parent 75f6ec1 commit 4ddf455

File tree

9 files changed

+93
-135
lines changed

9 files changed

+93
-135
lines changed
Lines changed: 30 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
22
* @name InsecureRmiJmxAuthenticationEnvironment
3-
* @description Creating a JMX/RMI server could lead to code execution through insecure deserialization if its environment does not restrict the types that can be deserialized.
4-
* @kind path-problem
3+
* @description This query detects if a JMX/RMI server is created with a potentially dangerous environment, which could lead to code execution through insecure deserialization.
4+
* @kind problem
55
* @problem.severity error
66
* @tags security
77
* external/cwe/cwe-665
@@ -11,11 +11,7 @@
1111

1212
import java
1313
import semmle.code.java.dataflow.DataFlow
14-
import semmle.code.java.dataflow.DataFlow2
1514
import semmle.code.java.Maps
16-
import DataFlow::PathGraph
17-
import semmle.code.java.dataflow.NullGuards
18-
import semmle.code.java.dataflow.Nullness
1915

2016
/** Holds if `constructor` instantiates an RMI or JMX server. */
2117
predicate isRmiOrJmxServerCreateConstructor(Constructor constructor) {
@@ -31,17 +27,23 @@ predicate isRmiOrJmxServerCreateMethod(Method method) {
3127
}
3228

3329
/**
34-
* Models flow from `new HashMap<>()` to a
35-
* `map.put("jmx.remote.rmi.server.credential.types", value)` call.
30+
* Models flow from the qualifier of a
31+
* `map.put("jmx.remote.rmi.server.credential.types", value)` call
32+
* to an RMI or JMX initialisation call.
3633
*/
37-
class MapToPutCredentialstypeConfiguration extends DataFlow2::Configuration {
38-
MapToPutCredentialstypeConfiguration() { this = "MapToPutCredentialstypeConfiguration" }
34+
class SafeFlow extends DataFlow::Configuration {
35+
SafeFlow() { this = "MapToPutCredentialstypeConfiguration" }
3936

40-
override predicate isSource(DataFlow2::Node source) {
41-
source.asExpr().(ClassInstanceExpr).getConstructedType() instanceof MapType
42-
}
37+
override predicate isSource(DataFlow::Node source) { putsCredentialtypesKey(source.asExpr()) }
4338

44-
override predicate isSink(DataFlow2::Node sink) { putsCredentialtypesKey(sink.asExpr()) }
39+
override predicate isSink(DataFlow::Node sink) {
40+
exists(Call c |
41+
isRmiOrJmxServerCreateConstructor(c.getCallee()) or
42+
isRmiOrJmxServerCreateMethod(c.getCallee())
43+
|
44+
sink.asExpr() = c.getArgument(1)
45+
)
46+
}
4547

4648
/**
4749
* Holds if a `put` call on `qualifier` puts a key match
@@ -53,8 +55,11 @@ class MapToPutCredentialstypeConfiguration extends DataFlow2::Configuration {
5355
"jmx.remote.rmi.server.credential.types" or
5456
put.getKey().(CompileTimeConstantExpr).getStringValue() =
5557
"jmx.remote.rmi.server.credentials.filter.pattern" or
56-
put.getKey().toString() = "RMIConnectorServer.CREDENTIAL_TYPES" or // This can probably be solved more nicely
57-
put.getKey().toString() = "RMIConnectorServer.CREDENTIALS_FILTER_PATTERN" // This can probably be solved more nicely
58+
put.getKey()
59+
.(FieldAccess)
60+
.getField()
61+
.hasQualifiedName("javax.management.remote.rmi", "RMIConnectorServer",
62+
["CREDENTIAL_TYPES", "CREDENTIALS_FILTER_PATTERN", "SERIAL_FILTER_PATTERN"])
5863
|
5964
put.getQualifier() = qualifier and
6065
put.getMethod().(MapMethod).getReceiverKeyType() instanceof TypeString and
@@ -63,90 +68,21 @@ class MapToPutCredentialstypeConfiguration extends DataFlow2::Configuration {
6368
}
6469
}
6570

66-
/**
67-
* Models flow from `new HashMap<>()` variable which is later used as environment during
68-
* a JMX/RMI server initialization with `newJMXConnectorServer(...)` or `RMIConnectorServer(...)`
69-
*/
70-
class MapToRmiServerInitConfiguration extends DataFlow::Configuration {
71-
MapToRmiServerInitConfiguration() { this = "MapToRmiServerInitConfiguration" }
72-
73-
override predicate isSource(DataFlow::Node source) {
74-
source.asExpr().(ClassInstanceExpr).getConstructedType() instanceof MapType
75-
}
76-
77-
override predicate isSink(DataFlow::Node sink) {
78-
exists(ConstructorCall ccall |
79-
sink.asExpr() = ccall.getArgument(1) and
80-
isRmiOrJmxServerCreateConstructor(ccall.getConstructor())
81-
)
82-
or
83-
exists(MethodAccess ma |
84-
sink.asExpr() = ma.getArgument(1) and
85-
isRmiOrJmxServerCreateMethod(ma.getMethod())
86-
)
87-
}
88-
}
89-
90-
/** Models if any JMX/RMI server are initialized with a null environment */
91-
class FlowServerInitializedWithNullEnv extends DataFlow::Configuration {
92-
FlowServerInitializedWithNullEnv() { this = "FlowServerInitializedWithNullEnv" }
93-
94-
override predicate isSource(DataFlow::Node source) { any() }
95-
96-
override predicate isSink(DataFlow::Node sink) {
97-
exists(ConstructorCall ccall |
98-
sink.asExpr() = ccall and
99-
isRmiOrJmxServerCreateConstructor(ccall.getConstructor()) and
100-
ccall.getArgument(1) = alwaysNullExpr()
101-
)
102-
or
103-
exists(MethodAccess ma |
104-
sink.asExpr() = ma and
105-
isRmiOrJmxServerCreateMethod(ma.getMethod()) and
106-
ma.getArgument(1) = alwaysNullExpr()
107-
)
108-
}
109-
}
110-
111-
/** Holds if within the passed PathNode a "jmx.remote.rmi.server.credential.types" is set. */
112-
predicate mapFlowContainsCredentialtype(DataFlow::PathNode source) {
113-
exists(MapToPutCredentialstypeConfiguration conf | conf.hasFlow(source.getNode(), _))
114-
}
115-
11671
/** Gets a string describing why the application is vulnerable, depending on if the vulnerability is present due to a) a null environment b) an insecurely set environment map */
117-
bindingset[source]
118-
string getRmiResult(DataFlow::PathNode source) {
72+
string getRmiResult(Expr e) {
11973
// We got a Map so we have a source and a sink node
120-
if source.getNode().getType() instanceof MapType
74+
if e instanceof NullLiteral
12175
then
12276
result =
123-
"RMI/JMX server initialized with insecure environment $@. The $@ never restricts accepted client objects to 'java.lang.String'. This exposes to deserialization attacks against the RMI authentication method."
77+
"RMI/JMX server initialized with a null environment. Missing type restriction in RMI authentication method exposes the application to deserialization attacks."
12478
else
125-
// The environment is not a map so we most likely have a "null" environment and therefore only a sink
12679
result =
127-
"RMI/JMX server initialized with 'null' environment $@. Missing type restriction in RMI authentication method exposes the application to deserialization attacks."
80+
"RMI/JMX server initialized with insecure environment $@, which never restricts accepted client objects to 'java.lang.String'. This exposes to deserialization attacks against the RMI authentication method."
12881
}
12982

130-
/** Holds for any map flow paths with **no** jmx.remote.rmi.server.credential.types set */
131-
predicate hasVulnerableMapFlow(DataFlow::PathNode source, DataFlow::PathNode sink) {
132-
exists(MapToRmiServerInitConfiguration dataflow |
133-
dataflow.hasFlowPath(source, sink) and
134-
not mapFlowContainsCredentialtype(source)
135-
)
136-
}
137-
138-
from
139-
DataFlow::PathNode source, DataFlow::PathNode sink,
140-
FlowServerInitializedWithNullEnv initNullDataflow
83+
from Call c, Expr envArg
14184
where
142-
// Check if server is created with null env
143-
initNullDataflow.hasFlowPath(source, sink)
144-
or
145-
/*
146-
* The map created by `new HashMap<String, Object>()` has to a) flow to the sink and
147-
* b) there must not exist a (different) sink that would put `"jmx.remote.rmi.server.credential.types"` into `source`.
148-
*/
149-
150-
hasVulnerableMapFlow(source, sink)
151-
select sink.getNode(), source, sink, getRmiResult(source), sink.getNode(), "here", source.getNode(),
152-
"source environment 'Map'"
85+
(isRmiOrJmxServerCreateConstructor(c.getCallee()) or isRmiOrJmxServerCreateMethod(c.getCallee())) and
86+
envArg = c.getArgument(1) and
87+
not any(SafeFlow conf).hasFlowToExpr(envArg)
88+
select c, getRmiResult(envArg), envArg, envArg.toString()
Lines changed: 4 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,4 @@
1-
edges
2-
| ../../../stubs/javax-management-remote-rmi-0.0.1/javax/management/remote/rmi/RMIConnectorServer.java:26:12:26:29 | this <constr(this)> [post update] : RMIConnectorServer | InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) |
3-
| InsecureRmiJmxEnvironmentConfiguration.java:25:31:25:45 | new HashMap<String,Object>(...) : HashMap | InsecureRmiJmxEnvironmentConfiguration.java:27:34:27:36 | env |
4-
| InsecureRmiJmxEnvironmentConfiguration.java:33:31:33:45 | new HashMap<String,Object>(...) : HashMap | InsecureRmiJmxEnvironmentConfiguration.java:35:59:35:61 | env |
5-
| InsecureRmiJmxEnvironmentConfiguration.java:40:31:40:45 | new HashMap<String,Object>(...) : HashMap | InsecureRmiJmxEnvironmentConfiguration.java:44:59:44:61 | env |
6-
| InsecureRmiJmxEnvironmentConfiguration.java:49:31:49:45 | new HashMap<String,Object>(...) : HashMap | InsecureRmiJmxEnvironmentConfiguration.java:53:34:53:36 | env |
7-
| InsecureRmiJmxEnvironmentConfiguration.java:58:31:58:45 | new HashMap<String,Object>(...) : HashMap | InsecureRmiJmxEnvironmentConfiguration.java:62:59:62:61 | env |
8-
| InsecureRmiJmxEnvironmentConfiguration.java:67:31:67:45 | new HashMap<String,Object>(...) : HashMap | InsecureRmiJmxEnvironmentConfiguration.java:71:34:71:36 | env |
9-
| InsecureRmiJmxEnvironmentConfiguration.java:76:31:76:45 | new HashMap<String,Object>(...) : HashMap | InsecureRmiJmxEnvironmentConfiguration.java:80:59:80:61 | env |
10-
| InsecureRmiJmxEnvironmentConfiguration.java:85:31:85:45 | new HashMap<String,Object>(...) : HashMap | InsecureRmiJmxEnvironmentConfiguration.java:89:34:89:36 | env |
11-
nodes
12-
| ../../../stubs/javax-management-remote-rmi-0.0.1/javax/management/remote/rmi/RMIConnectorServer.java:26:12:26:29 | this <constr(this)> [post update] : RMIConnectorServer | semmle.label | this <constr(this)> [post update] : RMIConnectorServer |
13-
| InsecureRmiJmxEnvironmentConfiguration.java:13:5:13:69 | newJMXConnectorServer(...) | semmle.label | newJMXConnectorServer(...) |
14-
| InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) | semmle.label | new RMIConnectorServer(...) |
15-
| InsecureRmiJmxEnvironmentConfiguration.java:25:31:25:45 | new HashMap<String,Object>(...) : HashMap | semmle.label | new HashMap<String,Object>(...) : HashMap |
16-
| InsecureRmiJmxEnvironmentConfiguration.java:27:34:27:36 | env | semmle.label | env |
17-
| InsecureRmiJmxEnvironmentConfiguration.java:33:31:33:45 | new HashMap<String,Object>(...) : HashMap | semmle.label | new HashMap<String,Object>(...) : HashMap |
18-
| InsecureRmiJmxEnvironmentConfiguration.java:35:59:35:61 | env | semmle.label | env |
19-
| InsecureRmiJmxEnvironmentConfiguration.java:40:31:40:45 | new HashMap<String,Object>(...) : HashMap | semmle.label | new HashMap<String,Object>(...) : HashMap |
20-
| InsecureRmiJmxEnvironmentConfiguration.java:44:59:44:61 | env | semmle.label | env |
21-
| InsecureRmiJmxEnvironmentConfiguration.java:49:31:49:45 | new HashMap<String,Object>(...) : HashMap | semmle.label | new HashMap<String,Object>(...) : HashMap |
22-
| InsecureRmiJmxEnvironmentConfiguration.java:53:34:53:36 | env | semmle.label | env |
23-
| InsecureRmiJmxEnvironmentConfiguration.java:58:31:58:45 | new HashMap<String,Object>(...) : HashMap | semmle.label | new HashMap<String,Object>(...) : HashMap |
24-
| InsecureRmiJmxEnvironmentConfiguration.java:62:59:62:61 | env | semmle.label | env |
25-
| InsecureRmiJmxEnvironmentConfiguration.java:67:31:67:45 | new HashMap<String,Object>(...) : HashMap | semmle.label | new HashMap<String,Object>(...) : HashMap |
26-
| InsecureRmiJmxEnvironmentConfiguration.java:71:34:71:36 | env | semmle.label | env |
27-
| InsecureRmiJmxEnvironmentConfiguration.java:76:31:76:45 | new HashMap<String,Object>(...) : HashMap | semmle.label | new HashMap<String,Object>(...) : HashMap |
28-
| InsecureRmiJmxEnvironmentConfiguration.java:80:59:80:61 | env | semmle.label | env |
29-
| InsecureRmiJmxEnvironmentConfiguration.java:85:31:85:45 | new HashMap<String,Object>(...) : HashMap | semmle.label | new HashMap<String,Object>(...) : HashMap |
30-
| InsecureRmiJmxEnvironmentConfiguration.java:89:34:89:36 | env | semmle.label | env |
31-
#select
32-
| InsecureRmiJmxEnvironmentConfiguration.java:13:5:13:69 | newJMXConnectorServer(...) | InsecureRmiJmxEnvironmentConfiguration.java:13:5:13:69 | newJMXConnectorServer(...) | InsecureRmiJmxEnvironmentConfiguration.java:13:5:13:69 | newJMXConnectorServer(...) | RMI/JMX server initialized with 'null' environment $@. Missing type restriction in RMI authentication method exposes the application to deserialization attacks. | InsecureRmiJmxEnvironmentConfiguration.java:13:5:13:69 | newJMXConnectorServer(...) | here | InsecureRmiJmxEnvironmentConfiguration.java:13:5:13:69 | newJMXConnectorServer(...) | source environment 'Map' |
33-
| InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) | ../../../stubs/javax-management-remote-rmi-0.0.1/javax/management/remote/rmi/RMIConnectorServer.java:26:12:26:29 | this <constr(this)> [post update] : RMIConnectorServer | InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) | RMI/JMX server initialized with 'null' environment $@. Missing type restriction in RMI authentication method exposes the application to deserialization attacks. | InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) | here | ../../../stubs/javax-management-remote-rmi-0.0.1/javax/management/remote/rmi/RMIConnectorServer.java:26:12:26:29 | this <constr(this)> [post update] | source environment 'Map' |
34-
| InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) | InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) | InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) | RMI/JMX server initialized with 'null' environment $@. Missing type restriction in RMI authentication method exposes the application to deserialization attacks. | InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) | here | InsecureRmiJmxEnvironmentConfiguration.java:18:5:18:50 | new RMIConnectorServer(...) | source environment 'Map' |
35-
| InsecureRmiJmxEnvironmentConfiguration.java:27:34:27:36 | env | InsecureRmiJmxEnvironmentConfiguration.java:25:31:25:45 | new HashMap<String,Object>(...) : HashMap | InsecureRmiJmxEnvironmentConfiguration.java:27:34:27:36 | env | RMI/JMX server initialized with insecure environment $@. The $@ never restricts accepted client objects to 'java.lang.String'. This exposes to deserialization attacks against the RMI authentication method. | InsecureRmiJmxEnvironmentConfiguration.java:27:34:27:36 | env | here | InsecureRmiJmxEnvironmentConfiguration.java:25:31:25:45 | new HashMap<String,Object>(...) | source environment 'Map' |
36-
| InsecureRmiJmxEnvironmentConfiguration.java:35:59:35:61 | env | InsecureRmiJmxEnvironmentConfiguration.java:33:31:33:45 | new HashMap<String,Object>(...) : HashMap | InsecureRmiJmxEnvironmentConfiguration.java:35:59:35:61 | env | RMI/JMX server initialized with insecure environment $@. The $@ never restricts accepted client objects to 'java.lang.String'. This exposes to deserialization attacks against the RMI authentication method. | InsecureRmiJmxEnvironmentConfiguration.java:35:59:35:61 | env | here | InsecureRmiJmxEnvironmentConfiguration.java:33:31:33:45 | new HashMap<String,Object>(...) | source environment 'Map' |
1+
| InsecureRmiJmxEnvironmentConfiguration.java:12:5:12:69 | newJMXConnectorServer(...) | RMI/JMX server initialized with a null environment. Missing type restriction in RMI authentication method exposes the application to deserialization attacks. | InsecureRmiJmxEnvironmentConfiguration.java:12:59:12:62 | null | null |
2+
| InsecureRmiJmxEnvironmentConfiguration.java:17:5:17:50 | new RMIConnectorServer(...) | RMI/JMX server initialized with a null environment. Missing type restriction in RMI authentication method exposes the application to deserialization attacks. | InsecureRmiJmxEnvironmentConfiguration.java:17:34:17:37 | null | null |
3+
| InsecureRmiJmxEnvironmentConfiguration.java:25:5:25:49 | new RMIConnectorServer(...) | RMI/JMX server initialized with insecure environment $@, which never restricts accepted client objects to 'java.lang.String'. This exposes to deserialization attacks against the RMI authentication method. | InsecureRmiJmxEnvironmentConfiguration.java:25:34:25:36 | env | env |
4+
| InsecureRmiJmxEnvironmentConfiguration.java:33:5:33:68 | newJMXConnectorServer(...) | RMI/JMX server initialized with insecure environment $@, which never restricts accepted client objects to 'java.lang.String'. This exposes to deserialization attacks against the RMI authentication method. | InsecureRmiJmxEnvironmentConfiguration.java:33:59:33:61 | env | env |

java/ql/test/experimental/query-tests/security/CWE-665/InsecureRmiJmxEnvironmentConfiguration.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import java.io.IOException;
22
import javax.management.remote.JMXConnectorServerFactory;
3-
43
import javax.management.remote.rmi.RMIConnectorServer;
54

65
import java.util.HashMap;
@@ -16,7 +15,6 @@ public void initInsecureJmxDueToNullEnv() throws IOException {
1615
public void initInsecureRmiDueToNullEnv() throws IOException {
1716
// Bad initializing env (arg1) with null
1817
new RMIConnectorServer(null, null, null, null);
19-
2018
}
2119

2220
public void initInsecureRmiDueToMissingEnvKeyValue() throws IOException {
@@ -71,7 +69,7 @@ public void secureeRmiConnectorServerConstants1() throws IOException {
7169
new RMIConnectorServer(null, env, null, null);
7270
}
7371

74-
public void secureeJmxConnectorServerConstants2() throws IOException {
72+
public void secureJmxConnectorServerConstants2() throws IOException {
7573
// Good
7674
Map<String, Object> env = new HashMap<>();
7775
env.put("jmx.remote.x.daemon", "true");
@@ -80,7 +78,7 @@ public void secureeJmxConnectorServerConstants2() throws IOException {
8078
JMXConnectorServerFactory.newJMXConnectorServer(null, env, null);
8179
}
8280

83-
public void secureeRmiConnectorServerConstants2() throws IOException {
81+
public void secureRmiConnectorServerConstants2() throws IOException {
8482
// Good
8583
Map<String, Object> env = new HashMap<>();
8684
env.put("jmx.remote.x.daemon", "true");
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
//semmle-extractor-options: --javac-args -source 11 -target 11 -cp ${testdir}/../../../../experimental/stubs/javax-management-remote-rmi-0.0.1
1+
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/rmi-remote-0.0.0
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
This is a workaround for a bug in which the extractor can't resolve type javax.management.remote.rmi.RMIConnectorServer even though it has been part of the JDK since Java 5
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package javax.management.remote.rmi;
2+
3+
import java.rmi.Remote;
4+
import java.io.Closeable;
5+
6+
interface RMIConnection extends Closeable, Remote { }
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package javax.management.remote.rmi;
2+
3+
import java.util.Map;
4+
5+
import javax.management.remote.JMXConnectorServer;
6+
import javax.management.remote.JMXConnector;
7+
import javax.management.remote.JMXServiceURL;
8+
import javax.management.remote.MBeanServerForwarder;
9+
import javax.management.MBeanServer;
10+
11+
// Note this is a partial stub sufficient to the needs of tests for CWE-665
12+
public class RMIConnectorServer extends JMXConnectorServer {
13+
14+
public RMIConnectorServer(JMXServiceURL url, Map<String,?> environment) { }
15+
public RMIConnectorServer(JMXServiceURL url, Map<String,?> environment, MBeanServer mbeanServer) { }
16+
public RMIConnectorServer(JMXServiceURL url, Map<String,?> environment, RMIServerImpl rmiServerImpl, MBeanServer mbeanServer) { }
17+
18+
public static String CREDENTIAL_TYPES = "";
19+
public static String CREDENTIALS_FILTER_PATTERN = "";
20+
public static String JNDI_REBIND_ATTRIBUTE = "";
21+
public static String RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE = "";
22+
public static String RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE = "";
23+
public static String SERIAL_FILTER_PATTERN = "";
24+
25+
public Map<String,?> getAttributes() { return null; }
26+
public JMXServiceURL getAddress() { return null; }
27+
public String[] getConnectionIds() { return null; }
28+
public boolean isActive() { return true; }
29+
public void setMBeanServerForwarder(MBeanServerForwarder mbsf) { }
30+
public void start() { }
31+
public void stop() { }
32+
public JMXConnector toJMXConnector(Map<String,?> env) { return null; }
33+
34+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package javax.management.remote.rmi;
2+
3+
interface RMIServer { }
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package javax.management.remote.rmi;
2+
3+
import java.io.Closeable;
4+
import java.rmi.Remote;
5+
6+
public class RMIServerImpl implements Closeable, RMIServer {
7+
8+
public void close() { }
9+
public String getVersion() { return null; }
10+
public RMIConnection newClient(Object credentials) { return null; }
11+
12+
}

0 commit comments

Comments
 (0)