Intercept File read to allow only classes from specific packages to read a specific file

50 views Asked by At

I have a usecase where I want to allow only classes from certain packages to read from a directory of files. This is achievable through grant policy in SecurityManager using a file like

grant codeBase "file:/path/to/application.jar" {
    permission java.io.FilePermission "/path/to/directory/*", "read,write";
    permission java.lang.RuntimePermission "accessClassInPackage.allowed.package";
};

but given that SecurityManager is deprecated, I was thinking if there is a way to achieve the same using byte-buddy.

Edit 1

I wrote this FileInterceptor to intercept all file reads. I am trying to do it at a BufferedReader level

public class FileInterceptor {

    public static void premain(String agentArgs, Instrumentation instrumentation) throws IOException {

        System.out.println("Inside premain");
        File temp = Files.createTempDirectory("tmp").toFile();
        Map<TypeDescription.ForLoadedType, byte[]> injectTypes = new HashMap<>();
        injectTypes.put(new TypeDescription.ForLoadedType(BufferedReaderAdvice.class),
            ClassFileLocator.ForClassLoader.read(BufferedReaderAdvice.class));
        ClassInjector.UsingInstrumentation.of(temp,
            ClassInjector.UsingInstrumentation.Target.BOOTSTRAP, instrumentation).inject(injectTypes);

        new AgentBuilder.Default()
            .ignore(ElementMatchers.nameStartsWith("net.bytebuddy."))
            .with(new AgentBuilder.InjectionStrategy.UsingInstrumentation(instrumentation, temp))
            .type(ElementMatchers.named("java.io.BufferedReader"))
            .transform((builder, typeDescription, classLoader, module, protectionDomain) ->
                builder.visit(Advice.to(BufferedReaderAdvice.class).on(
                    ElementMatchers.isConstructor().and(ElementMatchers.any()
                    )))) // Intercept calls to BufferedReader(InputStreamReader)
            .installOn(instrumentation);
    }

    public static class BufferedReaderAdvice {
        @Advice.OnMethodEnter()
        
        public static void enter(@Advice.This BufferedReader bufferedReader, @Advice.Argument(0) Reader fileReader) {
            String path = new File(((InputStreamReader) fileReader).getEncoding()).getAbsolutePath(); // Get the absolute path of the file
            System.out.println("Inside enter");
            if (path.startsWith("/path/") &&
                !bufferedReader.getClass().getPackage().getName().startsWith("com.whitelist")) {
                // Deny access for files in the path of interest but not being accessed by a package of interest
                throw new SecurityException("Access denied");
            }
        }
    }

}

I am trying to intercept calls via Files.readAllLines(path) in another project which uses the above as a javaagent. What I see happening is that the Inside premain gets invoked but the enter method doesn't get invoked. What am I missing in this case?

Edit 2

public static void premain(String agentArgs, Instrumentation instrumentation) throws IOException {

        System.out.println("Inside premain");
        new AgentBuilder.Default().disableClassFormatChanges().with(RETRANSFORMATION).ignore(none())
            .type(ElementMatchers.named("java.io.BufferedReader"))
            .transform((builder, typeDescription, classLoader, module, protectionDomain) ->
                builder.visit(Advice.to(BufferedReaderAdvice.class).on(isConstructor())))
            .installOn(instrumentation);
public static class BufferedReaderAdvice {
        @Advice.OnMethodEnter()
        public static void enter(@Advice.Origin Constructor constructor, @Advice.AllArguments Object[] params) {
            System.out.println("Inside enter");
            System.out.println("enter-------" + constructor.getDeclaringClass());
            String callingPackage = constructor.getDeclaringClass().getPackage().getName();
            InputStreamReader streamReader = (InputStreamReader) params[0];
            String path = new File(streamReader.getEncoding()).getAbsolutePath(); // Get the absolute path of the file
            System.out.println("callingPackage-->" + callingPackage);
            System.out.println("path-->" + path);
            for (Object param:params) {
                System.out.println(param);
            }
        }
    }

Calling code:

public void readFile(String filePath) {
        System.out.println("Class:"+getClass().getProtectionDomain().getCodeSource().getLocation());
        Path path = Paths.get(filePath);
        try {
            List<String> lines = Files.readAllLines(path);
            System.out.println(lines);
        } catch (IOException ex) {
            // handle exception...
        }
    }
0

There are 0 answers