Code source grants are policies governing the rights of code running in a JVM (Java Virtual Machine). In this article, I talk about 3 common implementation issues when dealing with code source grants in a Weblogic/OPSS (Oracle Platform Security Services) environment.
Code requiring grants are one of the protection mechanisms offered in the Java platform since Java SE 1.2. Java SE 1.2 improved the previous versions by allowing a fine-grained authorization model, opposed to the all-or-nothing model in Java SE 1.0 and 1.1.
In a nutshell, if you want to only allow code from a given jar file to read/write to a file in tmp folder, you can specify the following in one of the java.policy files:
1: grant codeBase “file://MyApp/MyJar.jar”
2: {
3: permission java.io.FilePermission “/tmp/*” “read/write”;
4: }
The code base grant along with the permission define what is known as a protection domain.
Then when you run the JVM with the security manager enabled (-Djava.security.manager), any code attempting to read/write to /tmp folder will be checked if it has been granted the permission.
Fair enough.
OPSS does not use the standard java.policy. Instead, it implements JAAS by looking at the configured policy store repository (system-jazn-data.xml by default) as the policy store for code source grants (as well as principal-based grants). Here’s a snippet:
1: <jazn-data>
3: <applications>
4: <application>
5: <name>MyApp</name>
6: <jazn-policy>
7: <grant>
8: <grantee>
9: <principals>
10: <principal>
11: <class>oracle.security.jps.internal.core.principals.JpsAuthenticatedRoleImpl</class>
12: <name>authenticated-role</name>
13: </principal>
14: </principals>
15: </grantee>
16: <permissions>
17: <permission>
18: <class>oracle.adf.share.security.authorization.RegionPermission</class>
19: <name>trunk.pagedefs.SearchPageDef</name>
20: <actions>view</actions>
21: </permission>
22: </permissions>
23: </grant>
24: </jazn-policy>
25: </application>
26: </applications>
27: </policy-store>
28: <jazn-policy>
29: <grant>
30: <grantee>
31: <codesource>
32: <url>file:${domain.home}/servers/${weblogic.Name}/tmp/_WL_user/MyApp/-</url>
33: </codesource>
34: </grantee>
35: <permissions>
36: <permission>
37: <class>oracle.security.jps.service.policystore.PolicyStoreAccessPermission</class>
38: <name>context=APPLICATION,name=MyApp</name>
39: <actions>getApplicationPolicy</actions>
40: </permission>
41: </permissions>
42: </grant>
43: </jazn-policy>
44: </jazn-data>
Lines 28-43 define the code source grant in OPSS standards. Notice it actually resembles a plain java code grant pretty well. This specific grant allows any code deployed under file:${domain.home}/servers/${weblogic.Name}/tmp/_WL_user/MyApp to getApplicationPolicy in the MyApp application policy context.
This leads us to issue #1.
Code source grants must be defined separately from principal-based grants.
In system-jazn-data.xml, principal-based grants are application-scoped, meaning they’re meaningful for a specific application and are defined within the <application> element. Code source grants are referred to as system grants and are defined in the outermost <jazn-policy> element, outside of any <application> element. OPSS is very strict when it comes to policies in LDAP. It won’t accept system grants in application scope.
Observe Weblogic server staging mode.
Within a grant definition, the codesource url element refers to its physical location in the file system. Weblogic server deploys bits into different locations depending on its staging mode. In stage mode, application bits are copied from the AdminServer to a specific directory in the managed servers. By default it is ${domain.home}/servers/${weblogic.Name}/tmp/_WL_user/<application-name>/ (or whatever specified by the staging directory name attribute). In nostage mode, applications are, by default, laid down at ${domain.name}/servers/AdminServer/upload/<application-name>/.
And if you use the embedded Weblogic server in JDeveloper, applications are read from another location: ${domain.home}/../o.j2ee/drs/<application-name>
As a result, code source grants in the policy files must be aligned to the server staging mode.
<application-name> is the actual deployed application name.
It’s a good idea to have something in the build system to target the system grants appropriately to your actual testing and production environments.
Know about AccessController.doPrivileged when invoking protected APIs
The JVM security model correctly takes a conservative approach when evaluating code source grants in the sense that it does not allow a piece of code to “do stuff” in behalf of other another piece of code unless explicitly stated by the application developer. Imagine if you had the rights to delete the contents of a folder and did that upon anyone’s request. Bad things would happen. So the JVM enforces the concept of a trusted path, meaning that whenever a secured operation is called, a permission check is made against all protection domains in the call chain in that executing thread. And this means every protection domain in that call chain must be granted the permission in the policy store. Obviously, the correct set of grants can get very tricky, especially if you’re writing code that is invoked as part of some framework that is also invoked off another framework... You can spend a considerable amount of work figuring out the right code source URLs.
However, the JVM gives us the option of calling the secured API inside an AccessController.doPrivileged block. In this way the JVM gets signaled to not check permissions further up in the call chain. In other words, the protection domain containing that block gets entitled to execute the operation on behalf of anyone calling it without the need of granting anyone else. Here’s some sample code:
1: JpsContextFactory f = JpsContextFactory.getContextFactory();
2: JpsContext c = f.getContext();
3: final PolicyStore ps = c.getServiceInstance(PolicyStore.class);
4: final String appId = "MyApp";
5: ApplicationPolicy ap = AccessController.doPrivileged(new
6: PrivilegedExceptionAction<ApplicationPolicy>() {
7: public ApplicationPolicy run() throws PolicyStoreException {
8: return ps.getApplicationPolicy(appId);
9: }
10: }, null);
11: // Continue with the ap object for policy operations.
Lines 5-10 make the getAplicationPolicy method executable by granting only the corresponding protection domain (and nobody else) in the policy store, exactly as in the system-jazn-data.xml shown above.
AccessController.doPrivileged expects either a PrivilegedAction or a PrivilegedExceptionAction type. Both define the run method, where the protected API call is to be made.
It is important that such an approach gets combined with other protection mechanisms, otherwise other code deployed in the container could leverage the privileged code in some “uncool” manner. And if you make that code available to external clients, make sure those interfaces are properly secured (well, this also applies to any sensitive aspect that you expose, regardless of AccessController.doPrivileged usage).