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 }