Related
I'm using a Java Agent (Agent.class) to transform a method in a program (Program.class) in a way that includes a call to the Agent class.
public Program.getMultiplier()F:
ALOAD 1
ALOAD 2
FDIV
+ INVOKESTATIC Agent.getCustomMultiplier(F)F
FRETURN
I've inspected the class loaders and their parents of both Agent and Program classes, and their hierarchy looks like this:
Agent.class: AppClassLoader <- PlatformClassLoader <- null
Program.class: URLClassLoader <- PlatformClassLoader <- null
When the Program executes the added INVOKESTATIC instruction, it throws a ClassNotFoundException -- it cannot find the Agent class as it was loaded by a different class loader.
As a temporary solution, I've tried forcing AppClassLoader to become a parent of URLClassLoader with reflection, which works in older Java versions but has been removed since Java 12.
Is there a more reliable way to make sure my Agent class is visible from any class loader?
You can add classes to the bootstrap class loader using appendToBootstrapClassLoaderSearch. This makes the classes of the specified jar file available to all classes whose defining class loader follows the standard delegation pattern.
But this requires the classes to be packaged in a jar file. When you specify the Agent’s own jar file, you have to be aware that classes loaded through the bootstrap loader are distinct from the classes loaded through the app loader, even when they originate from the same jar file. Further, the classes loaded by the bootstrap loader must not have dependencies to classes loaded by by other class loaders.
If your getCustomMultiplier method is supposed to interact with the running Agent, you have to separate the agent and the class containing this method.
Have your Agent listen to the creation of new ClassLoaders and then attach instances of them to the new ClassLoaders.
This way you preserve your "Agent listens to ClassLoader" interface, even if it now extends beyond the one platform class loader you expected the Agent to listen to.
You may be able to do something specific that works for URLClassLoader, but not all classes are loaded by an instance of URLClassLoader. Any OSGi project won't, most web servers also use their own classloaders in order to support hot reload, etc.
As far as I know there's no way to just casually update some 'global parent of all classloaders' or inject one; there's no such parent, and even if there was, a classloader is free to ignore its parent entirely.
Therefore the general answer is: No, you can't do that.
But, let's get our hacking hats on!
You're an agent already. One of the things you get to do as agent is to 'witness' classes as they are being loaded. Just invoke .addTransformer on the instance of Instrumentation you get in your agentmain and register one.
When you notice the Program class being loaded, do the following:
Take the bytecode and toss it through ASM, BCEL, Bytecode Buddy, or any other java 'class file reader/transformer' framework.
Also open up a class from within your agent's code (I wouldn't use Agent itself, I'd make a class called ProgramAddonMethods or whatnot as a container - everything inside is for the program to use / for your agent to 'inject' into that program.
Add every static member in ProgramAddonMethods directly to Program. As you do so, modify the typename on all accesses (both INVOKESTATIC and the read/write field opcodes) where the etypename is ProgramAddonMethods and make it the fully qualified name of the targeted class instead.
inject the INVOKESTATIC as you already do, but, rewrite it so that it's going to its own class, as you just copied all the static methods and fields over there.
Then return the bytecode of that modified class from your transformer.
This 100% guarantees you cannot possibly run into any module or classpath boundary issues and it will work with any classloader abstraction, guaranteed, but there are some caveats:
Just don't attempt to futz with instance anything. Make it all static methods and fields. You can make fake instance fields using an IdentityHashMap if you must (e.g. a static IdentityHashMap<Foo, String> names; is effectively identical to adding private String name; to the Foo class.. except it's a bit slower of course; presumably as you're already in a mess o reflection that's acceptable here).
Your code has to be 'dependency free'. It cannot rely on anything else, no libraries other than java.*, not even a helper class. This idea quickly runs out of steam if the job you're injecting becomes complicated. If you must, make a classloader for your own agent jar using the appropriate 'thread-safely initialize it only once' guards, and have that load in a bundle that does have the benefit of allowing dependencies.
This is all highly complicated stuff but you appear to have already worked out how to inject INVOKESTATIC calls, so, I think you know how to do this.
This is precisely what lombok does to 'patch' some methods in eclipse to ensure that things like save actions, auto-formatting, and syntax highlighting don't break - lombok injects knowledge of generated notes where appropriate and does it in this exact manner because eclipse uses a classloader platform called Equinox which makes any other solution problematic. You can look at it for inspiration or guidelines, though it's not particularly well documented. You're looking in particular at:
The lombok.eclipse.agent package in the eclipseAgent source root.
The lombok.patcher project which is lombok's only actual dependency, in particular the lombok.patcher.PatchScript.transplantMethod method.
Note that the next method may also interest you: lombok.patcher's 'insert' doesn't move the method - it injects the body of the method directly in there (it 'inlines'). This requires some serious finagling of the stack and is only advised for extremely simple one-liner-esque methods, and probably is excessive and unneccessary firepower for this problem.
DISCLAIMER: I wrote most of that.
So I have a bit of code, that creates an instance of a class.
Class<?> c = Class.forName("MyClass");
Constructor<?> cons = c.getConstructor();
cons.setAccessible(true);
Object instance = cons.newInstance();
Now I want to set some restrictions to that instance. When I call:
instance.doSomething();
I want to set restrictions for that bit of code (of the instance). So the methods called from that isntance can not do something fishy (System calls, File operations...).
I have tried to set a security manager, but that restricts all of the code (I still want to read/write files for the rest of my code).
Is it possible to restrict only certain objects?
TL;DR: Code
The question is essentially "How do I invoke a method on a particular instance, with privileges lower than normal?". There are three requirements here:
Code is to be authorized on a per-instance basis. An instance is privileged by default.
An instance may be selectively blacklisted, i.e., it may be accorded lower privileges than it normally would have been, for the duration of a method invocation that it receives.
Blacklisting must propagate to code executed on the receiver's behalf, specifically any objects of the same type that it interacts with, itself included; otherwise, if, say, the receiver were in turn to call
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
this.doSomethingElse();
return null;
});
doSomethingElse() would escape the sandbox.
All three are problematic:
The first one is not really1 achievable, because it presupposes that the runtime maintain—and expose—information about the instances (rather than merely the classes) on threads' execution stacks, which it does not2.
The second and third are only achievable as long as any blacklisted code does not assert its own (default, class-based) privileges via AccessController.doPrivileged(...), which, by design, it may at any time choose to.
Is there an alternative?
Well, how far are you willing to go? Modify AccessController / AccessControlContext internals? Or worse yet, internals of the VM? Or perhaps provide your own SecurityManager that reimplements the aforementioned components' functionality from scratch, in a way that satisfies your requirements? If the answer to all is "no", then I fear that your options are limited.
As an aside, you should ideally be able to make a binary choice when asked "Can or cannot this particular code, i.e. class, be entrusted with the particular privileges?", for this would tremendously simplify3 things. Unfortunately you cannot; and, to make matters worse, neither can you, presumably, modify the implementation of the class such that all of its instances can either be considered—with regards to a specific set of privileges—trustworthy or not, nor do you wish to simply mark the class, and therefore all of its instances, as untrusted (which I do believe you should!) and live with it.
Moving on to the proposed workaround. To overcome the shortcomings listed earlier, the original question will be rephrased as follows: "How do I invoke a method with elevated privileges accorded to the method receiver's ProtectionDomain?" I am going to answer this derivative question instead, suggesting, in contrast to the original one, that:
Code is to be authorized by the ProtectionDomain of its class, as is normally the case. Code is sandboxed by default.
Code may be selectively whitelisted, for the duration of a method invocation under a particular thread.
Whitelisting must propagate4 to code of the same class called by the receiver.
The revised requirements will be satisfied by means of a custom ClassLoader and DomainCombiner. The purpose of the first is to assign a distinct ProtectionDomain per class5; the other's is to temporarily replace the domains of individual classes within the current AccessControlContext for "on-demand whitelisting" purposes. The SecurityManager is additionally extended to prevent thread creation by unprivileged code4.
Note: I relocated the code to this gist to keep the post's length below the limit.
Standard disclaimer: Proof-of-concept code—take with several tablespoons of salt!
Running the example
Compile and deploy the code as suggested by the example policy configuration file, i.e., there should be two6 unrelated classpath entries (e.g. sibling directories at the filesystem level)—one for classes of the com.example.trusted package, and another for com.example.untrusted.Nasty.
Ensure also that you have replaced the policy configuration with the example one, and have modified the paths therein as appropriate.
Lastly run (after having appropriately modified the classpath entries, of course):
java -cp /path/to/classpath-entry-for-trusted-code:/path/to/classpath-entry-for-untrusted-code -Djava.system.class.loader=com.example.trusted.DiscriminatingClasspathClassLoader com.example.trusted.Main
The first call to the untrusted method should hopefully succeed, and the second fail.
1 It would perhaps be possible for instances of a specially crafted class (having, e.g., a domain of their own, assigned by some trusted component) to exercise their own privileges themselves (which does not hold true in this case, since you have no control over the implementation of instance's class, it appears). Nevertheless, this would still not satisfy the second and third requirement.
2 Recall that, under the default SecurityManager, a Permission is granted when all ProtectionDomains—to which normally classes, rather than instances, are mapped—of the thread's AccessControlContext imply that permission.
3 You would then simply have to grant permissions at the policy level, if you deemed the class trustworthy, or otherwise grant nothing at all, rather than have to worry about permissions per instance per security context and whatnot.
4 This is a hard decision: If whitelisting did not affect subsequent callees of the same type, the instance would not be able to call any privilege-requiring methods on itself. Now that it does, on the other hand, any other instance of the same type, that the original whitelisted method receiver interacts with, become privileged too! Thus you must ensure that the receiver does not call any "untrusted" instances of its own kind. It is for the same reason a bad idea to allow the receiver to spawn any threads.
5 As opposed to the strategy employed by the default application ClassLoader, which is to group all classes that reside under the same classpath entry within a single ProtectionDomain.
6 The reason for the inconvenience is that the ProtectionDomain, which our custom application ClassLoader's class gets mapped to by its parent, has a CodeSource implying all CodeSources referring to files under the loader's classpath entry. So far so good. Now, when asked to load a class, our loader attempts to discern between system/extension classes (loading of which it delegates to its parent) and application classes, by testing whether the .class file is located below JAVA_HOME. Naturally, to be allowed to do so, it requires read access to the filesystem subtree beneath JAVA_HOME. Unfortunately, granting the corresponding privilege to our loader's (notoriously broad) domain, implicitly grants the privilege to the domains of all other classes residing beneath the loader's classpath entry, including untrusted ones, as well. And that should hopefully explain why classpath entry-level isolation between trusted and untrusted code is necessary. There are of course workarounds, as always; e.g. mandating that trusted code be additionally signed in order to accrue any privileges; or perhaps using a more flexible URL scheme for code source identification, and/or altering code source implication semantics.
Further reading:
Default Policy Implementation and Policy File Syntax
API for Privileged Blocks
Secure Coding Guidelines for Java SE - §9 - Access Control
Troubleshooting Security
Historical note: Originally this answer proposed a nearly identical solution that abusedrelied on JAAS's SubjectDomainCombiner, rather than a custom one, for dynamic privilege modification. A "special" Principal would be attached to specific domains, which would then accrue additional Permissions upon evaluation by the Policy, based on their composite CodeSource-Principal identity.
Now I know I can mark a method as 'no modifier' so that its only accessible by Class & Package.
That is not what I need in this case though. What I need is this:
Methods on Class "Secure.java" can only be accessed from other Classes in the same JAR file.
AND (this is extra)
When call is made into a secure method, the call stack does not go back to a none secure class & then back in again. For example:
This is good:
com.nonsecure.ClassA.doStuff() calls
com.nonsecure.ClassB.doStuff() calls
com.secure.Secure.doStuff() calls
com.secure.SecureB.doStuff()
This is bad:
com.nonsecure.ClassA.doStuff() calls
com.nonsecure.ClassB.doStuff() calls
com.secure.Secure.doStuff() calls
com.nonsecure.ClassC.doStuff() calls [BAD: we went outside the classes in the secure JAR!]
com.secure.SecureB.doStuff()
Now I think I can do this via a little manual work:
private void checkSecurity()
{
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
for (StackTraceElement stackTraceElement : stackTraceElements)
{
// TODO: Add a little magic to check that we've not stepped outside the secure packages.
if (!stackTraceElement.getClassName().startsWith("com.secure"))
{
throw new SecurityException("Woop!");
}
}
}
QUESTION: This feels like there should be something that Java provides to help me out here?
I've read about
AccessController.doPrivileged(new PrivilegedExceptionAction<String>()
But seems to only be about accessing resources/network connections etc.
Not about accessing the call stack to ensure some sort of package protection.
Notes:
I am using Spring 3
The JAR file containing the secure code is signed
with a certificate.
Access to the secure class will only be from
within the JAR, direct access from outside the JAR must not be
allowed.
Your title says 'from same package' which provides the hint. Put the class into the same package as the classes that may access it; don't make it public, so it is only accessible to classes in that package; sign the JAR file; and seal the package.
You are basically looking for access restrictions based on the container (jar) of your classes. Java does not provide such control on its own (at least not easily).
OSGI specifications are closer to the access control you want to achieve. In essence, OSGI do support and enforce access restrictions and rules based on the jar files (which it calls bundles).
You say you are using Spring: maybe have a look on these articles from JavaWorld here and here to check in which extent OSGI can help you.
In the end I used the checkSecurity() method above.
I didn't find a Java 1.6 feature to help me out.
I am playing around with the java .policy file and was wondering how I could go about doing something like preventing calls to java.util.Date(), as an example.
I just want to get a better sense of the .policy file works and how it can be used to sandbox code.
You'll be out of luck there I'm afraid.
As Paŭlo Ebermann says, package.access can block out package hierarchies. You can be more precise about this with a custom SecurityManager, which is usually a damn good indication you are doing something really dodgy.
In general you can make a ClassLoader that doesn't always delegate to its parent. Technically against the current Java SE spec, although the Java EE spec encourages it. You could block out java.util.Date. It's still accessible through reflection if any other class references it, or you can get an instance of it. You could block out the transitive closure of uses of Date, including those that in some way return a Date. However, to complete the scheme with your minimal date you'd have to load a java.util.Date in your class loader, which you can't along with all other java.* classes.
So, err, replace the java.util.Date class in rt.jar (possibly using a Java Agent), and substitute in any class you don't want to restrict new Date() with new Date(System.currentTimeMillis()).
(Btw, +1 to anything that reduces the dependency on System.currentTimeMillis() and other magic methods.)
To restrict access to certain packages, you have actually to change not the .policy file, but the security.properties. There is an entry package.access=... which lists the packages for which RuntimePermissions are needed. So, you can't restrict specifically the access to a single class, only to a whole package (including subpackages, if needed), i.e. java.util.
(You can alternatively access this by the Security.?etProperty methods.)
If you did this, you later can add the right RuntimePermission to the Policy to let the "good" code use it.
I think quite a good part of the JRE would cease working if you restrict access to java.util, so better try another class for testing.
The way the sandbox mostly works is that there are calls from classes that do security-sensitive stuff to the current SecurityManager to check whether or not such a call should succeed. Since the Date class isn't perceived to be security-sensitive, no such calls exist in it's code and that's why - as explained by Tom and Paulo - it is very difficult to restrict it.
For example, in contrast: File operations are perceived to be security sensitive and that's why the File class has calls to the SecurityManager. As an example the delete method:
public boolean delete() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkDelete(path);
}
return fs.delete(this);
}
And thanks to the SecurityManager check in the File class you can restrict File delete operations in the .policy file with more ease.
Is there a feasible way to get my own code run whenever any class is loaded in Java, without forcing the user explicitly and manually loading all classes with a custom classloader?
Without going too much into the details, whenever a class implementing a certain interface read its annotation that links it with another class, and give the pair to a third class.
Edit: Heck, I'll go to details: I'm doing an event handling library. What I'm doing is having the client code do their own Listener / Event pairs, which need to be registered with my library as a pair. (hm, that wasn't that long after all).
Further Edit: Currently the client code needs to register the pair of classes/interfaces manually, which works pretty well. My intent is to automate this away, and I thought that linking the two classes with annotations would help. Next, I want to get rid of the client code needing to keeping the list of registrations up to date always.
PS: The static block won't do, since my interface is bundled into a library, and the client code will create further interfaces. Thus, abstract classes won't do either, since it must be an interface.
If you want to base the behavior on an interface, you could use a static initializer in that interface.
public interface Foo{
static{
// do initializing here
}
}
I'm not saying it's good practice, but it will definitely initialize the first time one of the implementing classes is loaded.
Update: static blocks in interfaces are illegal. Use abstract classes instead!
Reference:
Initializers (Sun Java Tutorial)
But if I understand you right, you want the initialization to happen once per implementing class. That will be tricky. You definitely can't do that with an interface based solution. You could do it with an abstract base class that has a dynamic initializer (or constructor), that checks whether the requested mapping already exists and adds it if it doesn't, but doing such things in constructors is quite a hack.
I'd say you cleanest options are either to generate Code at build time (through annotation processing with apt or through bytecode analysis with a tool like asm) or to use an agent at class load time to dynamically create the mapping.
Ah, more input. Very good. So clients use your library and provide mappings based on annotations. Then I'd say your library should provide an initializer method, where client code can register classes. Something like this:
YourLibrary.getInstance().registerMappedClasses(
CustomClass1.class,
CustomClass2.class,
CustomClass3.class,
CustomClass4.class
)
Or, even better, a package scanning mechanism (example code to implement this can be found at this question):
YourLibrary.getInstance().registerMappedClassesFromPackages(
"com.mycompany.myclientcode.abc",
"com.mycompany.myclientcode.def"
)
Anyway, there is basically no way to avoid having your clients do that kind of work, because you can't control their build process nor their classloader for them (but you could of course provide guides for classloader or build configuration).
If you want some piece of code to be run on any class loading, you should:
overwrite the ClassLoader, adding your own custom code at the loadClass methods (don't forget forwarding to the parent ClassLoader after or before your custom code).
Define this custom ClassLoader as the default for your system (here you got how to do it: How to set my custom class loader to be the default?).
Run and check it.
Depending on what kind of environment you are, there are chances that not all the classes be loaded trouugh your custom ClassLoader (some utility packages use their own CL, some Java EE containers handle some spacific areas with specific classLoaders, etc.), but it's a kind of aproximation to what you are asking.