-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Add a policy parser for Java Agent #17753
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
791760a
7ea73eb
2225ecb
14a3b25
21a7eb5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
/* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
* | ||
* The OpenSearch Contributors require contributions made to | ||
* this file be licensed under the Apache-2.0 license or a | ||
* compatible open source license. | ||
* | ||
* Modifications Copyright OpenSearch Contributors. See | ||
* GitHub history for details. | ||
*/ | ||
|
||
apply plugin: 'opensearch.build' | ||
apply plugin: 'opensearch.publish' | ||
|
||
ext { | ||
failOnJavadocWarning = false | ||
} | ||
|
||
base { | ||
archivesName = 'opensearch-agent-policy' | ||
} | ||
|
||
disableTasks('forbiddenApisMain') | ||
|
||
dependencies { | ||
testImplementation(project(":test:framework")) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kumargu sorry I am late - we cannot depend on |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
/* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
* | ||
* The OpenSearch Contributors require contributions made to | ||
* this file be licensed under the Apache-2.0 license or a | ||
* compatible open source license. | ||
*/ | ||
package org.opensearch.secure_sm.policy; | ||
|
||
import java.util.List; | ||
|
||
public record GrantEntry(String codeBase, List<PermissionEntry> permissionEntries) { | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
/* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
* | ||
* The OpenSearch Contributors require contributions made to | ||
* this file be licensed under the Apache-2.0 license or a | ||
* compatible open source license. | ||
*/ | ||
package org.opensearch.secure_sm.policy; | ||
|
||
public record PermissionEntry(String permission, String name, String action) { | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,320 @@ | ||
/* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
* | ||
* The OpenSearch Contributors require contributions made to | ||
* this file be licensed under the Apache-2.0 license or a | ||
* compatible open source license. | ||
*/ | ||
|
||
package org.opensearch.secure_sm.policy; | ||
|
||
import java.io.File; | ||
import java.io.FileInputStream; | ||
import java.io.FilePermission; | ||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.io.InputStreamReader; | ||
import java.net.MalformedURLException; | ||
import java.net.NetPermission; | ||
import java.net.SocketPermission; | ||
import java.net.URI; | ||
import java.net.URISyntaxException; | ||
import java.net.URL; | ||
import java.net.URLDecoder; | ||
import java.nio.charset.StandardCharsets; | ||
import java.security.AllPermission; | ||
import java.security.CodeSource; | ||
import java.security.Permission; | ||
import java.security.PermissionCollection; | ||
import java.security.Permissions; | ||
import java.security.ProtectionDomain; | ||
import java.security.SecurityPermission; | ||
import java.security.cert.Certificate; | ||
import java.util.ArrayList; | ||
import java.util.Enumeration; | ||
import java.util.List; | ||
import java.util.Optional; | ||
import java.util.PropertyPermission; | ||
import java.util.Set; | ||
|
||
@SuppressWarnings("removal") | ||
public class PolicyFile extends java.security.Policy { | ||
public static final Set<String> PERM_CLASSES_TO_SKIP = Set.of( | ||
Check warning on line 42 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
"org.opensearch.secure_sm.ThreadContextPermission", | ||
"org.opensearch.secure_sm.ThreadPermission", | ||
"org.opensearch.SpecialPermission", | ||
"org.bouncycastle.crypto.CryptoServicesPermission", | ||
"org.opensearch.script.ClassPermission", | ||
"javax.security.auth.AuthPermission", | ||
"javax.security.auth.kerberos.ServicePermission" | ||
); | ||
|
||
private final PolicyInfo policyInfo; | ||
private final URL url; | ||
|
||
public PolicyFile(URL url) { | ||
this.url = url; | ||
Check warning on line 56 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
try { | ||
policyInfo = init(url); | ||
} catch (PolicyInitializationException e) { | ||
throw new RuntimeException("Failed to initialize policy file", e); | ||
} | ||
} | ||
Check warning on line 62 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
|
||
private PolicyInfo init(URL policy) throws PolicyInitializationException { | ||
PolicyInfo info = new PolicyInfo(); | ||
try (InputStreamReader reader = new InputStreamReader(getInputStream(policy), StandardCharsets.UTF_8)) { | ||
List<GrantEntry> grantEntries = PolicyParser.read(reader); | ||
Check warning on line 67 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
for (GrantEntry grantEntry : grantEntries) { | ||
addGrantEntry(grantEntry, info); | ||
} | ||
} catch (Exception e) { | ||
throw new PolicyInitializationException("Failed to load policy from: " + policy, e); | ||
} | ||
return info; | ||
Check warning on line 74 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
} | ||
|
||
public static InputStream getInputStream(URL url) throws IOException { | ||
if ("file".equals(url.getProtocol())) { | ||
String path = url.getFile().replace('/', File.separatorChar); | ||
path = URLDecoder.decode(path, StandardCharsets.UTF_8); | ||
return new FileInputStream(path); | ||
Check warning on line 81 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
} else { | ||
return url.openStream(); | ||
Check warning on line 83 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
} | ||
} | ||
|
||
private CodeSource getCodeSource(GrantEntry grantEntry) throws PolicyInitializationException { | ||
try { | ||
Certificate[] certs = null; | ||
Check warning on line 89 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
URL location = (grantEntry.codeBase() != null) ? newURL(grantEntry.codeBase()) : null; | ||
return canonicalizeCodebase(new CodeSource(location, certs)); | ||
} catch (Exception e) { | ||
throw new PolicyInitializationException("Failed to get CodeSource", e); | ||
Check warning on line 93 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
} | ||
} | ||
|
||
private void addGrantEntry(GrantEntry grantEntry, PolicyInfo newInfo) throws PolicyInitializationException { | ||
CodeSource codesource = getCodeSource(grantEntry); | ||
Check warning on line 98 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
if (codesource == null) { | ||
throw new PolicyInitializationException("Null CodeSource for: " + grantEntry.codeBase()); | ||
Check warning on line 100 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
} | ||
|
||
List<Permission> permissions = new ArrayList<>(); | ||
List<PermissionEntry> permissionList = grantEntry.permissionEntries(); | ||
Check warning on line 104 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
for (PermissionEntry pe : permissionList) { | ||
final PermissionEntry expandedEntry = expandPermissionName(pe); | ||
Check warning on line 106 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
try { | ||
Optional<Permission> perm = getInstance(expandedEntry.permission(), expandedEntry.name(), expandedEntry.action()); | ||
Check warning on line 108 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
if (perm.isPresent()) { | ||
permissions.add(perm.get()); | ||
Check warning on line 110 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
} | ||
} catch (ClassNotFoundException e) { | ||
Check warning on line 112 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
// these were mostly custom permission classes added for security | ||
// manager. Since security manager is deprecated, we can skip these | ||
// permissions classes. | ||
if (PERM_CLASSES_TO_SKIP.contains(pe.permission())) { | ||
continue; // skip this permission | ||
Check warning on line 117 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
} | ||
throw new PolicyInitializationException("Permission class not found: " + pe.permission(), e); | ||
} | ||
} | ||
newInfo.policyEntries.add(new PolicyEntry(codesource, permissions)); | ||
} | ||
Check warning on line 123 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
|
||
private static PermissionEntry expandPermissionName(PermissionEntry pe) { | ||
if (pe.name() == null || !pe.name().contains("${{")) { | ||
return pe; | ||
Check warning on line 127 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
} | ||
|
||
int startIndex = 0; | ||
Check warning on line 130 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
int b, e; | ||
StringBuilder sb = new StringBuilder(); | ||
Check warning on line 132 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
|
||
while ((b = pe.name().indexOf("${{", startIndex)) != -1 && (e = pe.name().indexOf("}}", b)) != -1) { | ||
sb.append(pe.name(), startIndex, b); | ||
String value = pe.name().substring(b + 3, e); | ||
sb.append("${{").append(value).append("}}"); | ||
startIndex = e + 2; | ||
} | ||
Check warning on line 139 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
|
||
sb.append(pe.name().substring(startIndex)); | ||
return new PermissionEntry(pe.permission(), sb.toString(), pe.action()); | ||
Check warning on line 142 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
} | ||
|
||
private static final Optional<Permission> getInstance(String type, String name, String actions) throws ClassNotFoundException { | ||
Class<?> pc = Class.forName(type, false, null); | ||
cwperks marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Permission answer = getKnownPermission(pc, name, actions); | ||
Check warning on line 147 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
|
||
return Optional.ofNullable(answer); | ||
Check warning on line 149 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
} | ||
|
||
private static Permission getKnownPermission(Class<?> claz, String name, String actions) { | ||
if (claz.equals(FilePermission.class)) { | ||
return new FilePermission(name, actions); | ||
Check warning on line 154 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
} else if (claz.equals(SocketPermission.class)) { | ||
return new SocketPermission(name, actions); | ||
Check warning on line 156 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
} else if (claz.equals(RuntimePermission.class)) { | ||
return new RuntimePermission(name, actions); | ||
Check warning on line 158 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
} else if (claz.equals(PropertyPermission.class)) { | ||
return new PropertyPermission(name, actions); | ||
Check warning on line 160 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
} else if (claz.equals(NetPermission.class)) { | ||
return new NetPermission(name, actions); | ||
Check warning on line 162 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
} else if (claz.equals(AllPermission.class)) { | ||
return new AllPermission(); | ||
Check warning on line 164 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
} else if (claz.equals(SecurityPermission.class)) { | ||
return new SecurityPermission(name, actions); | ||
Check warning on line 166 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
} else { | ||
return null; | ||
Check warning on line 168 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
} | ||
} | ||
|
||
@Override | ||
public void refresh() { | ||
try { | ||
init(url); | ||
} catch (PolicyInitializationException e) { | ||
throw new RuntimeException("Failed to refresh policy", e); | ||
} | ||
} | ||
Check warning on line 179 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
|
||
@Override | ||
public boolean implies(ProtectionDomain pd, Permission p) { | ||
PermissionCollection pc = getPermissions(pd); | ||
Check warning on line 183 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
return pc != null && pc.implies(p); | ||
} | ||
|
||
@Override | ||
public PermissionCollection getPermissions(ProtectionDomain domain) { | ||
Permissions perms = new Permissions(); | ||
Check warning on line 189 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
if (domain == null) return perms; | ||
|
||
try { | ||
getPermissionsForProtectionDomain(perms, domain); | ||
} catch (PolicyInitializationException e) { | ||
throw new RuntimeException("Failed to get permissions for domain", e); | ||
} | ||
Check warning on line 196 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
|
||
PermissionCollection pc = domain.getPermissions(); | ||
Check warning on line 198 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
if (pc != null) { | ||
synchronized (pc) { | ||
Enumeration<Permission> e = pc.elements(); | ||
Check warning on line 201 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
while (e.hasMoreElements()) { | ||
perms.add(e.nextElement()); | ||
Check warning on line 203 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
} | ||
} | ||
Check warning on line 205 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
} | ||
|
||
return perms; | ||
Check warning on line 208 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
} | ||
|
||
@Override | ||
public PermissionCollection getPermissions(CodeSource codesource) { | ||
if (codesource == null) return new Permissions(); | ||
|
||
Permissions perms = new Permissions(); | ||
Check warning on line 215 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
CodeSource canonicalCodeSource; | ||
|
||
try { | ||
canonicalCodeSource = canonicalizeCodebase(codesource); | ||
} catch (PolicyInitializationException e) { | ||
throw new RuntimeException("Failed to canonicalize CodeSource", e); | ||
} | ||
Check warning on line 222 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
|
||
for (PolicyEntry entry : policyInfo.policyEntries) { | ||
if (entry.codeSource().implies(canonicalCodeSource)) { | ||
for (Permission permission : entry.permissions) { | ||
perms.add(permission); | ||
} | ||
Check warning on line 228 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
} | ||
} | ||
Check warning on line 230 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
|
||
return perms; | ||
Check warning on line 232 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
} | ||
|
||
private void getPermissionsForProtectionDomain(Permissions perms, ProtectionDomain pd) throws PolicyInitializationException { | ||
final CodeSource cs = pd.getCodeSource(); | ||
Check warning on line 236 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
if (cs == null) return; | ||
|
||
CodeSource canonicalCodeSource = canonicalizeCodebase(cs); | ||
Check warning on line 239 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
|
||
for (PolicyEntry entry : policyInfo.policyEntries) { | ||
if (entry.codeSource().implies(canonicalCodeSource)) { | ||
for (Permission permission : entry.permissions) { | ||
perms.add(permission); | ||
} | ||
Check warning on line 245 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
} | ||
} | ||
} | ||
Check warning on line 248 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
|
||
private CodeSource canonicalizeCodebase(CodeSource cs) throws PolicyInitializationException { | ||
URL location = cs.getLocation(); | ||
Check warning on line 251 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
if (location == null) return cs; | ||
|
||
try { | ||
URL canonicalUrl = canonicalizeUrl(location); | ||
return new CodeSource(canonicalUrl, cs.getCertificates()); | ||
} catch (IOException e) { | ||
throw new PolicyInitializationException("Failed to canonicalize CodeSource", e); | ||
Check warning on line 258 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
} | ||
} | ||
|
||
@SuppressWarnings("deprecation") | ||
private URL canonicalizeUrl(URL url) throws IOException { | ||
String protocol = url.getProtocol(); | ||
Check warning on line 264 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
|
||
if ("jar".equals(protocol)) { | ||
String spec = url.getFile(); | ||
int separator = spec.indexOf("!/"); | ||
Check warning on line 268 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
if (separator != -1) { | ||
try { | ||
url = new URL(spec.substring(0, separator)); | ||
cwperks marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} catch (MalformedURLException e) { | ||
throw new IOException("Malformed nested jar URL", e); | ||
} | ||
Check warning on line 274 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
} | ||
} | ||
|
||
if ("file".equals(url.getProtocol())) { | ||
String path = url.getPath(); | ||
path = canonicalizePath(path); | ||
return new File(path).toURI().toURL(); | ||
Check warning on line 281 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
} | ||
|
||
return url; | ||
Check warning on line 284 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
} | ||
|
||
private String canonicalizePath(String path) throws IOException { | ||
if (path.endsWith("*")) { | ||
path = path.substring(0, path.length() - 1); | ||
return new File(path).getCanonicalPath() + "*"; | ||
Check warning on line 290 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
} else { | ||
return new File(path).getCanonicalPath(); | ||
Check warning on line 292 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
} | ||
} | ||
|
||
private record PolicyEntry(CodeSource codeSource, List<Permission> permissions) { | ||
Check warning on line 296 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
@Override | ||
public String toString() { | ||
StringBuilder sb = new StringBuilder(); | ||
sb.append("{").append(codeSource).append("\n"); | ||
Check warning on line 300 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
for (Permission p : permissions) { | ||
sb.append(" ").append(p).append("\n"); | ||
} | ||
sb.append("}\n"); | ||
return sb.toString(); | ||
Check warning on line 305 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
} | ||
} | ||
|
||
private static class PolicyInfo { | ||
final List<PolicyEntry> policyEntries; | ||
|
||
PolicyInfo() { | ||
policyEntries = new ArrayList<>(); | ||
} | ||
Check warning on line 314 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
} | ||
|
||
private static URL newURL(String spec) throws MalformedURLException, URISyntaxException { | ||
return new URI(spec).toURL(); | ||
Check warning on line 318 in libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/policy/PolicyFile.java
|
||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.