Skip to content

Commit 6de037e

Browse files
committed
Working on security sandbox - to be optimized and well tested.
1 parent f534be9 commit 6de037e

File tree

10 files changed

+394
-14
lines changed

10 files changed

+394
-14
lines changed

src/fbjava-tests/src/main/java/example/Functions.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,4 +169,9 @@ public static int f20()
169169
{
170170
return ++n;
171171
}
172+
173+
public static String f21(String property)
174+
{
175+
return System.getProperty(property);
176+
}
172177
}

src/fbjava-tests/src/main/java/example/Procedures.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,29 @@ public void close()
9292
}
9393
};
9494
}
95+
96+
public static ExternalResultSet p7(String property, String[] result)
97+
{
98+
return new ExternalResultSet() {
99+
boolean first = true;
100+
101+
@Override
102+
public boolean fetch() throws Exception
103+
{
104+
if (first)
105+
{
106+
result[0] = System.getProperty(property);
107+
first = false;
108+
return true;
109+
}
110+
else
111+
return false;
112+
}
113+
114+
@Override
115+
public void close()
116+
{
117+
}
118+
};
119+
}
95120
}

src/fbjava/src/main/java/org/firebirdsql/fbjava/impl/DbClassLoader.java

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121
import java.io.IOException;
2222
import java.net.URL;
2323
import java.net.URLClassLoader;
24+
import java.security.CodeSource;
25+
import java.security.PermissionCollection;
26+
import java.security.Permissions;
27+
import java.security.cert.Certificate;
2428
import java.sql.Connection;
2529
import java.sql.DriverManager;
2630
import java.sql.SQLException;
@@ -37,15 +41,22 @@
3741
*/
3842
final class DbClassLoader extends URLClassLoader
3943
{
44+
String databaseName;
4045
private FBConnection connection;
46+
CodeSource codeSource;
47+
PermissionCollection codeSourcePermission = new Permissions();
4148

42-
DbClassLoader(String databaseName, String adminUser, String adminPassword, URL[] urls, ClassLoader parent)
49+
DbClassLoader(String databaseName, URL[] urls, ClassLoader parent)
4350
throws SQLException
4451
{
4552
super(urls, parent);
4653

54+
this.databaseName = databaseName;
55+
56+
codeSource = new CodeSource(urls[0], (Certificate[]) null);
57+
4758
connection = (FBConnection) DriverManager.getConnection(
48-
"jdbc:firebirdsql:embedded:" + databaseName + "?charSet=UTF-8", adminUser, adminPassword);
59+
"jdbc:firebirdsql:embedded:" + databaseName + "?charSet=UTF-8");
4960
connection.setAutoCommit(false);
5061
connection.setReadOnly(true);
5162

@@ -54,13 +65,16 @@ final class DbClassLoader extends URLClassLoader
5465
tpb.addArgument(ISCConstants.isc_tpb_rec_version);
5566
tpb.addArgument(ISCConstants.isc_tpb_read);
5667
connection.setTransactionParameters(Connection.TRANSACTION_READ_COMMITTED, tpb);
68+
69+
DbPolicy.databaseOpened();
5770
}
5871

5972
@Override
6073
public void close() throws IOException
6174
{
6275
try
6376
{
77+
DbPolicy.databaseClosed();
6478
connection.close();
6579
}
6680
catch (SQLException e)
@@ -69,6 +83,12 @@ public void close() throws IOException
6983
}
7084
}
7185

86+
@Override
87+
protected PermissionCollection getPermissions(CodeSource codesource)
88+
{
89+
return codeSourcePermission;
90+
}
91+
7292
public Connection getConnection()
7393
{
7494
return connection;
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
/*
2+
* FB/Java plugin
3+
*
4+
* Distributable under LGPL license.
5+
* You may obtain a copy of the License at http://www.gnu.org/copyleft/lgpl.html
6+
*
7+
* This program is distributed in the hope that it will be useful,
8+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
9+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10+
* LGPL License for more details.
11+
*
12+
* This file was created by members of the Firebird development team.
13+
* All individual contributions remain the Copyright (C) of those
14+
* individuals. Contributors to this file are either listed here or
15+
* can be obtained from a git log command.
16+
*
17+
* All rights reserved.
18+
*/
19+
package org.firebirdsql.fbjava.impl;
20+
21+
import java.lang.reflect.Constructor;
22+
import java.security.AccessController;
23+
import java.security.AllPermission;
24+
import java.security.Permission;
25+
import java.security.PermissionCollection;
26+
import java.security.Permissions;
27+
import java.security.Policy;
28+
import java.security.Principal;
29+
import java.security.PrivilegedAction;
30+
import java.security.ProtectionDomain;
31+
import java.sql.Connection;
32+
import java.sql.DriverManager;
33+
import java.sql.PreparedStatement;
34+
import java.sql.ResultSet;
35+
import java.sql.SQLException;
36+
import java.util.HashMap;
37+
import java.util.HashSet;
38+
39+
import javax.security.auth.Subject;
40+
41+
import org.firebirdsql.gds.ISCConstants;
42+
import org.firebirdsql.gds.TransactionParameterBuffer;
43+
import org.firebirdsql.jdbc.FBConnection;
44+
45+
46+
/**
47+
* Security policy configured in a database.
48+
*
49+
* @author <a href="mailto:adrianosf@gmail.com">Adriano dos Santos Fernandes</a>
50+
*/
51+
final class DbPolicy extends Policy
52+
{
53+
private static int count;
54+
private static String securityDatabase;
55+
private static FBConnection securityDb;
56+
private static PreparedStatement stmt;
57+
private static HashMap<String, Subject> userSubjects = new HashMap<String, Subject>();
58+
59+
public DbPolicy(String securityDatabase)
60+
{
61+
DbPolicy.securityDatabase = securityDatabase;
62+
}
63+
64+
static void databaseOpened() throws SQLException
65+
{
66+
synchronized (securityDatabase)
67+
{
68+
if (count++ == 0)
69+
{
70+
assert securityDb == null;
71+
72+
securityDb = (FBConnection) DriverManager.getConnection(
73+
"jdbc:firebirdsql:embedded:" + securityDatabase + "?charSet=UTF-8");
74+
securityDb.setAutoCommit(false);
75+
securityDb.setReadOnly(true);
76+
77+
TransactionParameterBuffer tpb = securityDb.createTransactionParameterBuffer();
78+
tpb.addArgument(ISCConstants.isc_tpb_read_committed);
79+
tpb.addArgument(ISCConstants.isc_tpb_rec_version);
80+
tpb.addArgument(ISCConstants.isc_tpb_read);
81+
securityDb.setTransactionParameters(Connection.TRANSACTION_READ_COMMITTED, tpb);
82+
83+
stmt = securityDb.prepareStatement(
84+
"select p.class_name, p.arg1, p.arg2\n" +
85+
" from database_permission_group dpg\n" +
86+
" join permission_group pg\n" +
87+
" on pg.id = dpg.permission_group\n" +
88+
" join permission p\n" +
89+
" on p.permission_group = pg.id\n" +
90+
" where cast(? as varchar(1024)) similar to dpg.database_pattern escape '\\' and\n" +
91+
" p.user_name in (?, 'PUBLIC')");
92+
}
93+
}
94+
}
95+
96+
static void databaseClosed() throws SQLException
97+
{
98+
synchronized (securityDatabase)
99+
{
100+
if (--count == 0)
101+
{
102+
stmt.close();
103+
securityDb.close();
104+
securityDb = null;
105+
}
106+
}
107+
}
108+
109+
static synchronized Subject getUserSubject(String databaseName, String userName)
110+
{
111+
synchronized (userSubjects)
112+
{
113+
Subject subj = userSubjects.get(userName);
114+
if (subj != null)
115+
return subj;
116+
117+
final HashSet<Principal> principals = new HashSet<Principal>();
118+
principals.add(new DbPrincipal(databaseName, userName));
119+
subj = new Subject(true, principals, new HashSet<Object>(), new HashSet<Object>());
120+
121+
userSubjects.put(userName, subj);
122+
123+
return subj;
124+
}
125+
}
126+
127+
@Override
128+
public PermissionCollection getPermissions(final ProtectionDomain domain)
129+
{
130+
final Permissions permissions = new Permissions();
131+
132+
// Grant all permission to code stored at filesystem
133+
if ("file".equals(domain.getCodeSource().getLocation().getProtocol()))
134+
{
135+
permissions.add(new AllPermission());
136+
return permissions;
137+
}
138+
139+
return AccessController.doPrivileged(new PrivilegedAction<PermissionCollection>() {
140+
@Override
141+
public PermissionCollection run()
142+
{
143+
for (Principal principal : domain.getPrincipals())
144+
{
145+
if (principal instanceof DbPrincipal)
146+
loadPermissions(((DbPrincipal) principal).getDatabaseName(), principal.getName(), permissions);
147+
}
148+
149+
return permissions;
150+
}
151+
});
152+
}
153+
154+
@Override
155+
public boolean implies(final ProtectionDomain domain, final Permission permission)
156+
{
157+
// Grant all permission to code stored at filesystem
158+
if ("file".equals(domain.getCodeSource().getLocation().getProtocol()))
159+
return true;
160+
161+
return AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
162+
@Override
163+
public Boolean run()
164+
{
165+
PermissionCollection perms = getPermissions(domain);
166+
return perms.implies(permission);
167+
}
168+
});
169+
}
170+
171+
private void loadPermissions(String databaseName, String userName, PermissionCollection permissions)
172+
{
173+
//// TODO: cache
174+
synchronized (stmt)
175+
{
176+
try
177+
{
178+
stmt.setString(1, databaseName);
179+
stmt.setString(2, userName);
180+
ResultSet rs = stmt.executeQuery();
181+
try
182+
{
183+
while (rs.next())
184+
{
185+
try
186+
{
187+
Class<?> clazz = Class.forName(rs.getString(1));
188+
String arg1 = rs.getString(2);
189+
String arg2 = rs.getString(3);
190+
191+
if (arg2 != null)
192+
{
193+
Constructor<?> constr = clazz.getDeclaredConstructor(
194+
String.class, String.class);
195+
permissions.add((Permission) constr.newInstance(arg1, arg2));
196+
}
197+
else if (arg1 != null)
198+
{
199+
Constructor<?> constr = clazz.getDeclaredConstructor(String.class);
200+
permissions.add((Permission) constr.newInstance(arg1));
201+
}
202+
else
203+
{
204+
Constructor<?> constr = clazz.getDeclaredConstructor();
205+
permissions.add((Permission) constr.newInstance());
206+
}
207+
}
208+
catch (Exception e)
209+
{
210+
}
211+
}
212+
}
213+
finally
214+
{
215+
rs.close();
216+
}
217+
}
218+
catch (SQLException e)
219+
{
220+
}
221+
}
222+
}
223+
}

0 commit comments

Comments
 (0)