This is an automated email from the ASF dual-hosted git repository.
amichai pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/aries-rsa.git
The following commit(s) were added to refs/heads/master by this push:
new fbe05d71 ARIES-2123 Disallow invoking arbitrary methods on the service
object
fbe05d71 is described below
commit fbe05d71d0e332164a5719c7bef28111a78391e5
Author: Amichai Rothman <[email protected]>
AuthorDate: Tue Jun 25 12:48:22 2024 +0300
ARIES-2123 Disallow invoking arbitrary methods on the service object
---
.../aries/rsa/provider/tcp/MethodInvoker.java | 44 ++++++++++++++++++++--
.../apache/aries/rsa/provider/tcp/TcpProvider.java | 2 +-
.../apache/aries/rsa/provider/tcp/TcpServer.java | 5 ++-
.../aries/rsa/provider/tcp/MethodInvokerTest.java | 18 ++++++++-
4 files changed, 62 insertions(+), 7 deletions(-)
diff --git
a/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/MethodInvoker.java
b/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/MethodInvoker.java
index c5395ff8..12a933eb 100644
---
a/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/MethodInvoker.java
+++
b/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/MethodInvoker.java
@@ -19,8 +19,10 @@
package org.apache.aries.rsa.provider.tcp;
import java.lang.reflect.Method;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.List;
/**
* Reflectively invokes the methods of a service object
@@ -30,9 +32,11 @@ public class MethodInvoker {
private HashMap<Object, Object> primTypes;
private Object service;
+ private Class<?>[] interfaces;
- public MethodInvoker(Object service) {
+ public MethodInvoker(Object service, Class<?>[] interfaces) {
this.service = service;
+ this.interfaces = interfaces;
this.primTypes = new HashMap<>();
this.primTypes.put(Byte.TYPE, Byte.class);
this.primTypes.put(Short.TYPE, Short.class);
@@ -44,6 +48,26 @@ public class MethodInvoker {
this.primTypes.put(Character.TYPE, Character.class);
}
+ protected MethodInvoker(Object service) {
+ this(service, (Class<?>[])null);
+ }
+
+ public MethodInvoker(Object service, List<String> interfaces) {
+ this(service, loadClasses(interfaces,
service.getClass().getClassLoader()));
+ }
+
+ private static Class<?>[] loadClasses(List<String> interfaces, ClassLoader
loader) {
+ List<Class<?>> classes = new ArrayList<>();
+ for (String iface : interfaces) {
+ try {
+ classes.add(loader.loadClass(iface));
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return classes.toArray(new Class<?>[0]);
+ }
+
public Object getService() {
return service;
}
@@ -54,9 +78,23 @@ public class MethodInvoker {
return method.invoke(service, args);
}
+ private Method verifyInterface(Method method) throws NoSuchMethodException
{
+ if (interfaces == null) // if there are no restrictions, allow
everything
+ return method;
+ // for security, make sure it's in the exported interfaces
+ for (Class<?> iface : interfaces) {
+ try {
+ iface.getMethod(method.getName(), method.getParameterTypes());
+ return method;
+ } catch (NoSuchMethodException ignore) {}
+ }
+ throw new NoSuchMethodException("method is not in any interface");
+ }
+
private Method getMethod(String methodName, Class<?>[] parameterTypesAr)
throws NoSuchMethodException {
try {
- return service.getClass().getMethod(methodName, parameterTypesAr);
+ Method method = service.getClass().getMethod(methodName,
parameterTypesAr);
+ return verifyInterface(method);
} catch (NoSuchMethodException e) {
Method[] methods = service.getClass().getMethods();
for (Method method : methods) {
@@ -64,7 +102,7 @@ public class MethodInvoker {
continue;
}
if (allParamsMatch(method.getParameterTypes(),
parameterTypesAr)) {
- return method;
+ return verifyInterface(method);
}
}
throw new NoSuchMethodException(String.format("No method found
that matches name %s, types %s",
diff --git
a/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/TcpProvider.java
b/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/TcpProvider.java
index 6ec64414..08357ba8 100644
---
a/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/TcpProvider.java
+++
b/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/TcpProvider.java
@@ -110,7 +110,7 @@ public class TcpProvider implements DistributionProvider {
if (endpoint.getNumThreads() > server.getNumThreads()) {
server.setNumThreads(endpoint.getNumThreads());
}
- server.addService(endpoint.description().getId(), serviceO);
+ server.addService(endpoint.description(), serviceO);
}
private synchronized void removeServer(TcpEndpoint endpoint) {
diff --git
a/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/TcpServer.java
b/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/TcpServer.java
index c5657ce5..aa62166e 100644
---
a/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/TcpServer.java
+++
b/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/TcpServer.java
@@ -31,6 +31,7 @@ import java.util.concurrent.*;
import org.apache.aries.rsa.provider.tcp.ser.BasicObjectOutputStream;
import org.apache.aries.rsa.provider.tcp.ser.BasicObjectInputStream;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
import org.osgi.util.promise.Promise;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -65,8 +66,8 @@ public class TcpServer implements Closeable, Runnable {
return this.serverSocket.getLocalPort();
}
- public void addService(String endpointId, Object service) {
- invokers.put(endpointId, new MethodInvoker(service));
+ public void addService(EndpointDescription endpoint, Object service) {
+ invokers.put(endpoint.getId(), new MethodInvoker(service,
endpoint.getInterfaces()));
}
public void removeService(String endpointId) {
diff --git
a/provider/tcp/src/test/java/org/apache/aries/rsa/provider/tcp/MethodInvokerTest.java
b/provider/tcp/src/test/java/org/apache/aries/rsa/provider/tcp/MethodInvokerTest.java
index 7dcb296f..98ad1d5c 100644
---
a/provider/tcp/src/test/java/org/apache/aries/rsa/provider/tcp/MethodInvokerTest.java
+++
b/provider/tcp/src/test/java/org/apache/aries/rsa/provider/tcp/MethodInvokerTest.java
@@ -21,6 +21,7 @@ package org.apache.aries.rsa.provider.tcp;
import org.junit.Test;
import java.lang.reflect.InvocationTargetException;
+import java.util.function.Function;
import static org.junit.Assert.*;
@@ -112,4 +113,19 @@ public class MethodInvokerTest {
assertThrows(NoSuchMethodException.class, () -> invoker.invoke("f",
new Object[] { 1, 2 }));
}
-}
\ No newline at end of file
+ @Test
+ public void testNonIntefaceMethod() throws Exception {
+ class Tester implements Function<String, Integer> {
+ public int f(String s) { return s == null ? 0 : s.length(); }
+
+ @Override
+ public Integer apply(String s) { return f(s); }
+ }
+ MethodInvoker invoker = new MethodInvoker(new Tester(),
Tester.class.getInterfaces());
+ // invoke method in interface
+ assertEquals(0, invoker.invoke("apply", new Object[]{ "" }));
+ // invoke method not in interface
+ assertThrows(NoSuchMethodException.class, () -> invoker.invoke("f",
new Object[]{ "" }));
+ }
+
+}