Skip to content

Commit 7d13c15

Browse files
Dmitry Ratnikovkares
authored andcommitted
some ~ explicit security manager checks for jruby/openssl
... transplanted from jruby/jruby#853
1 parent b71026d commit 7d13c15

File tree

2 files changed

+253
-0
lines changed

2 files changed

+253
-0
lines changed
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package org.jruby.ext.openssl.security;
2+
3+
import org.jruby.Ruby;
4+
import org.jruby.RubyString;
5+
import org.jruby.runtime.builtin.IRubyObject;
6+
7+
import java.util.ArrayList;
8+
import java.util.List;
9+
10+
public class SecurityManager extends java.lang.SecurityManager {
11+
public static class RubyPermission {
12+
private final IRubyObject lambda;
13+
14+
public RubyPermission(IRubyObject lambda) {
15+
this.lambda = lambda;
16+
}
17+
18+
public boolean matches(java.security.Permission perm) {
19+
Ruby runtime = lambda.getRuntime();
20+
21+
return lambda.callMethod(runtime.getCurrentContext(),
22+
"call",
23+
new IRubyObject[] {
24+
RubyString.newString(runtime, perm.getClass().getSimpleName()),
25+
RubyString.newString(runtime, perm.getName()),
26+
RubyString.newString(runtime, perm.getActions())
27+
}).isTrue();
28+
}
29+
}
30+
31+
/*
32+
public static SecurityManager install() {
33+
SecurityManager manager = new SecurityManager();
34+
35+
System.setSecurityManager(manager);
36+
37+
return manager;
38+
} */
39+
40+
private boolean verbose = false;
41+
private boolean strict = false;
42+
private final List<RubyPermission> temporaryPermissions = new ArrayList<RubyPermission>();
43+
44+
@Override
45+
public void checkPermission(java.security.Permission perm) {
46+
for (RubyPermission permission: temporaryPermissions) {
47+
if (permission.matches(perm)) {
48+
return;
49+
}
50+
}
51+
52+
if (strict) {
53+
logTrace(perm.toString() + " denied");
54+
super.checkPermission(perm);
55+
}
56+
}
57+
58+
public SecurityManager setStrict(boolean strict) {
59+
this.strict = strict;
60+
return this;
61+
}
62+
63+
public SecurityManager permit(RubyPermission permission) {
64+
temporaryPermissions.add(permission);
65+
return this;
66+
}
67+
68+
public SecurityManager revoke(RubyPermission permission) {
69+
temporaryPermissions.remove(permission);
70+
return this;
71+
}
72+
73+
public SecurityManager setVerbosity(boolean verbose) {
74+
this.verbose = verbose;
75+
return this;
76+
}
77+
78+
private void logTrace(String message) {
79+
if (verbose) {
80+
new Exception(message).printStackTrace();
81+
}
82+
}
83+
}

src/test/ruby/test_security.rb

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
$CLASSPATH << File.expand_path('../../../pkg/test-classes', File.dirname(__FILE__))
2+
3+
class SecurityWrapper
4+
5+
java_import 'org.jruby.ext.openssl.security.SecurityManager'
6+
7+
attr_reader :java_manager
8+
9+
def initialize(java_manager)
10+
@java_manager = java_manager
11+
end
12+
13+
def install_security_manager
14+
java.lang.System.setSecurityManager java_manager
15+
end
16+
17+
def allow(permissions_hash = nil, &block)
18+
if permissions_hash
19+
permissions = parse_hash_value(permissions_hash).uniq.map { |v| v.flatten }
20+
21+
return allow do |expected_type, expected_name, expected_actions|
22+
permissions.any? do |parr|
23+
(type, name, actions) = *parr
24+
(type == expected_type &&
25+
(name.nil? || name == expected_name) &&
26+
(actions.nil? || actions == expected_actions))
27+
end
28+
end
29+
end
30+
31+
SecurityManager::RubyPermission.new(block).tap { |perm| java_manager.permit perm }
32+
end
33+
34+
def with_permissions(hash)
35+
p = allow(hash)
36+
begin
37+
yield
38+
ensure
39+
java_manager.revoke p
40+
end
41+
end
42+
43+
def permissive!
44+
java_manager.setStrict(false)
45+
self
46+
end
47+
48+
def strict!
49+
java_manager.setStrict(true)
50+
self
51+
end
52+
53+
def verbose!
54+
java_manager.setVerbosity true
55+
self
56+
end
57+
58+
def silent!
59+
java_manager.setVerbosity false
60+
self
61+
end
62+
63+
private
64+
65+
def parse_hash_value(value)
66+
return [ value ].compact unless value.is_a?(Hash)
67+
value.reduce([]) { |arr, kv| arr += [ kv.first ].product(parse_hash_value(kv.last)) }
68+
end
69+
end
70+
71+
Security = SecurityWrapper.new org.jruby.ext.openssl.security.SecurityManager.new
72+
73+
Security.allow do |type, name, actions|
74+
case type
75+
when 'FilePermission'
76+
# Allow to read the FS (.rb, .pem, .class, ...)
77+
actions == "read"
78+
when 'LoggingPermission'
79+
# NOTE invokebinder initializes JUL :
80+
# https://github.com/headius/invokebinder/blob/master/src/main/java/com/headius/invokebinder/Binder.java#L70
81+
name == "control" # ("java.util.logging.LoggingPermission" "control")
82+
when 'PropertyPermission'
83+
actions == "read" && [
84+
"java.protocol.handler.pkgs",
85+
"sun.misc.ProxyGenerator.saveGeneratedFiles",
86+
87+
# FFI needs to be able to read its properties
88+
/^jnr\.ffi\..*/,
89+
90+
# Allow reading any jruby properties
91+
/^jruby/,
92+
93+
# Allow knowledge about environment
94+
/^os\..*/,
95+
/^user\..*/,
96+
"sun.arch.data.model",
97+
"java.home",
98+
99+
# Allow proxies
100+
/^sun.reflect.proxy.*/,
101+
102+
# NOTE invokebinder initializes JUL :
103+
"sun.util.logging.disableCallerCheck"
104+
].any? { |read_permission| read_permission === name }
105+
when 'RuntimePermission'
106+
# Allow loading of native libraries
107+
name =~ /^loadLibrary\..*\.so$/ ||
108+
name == "loadLibrary.nio" ||
109+
110+
# jnr.posix needs this
111+
name == "accessDeclaredMembers" ||
112+
name == "createClassLoader" ||
113+
114+
# Let Main do System.exit
115+
name == "exitVM.1" ||
116+
117+
name =~ /^accessClassInPackage\.sun.*$/ ||
118+
119+
# Not sure what this is about
120+
name == "getProtectionDomain" ||
121+
name == "fileSystemProvider"
122+
when 'ReflectPermission'
123+
# JRuby makes heavy usage of reflection for dynamic invocation, etc
124+
name == "suppressAccessChecks"
125+
else
126+
false
127+
end
128+
end
129+
130+
##
131+
132+
Security.install_security_manager
133+
134+
##
135+
136+
Security.strict!.with_permissions({
137+
"SecurityPermission" => [
138+
"getProperty.keystore.type",
139+
"putProviderProperty.SunJGSS",
140+
"putProviderProperty.SunEC-Internal",
141+
"putProviderProperty.BC",
142+
"insertProvider.BC"
143+
],
144+
145+
# OpenSSL uses java.text.SimpleDateFormat that needs to load this
146+
"RuntimePermission" => "accessClassInPackage.sun.util.resources",
147+
"PropertyPermission" => {
148+
"com.sun.security.preserveOldDCEncoding" => "read",
149+
"sun.security.key.serial.interop" => "read",
150+
151+
# Maybe this should be global?
152+
"java.nio.file.spi.DefaultFileSystemProvider" => "read",
153+
154+
# SimpleDateFormat again
155+
"sun.timezone.ids.oldmapping" => "read",
156+
"sun.nio.fs.chdirAllowed" => "read",
157+
158+
# java.util.TimeZone.getDefault memoizes the default in property
159+
"user.timezone" => "write"
160+
}
161+
162+
}) do
163+
require 'openssl'
164+
end
165+
166+
##
167+
168+
Security.permissive!
169+
170+
puts "using permissive! security with installed manager: #{java.lang.System.getSecurityManager}"

0 commit comments

Comments
 (0)