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[]{ "" }));
+    }
+
+}

Reply via email to