001 package sysModel;
002
003 import sysModel.fish.AFish;
004
005 import javax.sound.sampled.AudioPermission;
006 import java.awt.*;
007 import java.io.FilePermission;
008 import java.io.SerializablePermission;
009 import java.lang.reflect.ReflectPermission;
010 import java.net.NetPermission;
011 import java.net.SocketPermission;
012 import java.security.Permission;
013 import java.security.SecurityPermission;
014 import java.sql.SQLPermission;
015 import java.util.PropertyPermission;
016 import java.util.Stack;
017
018 /**
019 * Special security manager that does not permit fish to call System.exit() or System.setSecurityManager().
020 *
021 * @author Mathias Ricken
022 */
023 public class MBSSecurityManager extends SecurityManager {
024 /**
025 * Constructor.
026 * @param drJava true if DrJava is running
027 */
028 public MBSSecurityManager(boolean drJava) {
029 // TODO: Should create new security manager and load policy, but using the command line and DrJava is problematic right now
030 //_sm = new SecurityManager();
031 //PolicyFile pf = new PolicyFile();
032 //Policy.setPolicy(pf);
033 _isProtected = true;
034 _classPath = System.getProperty("java.class.path");
035 _pathSep = System.getProperty("path.separator");
036 _classPathDirs = _classPath.split(_pathSep);
037 _drJava = drJava;
038 }
039
040 /**
041 * True if DrJava is present.
042 */
043 private boolean _drJava = false;
044
045 /**
046 * Flag if actions are protected.
047 */
048 private boolean _isProtected = true;
049
050 /**
051 * SecurityManager to delegate to.
052 */
053 private SecurityManager _sm = new SecurityManager();
054
055 /**
056 * Class path.
057 */
058 private String _classPath;
059
060 /**
061 * Path separator.
062 */
063 private String _pathSep;
064
065 /**
066 * Class path directories.
067 */
068 private String[] _classPathDirs;
069
070 /**
071 * Fish thread group.
072 */
073 private ThreadGroup _fishThreadGroup = new ThreadGroup("FishThreadGroup");
074
075 /**
076 * Class loader.
077 */
078 private MBSClassLoader _classLoader = new MBSClassLoader(this);
079
080 /**
081 * Stack of protection flags.
082 */
083 private Stack<Boolean> _protectionFlagStack = new Stack<Boolean>();
084
085 /**
086 * Set the protection flag.
087 *
088 * @param isProtected true if actions are to be protected
089 */
090 public void setProtected(boolean isProtected) {
091 _isProtected = isProtected;
092 }
093
094 /**
095 * Get the protection flag.
096 * @return true if actions are to be protected
097 */
098 public boolean isProtected() {
099 return _isProtected;
100 }
101
102 /**
103 * Push the current protection flag onto the stack and set a new value.
104 * @param isProtected true if actions are to be protected
105 */
106 public void pushProtected(boolean isProtected) {
107 _protectionFlagStack.push(_isProtected);
108 _isProtected = isProtected;
109 }
110
111 /**
112 * Pops the top protection flag from the stack and sets it as new value.
113 */
114 public void popProtected() {
115 _isProtected = _protectionFlagStack.pop();
116 }
117
118 /**
119 * Returns true if the filename is on the class path.
120 *
121 * @param filename filename to test
122 * @return true if on classpath
123 */
124 protected boolean isOnClassPath(String filename) {
125 //System.out.println("Checking "+filename);
126 for (int i = 0; i < _classPathDirs.length; ++i) {
127 //System.out.println("\t"+_classPathDirs[i]);
128 if (filename.startsWith(_classPathDirs[i])) {
129 return true;
130 }
131 }
132
133 return false;
134 }
135
136 /**
137 * Returns true if a fish is somewhere on the call stack.
138 *
139 * @return true if fish is a caller
140 */
141 protected boolean isFishCaller() {
142 Class[] classes = getClassContext();
143 for (int i = 0; i < classes.length; ++i) {
144 if (AFish.class.isAssignableFrom(classes[i])) {
145 // System.out.println("Found a fish class: "+classes[0].getName());
146 return true;
147 }
148 }
149 return false;
150 }
151
152 /**
153 * Throws a <code>SecurityException</code> if the requested access, specified by the given permission, is not
154 * permitted based on the security policy currently in effect.
155 * <p/>
156 * This method calls <code>AccessController.checkPermission</code> with the given permission.
157 *
158 * @param perm the requested permission.
159 * @throws SecurityException if access is not permitted based on the current security policy.
160 * @throws NullPointerException if the permission argument is <code>null</code>.
161 * @since 1.2
162 */
163 public void checkPermission(Permission perm) {
164 if (_drJava) {
165 return;
166 }
167
168 boolean wasProtected = _isProtected;
169 try {
170 pushProtected(false);
171
172 if (wasProtected) {
173 if ((null != Thread.currentThread().getThreadGroup()) && (Thread.currentThread().getThreadGroup()==_fishThreadGroup) && (isFishCaller())) {
174 // System.err.println("checkPermission: "+perm);
175 if ((perm instanceof RuntimePermission)) {
176 // disallow
177 throw new SecurityException("Not allowed to deal with runtime!");
178 }
179 else if (perm instanceof AudioPermission) {
180 // disallow
181 throw new SecurityException("Not allowed to deal with audio!");
182 }
183 else if (perm instanceof AWTPermission) {
184 // disallow
185 throw new SecurityException("Not allowed to deal with AWT!");
186 }
187 else if ((perm instanceof FilePermission) &&
188 ((!"read".equals(perm.getActions())) || ((!perm.getName().endsWith(".class") && (!perm.getName().endsWith(".jar"))))/*|| (!isOnClassPath(perm.getName()))*/)) {
189 // TODO: isOnClassPath should be checked, but jar files and DrJava are problematic right now
190 // disallow
191 throw new SecurityException("Not allowed to deal with files!");
192 }
193 else if (perm instanceof NetPermission) {
194 // disallow
195 throw new SecurityException("Not allowed to deal with network!");
196 }
197 else if ((perm instanceof PropertyPermission) &&
198 ((!"read".equals(perm.getActions())) || !"line.separator".equals(perm.getName()))) {
199 // disallow
200 StackTraceElement[] stes = Thread.currentThread().getStackTrace();
201 for (StackTraceElement ste: stes) {
202 System.out.println("### "+ste);
203 }
204 throw new SecurityException("Not allowed to deal with properties!");
205 }
206 else if (perm instanceof ReflectPermission) {
207 // disallow
208 throw new SecurityException("Not allowed to do reflection!");
209 }
210 else if (perm instanceof SecurityPermission) {
211 // disallow
212 throw new SecurityException("Not allowed to deal with security!");
213 }
214 else if (perm instanceof SerializablePermission) {
215 // disallow
216 throw new SecurityException("Not allowed to deal with serialization!");
217 }
218 else if (perm instanceof SocketPermission) {
219 // disallow
220 throw new SecurityException("Not allowed to deal with sockets!");
221 }
222 else if (perm instanceof SQLPermission) {
223 // disallow
224 throw new SecurityException("Not allowed to deal with SQL!");
225 }
226 }
227 }
228
229 // delegate
230 // TODO: Should delegate, but using the command line and DrJava is problematic right now
231 //_sm.checkPermission(perm);
232 }
233 finally {
234 popProtected();
235 }
236 }
237
238 /**
239 * Throws a <code>SecurityException</code> if the calling thread is not allowed to modify the thread argument.
240 * <p/>
241 * This method is invoked for the current security manager by the <code>stop</code>, <code>suspend</code>,
242 * <code>resume</code>, <code>setPriority</code>, <code>setName</code>, and <code>setDaemon</code> methods of class
243 * <code>Thread</code>.
244 * <p/>
245 * If the thread argument is a system thread (belongs to the thread group with a <code>null</code> parent) then this
246 * method calls <code>checkPermission</code> with the <code>RuntimePermission("modifyThread")</code> permission. If
247 * the thread argument is <i>not</i> a system thread, this method just returns silently.
248 * <p/>
249 * Applications that want a stricter policy should override this method. If this method is overridden, the method
250 * that overrides it should additionally check to see if the calling thread has the
251 * <code>RuntimePermission("modifyThread")</code> permission, and if so, return silently. This is to ensure that
252 * code granted that permission (such as the SDK itself) is allowed to manipulate any thread.
253 * <p/>
254 * If this method is overridden, then <code>super.checkAccess</code> should be called by the first statement in the
255 * overridden method, or the equivalent security check should be placed in the overridden method.
256 *
257 * @param t the thread to be checked.
258 * @throws SecurityException if the calling thread does not have permission to modify the thread.
259 * @throws NullPointerException if the thread argument is <code>null</code>.
260 * @see Thread#resume() resume
261 * @see Thread#setDaemon(boolean) setDaemon
262 * @see Thread#setName(String) setName
263 * @see Thread#setPriority(int) setPriority
264 * @see Thread#stop() stop
265 * @see Thread#suspend() suspend
266 * @see #checkPermission(Permission) checkPermission
267 */
268 public void checkAccess(Thread t) {
269 if (_drJava) {
270 return;
271 }
272
273 //System.err.println("checkAccess: Thread "+t.getId());
274
275 super.checkAccess(t);
276
277 if (_isProtected) {
278 checkPermission(new RuntimePermission("modifyThread"));
279 }
280 }
281
282 /**
283 * Throws a <code>SecurityException</code> if the calling thread is not allowed to modify the thread group
284 * argument.
285 * <p/>
286 * This method is invoked for the current security manager when a new child thread or child thread group is created,
287 * and by the <code>setDaemon</code>, <code>setMaxPriority</code>, <code>stop</code>, <code>suspend</code>,
288 * <code>resume</code>, and <code>destroy</code> methods of class <code>ThreadGroup</code>.
289 * <p/>
290 * If the thread group argument is the system thread group ( has a <code>null</code> parent) then this method calls
291 * <code>checkPermission</code> with the <code>RuntimePermission("modifyThreadGroup")</code> permission. If the
292 * thread group argument is <i>not</i> the system thread group, this method just returns silently.
293 * <p/>
294 * Applications that want a stricter policy should override this method. If this method is overridden, the method
295 * that overrides it should additionally check to see if the calling thread has the
296 * <code>RuntimePermission("modifyThreadGroup")</code> permission, and if so, return silently. This is to ensure
297 * that code granted that permission (such as the SDK itself) is allowed to manipulate any thread.
298 * <p/>
299 * If this method is overridden, then <code>super.checkAccess</code> should be called by the first statement in the
300 * overridden method, or the equivalent security check should be placed in the overridden method.
301 *
302 * @param g the thread group to be checked.
303 * @throws SecurityException if the calling thread does not have permission to modify the thread group.
304 * @throws NullPointerException if the thread group argument is <code>null</code>.
305 * @see ThreadGroup#destroy() destroy
306 * @see ThreadGroup#resume() resume
307 * @see ThreadGroup#setDaemon(boolean) setDaemon
308 * @see ThreadGroup#setMaxPriority(int) setMaxPriority
309 * @see ThreadGroup#stop() stop
310 * @see ThreadGroup#suspend() suspend
311 * @see #checkPermission(Permission) checkPermission
312 */
313 public void checkAccess(ThreadGroup g) {
314 if (_drJava) {
315 return;
316 }
317
318 //System.err.println("checkAccess: ThreadGroup "+g.getName());
319
320 super.checkAccess(g);
321
322 if ((_isProtected) && (null != Thread.currentThread().getThreadGroup()) && (Thread.currentThread().getThreadGroup()==_fishThreadGroup)) {
323 if (!"system".equals(g.getName())) {
324 throw new SecurityException("Not allowed to manipulate threads!");
325 }
326 checkPermission(new RuntimePermission("modifyThreadGroup"));
327 }
328 }
329
330 /**
331 * Throws a <code>SecurityException</code> if the calling thread is not allowed to cause the Java Virtual Machine to
332 * halt with the specified status code.
333 * <p/>
334 * This method is invoked for the current security manager by the <code>exit</code> method of class
335 * <code>Runtime</code>. A status of <code>0</code> indicates success; other values indicate various errors.
336 * <p/>
337 * This method calls <code>checkPermission</code> with the <code>RuntimePermission("exitVM")</code> permission.
338 * <p/>
339 * If you override this method, then you should make a call to <code>super.checkExit</code> at the point the
340 * overridden method would normally throw an exception.
341 *
342 * @param status the exit status.
343 * @throws SecurityException if the calling thread does not have permission to halt the Java Virtual Machine with
344 * the specified status.
345 * @see Runtime#exit(int) exit
346 * @see #checkPermission(Permission) checkPermission
347 */
348 public void checkExit(int status) {
349 if (_drJava) {
350 return;
351 }
352
353 if (_isProtected) {
354 throw new SecurityException("Not allowed to call System.exit()!");
355 }
356 else {
357 // delegate
358 // TODO: Should delegate, but using the command line and DrJava is problematic right now
359 //_sm.checkExit(status);
360 }
361 }
362
363 /**
364 * Get the thread group the fish are run in.
365 * @return fish thread group
366 */
367 public ThreadGroup getFishThreadGroup() {
368 return _fishThreadGroup;
369 }
370
371 /**
372 * Return the class loader.
373 * @return class loader
374 */
375 public ClassLoader getClassLoader() {
376 if (_drJava) {
377 return getClass().getClassLoader();
378 }
379 else {
380 return _classLoader;
381 }
382 }
383 }