CS551: Security and Privacy on the Internet, Fall 2000 |
sun-jdk-1.1.7/javasrc/src/share/sun/sun/applet/AppletSecurity.java
/* * @(#)AppletSecurity.java 1.76 98/07/01 * * Copyright 1995-1998 by Sun Microsystems, Inc., * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A. * All rights reserved. * * This software is the confidential and proprietary information * of Sun Microsystems, Inc. ("Confidential Information"). You * shall not disclose such Confidential Information and shall use * it only in accordance with the terms of the license agreement * you entered into with Sun. */ package sun.applet; import java.io.File; import java.io.IOException; import java.io.FileDescriptor; import java.net.URL; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.StringTokenizer; import java.util.Vector; import java.util.Hashtable; import java.security.*; import sun.security.provider.*; /** * This class defines an applet security policy * * @version 1.76, 07/01/98 */ public class AppletSecurity extends SecurityManager { private static boolean debug = false; final static int NETWORK_NONE = 1; final static int NETWORK_HOST = 2; final static int NETWORK_UNRESTRICTED = 3; private final static int PRIVELEGED_PORT = 1024; boolean initACL; String readACL[]; String writeACL[]; int networkMode; // where we look for identities IdentityScope scope; // local cache for network-loaded classes Hashtable loadedClasses; /** * Construct and initialize. */ public AppletSecurity() { reset(); } /** * Reset from Properties */ public void reset() { String str = System.getProperty("appletviewer.security.mode"); if (str == null) { str = "host"; } if (str.equals("unrestricted")) { networkMode = NETWORK_UNRESTRICTED; } else if (str.equals("none")) { networkMode = NETWORK_NONE; } else { networkMode = NETWORK_HOST; } // see if the system scope is one we know. // XXX: DO NOT REMOVE THIS IN 1.1.X. This call indirectly // initializes java.security.Security, as well as the // IdentityDatbase *before* any applets are loaded. IdentityScope scope = IdentityScope.getSystemScope(); if (scope instanceof IdentityDatabase) { this.scope = (IdentityDatabase)scope; debug("installing " + scope + " as the scope for signers."); } else { debug("no signer scope found."); } loadedClasses = new Hashtable(); } /** * True if called directly from an applet. */ boolean fromApplet() { return classLoaderDepth() == 1; } /** * This method takes a set of signers and returns true if * this set of signers implies that a class is trusted. * In this implementation, it returns true if any of the * signers is a SystemIdentity which is trusted. */ protected boolean assessTrust(Object[] signers) { /* Remind: do we want to actually look into the scope here? */ for (int i = 0; i < signers.length; i++) { if (signers[i] instanceof SystemIdentity) { SystemIdentity sysid = (SystemIdentity)signers[i]; if (sysid.isTrusted()) { return true; } } else if (signers[i] instanceof SystemSigner) { SystemSigner sysid = (SystemSigner)signers[i]; if (sysid.isTrusted()) { return true; } } } return false; } /** * True if called indirectly from anuntrusted applet. */ boolean inApplet() { return inClassLoader(); } /** * The only variable that currently affects whether an applet can * perform certain operations is the host it came from. */ public Object getSecurityContext() { ClassLoader loader = currentClassLoader(); if (loader == null) { return null; } else if (loader instanceof AppletClassLoader) { AppletClassLoader appletLoader = (AppletClassLoader)loader; return appletLoader.base; } else { throw new AppletSecurityException("getsecuritycontext.unknown"); } } /** * Override getInCheck() and make it synchronized. * This is to plug a hole explained in bug: 4052456 * InetAddress reveals IP addresses of machines behind firewall */ public synchronized boolean getInCheck() { return super.getInCheck(); } /** * Applets are not allowed to create class loaders, or even execute any * of ClassLoader's methods. The name of this method should be changed to * checkClassLoaderOperation or somesuch. */ public synchronized void checkCreateClassLoader() { if (classLoaderDepth() == 2) { throw new AppletSecurityException("checkcreateclassloader"); } } /** * Returns true if this threadgroup is in the applet's own thread * group. This will return false if there is no current class * loader. */ protected boolean inThreadGroup(ThreadGroup g) { ClassLoader loader = currentClassLoader(); /* If this class wasn't loaded by an AppletClassLoader, we have not eay of telling, for now. */ if (loader instanceof AppletClassLoader) { AppletClassLoader appletLoader = (AppletClassLoader)loader; ThreadGroup appletGroup = appletLoader.getThreadGroup(); return appletGroup.parentOf(g); } return false; } /** * Returns true of the threadgroup of thread is in the applet's * own threadgroup. */ protected boolean inThreadGroup(Thread thread) { return inThreadGroup(thread.getThreadGroup()); } /** * Applets are not allowed to manipulate threads outside * applet thread groups. */ public synchronized void checkAccess(Thread t) { if (classLoaderDepth()==3 && (! inThreadGroup(t))) { throw new AppletSecurityException("checkaccess.thread"); } } /** * Applets are not allowed to manipulate thread groups outside * applet thread groups. */ public synchronized void checkAccess(ThreadGroup g) { if (classLoaderDepth() == 4 && (! inThreadGroup(g))) { throw new AppletSecurityException("checkaccess.threadgroup", g.toString()); } } /** * Applets are not allowed to exit the VM. */ public synchronized void checkExit(int status) { if (inApplet()) { throw new AppletSecurityException("checkexit", String.valueOf(status)); } } /** * Applets are not allowed to fork processes. */ public synchronized void checkExec(String cmd){ if (inApplet()) { throw new AppletSecurityException("checkexec", cmd); } } /** * Applets are not allowed to link dynamic libraries. */ public synchronized void checkLink(String lib){ switch (classLoaderDepth()) { case 2: // Runtime.load case 3: // System.loadLibrary throw new AppletSecurityException("checklink", lib); default: break; } } /** * Applets are not allowed to access the entire system properties * list, only properties explicitly labeled as accessible to applets. */ public synchronized void checkPropertiesAccess() { if (classLoaderDepth() == 2) { throw new AppletSecurityException("checkpropsaccess"); } } /** * Applets can access the system property named by key * only if its twin key.applet property is set to true. * For example, the propertyjava.home
can be read by * applets only ifjava.home.applet
istrue
. */ public synchronized void checkPropertyAccess(String key) { if (classLoaderDepth() == 2) { String prop = System.getProperty(key + ".applet"); boolean allow = new Boolean(prop).booleanValue(); if (allow) { return; } else { throw new AppletSecurityException("checkpropsaccess.key", prop); } } } /** * Parse an ACL. Deals with "~" and "+" */ void parseACL(Vector v, String path, String defaultPath) { String sep = System.getProperty("path.separator"); StringTokenizer t = new StringTokenizer(path, sep); while (t.hasMoreTokens()) { String dir = t.nextToken(); if (dir.startsWith("~")) { v.addElement(System.getProperty("user.home") + dir.substring(1)); } else if (dir.equals("+")) { if (defaultPath != null) { parseACL(v, defaultPath, null); } } else { v.addElement(dir); } } } /** * Parse an ACL. */ String[] parseACL(String path, String defaultPath) { if (path == null) { return new String[0]; } if (path.equals("*")) { return null; } Vector v = new Vector(); parseACL(v, path, defaultPath); String acl[] = new String[v.size()]; v.copyInto(acl); return acl; } /** * Initialize ACLs. Called only once. */ void initializeACLs() { readACL = parseACL(System.getProperty("acl.read"), System.getProperty("acl.read.default")); writeACL = parseACL(System.getProperty("acl.write"), System.getProperty("acl.write.default")); initACL = true; } /** * Check if an applet can read a particular file. */ public synchronized void checkRead(String file) { ClassLoader loader = currentClassLoader(); /* If no class loader, it's a system class. */ if (loader == null) { return; } /* If not an AppletClassLoader, we don't know what to do */ if (! (loader instanceof AppletClassLoader)) { throw new AppletSecurityException("checkread.unknown", file); } AppletClassLoader appletLoader = (AppletClassLoader)loader; checkRead(file, appletLoader.base); } public synchronized void checkRead(String file, URL base) { if (base != null) { if (!initACL) { initializeACLs(); } if (readACL == null) { return; } String realPath = null; try { realPath = (new File(file)).getCanonicalPath(); } catch (IOException e) { throw new AppletSecurityException("checkread.exception1", e.getMessage(), file); } for (int i = readACL.length ; i-- > 0 ;) { if (realPath.startsWith(readACL[i])) { return; } } // if the applet is loaded from a file URL, allow reading // in that directory String host = base.getHost(); if (base.getProtocol().equals("file") && (host == null || host.equals("") || host.equals("~") || host.equals("localhost"))) { String dir = null; try { dir = (new File(base.getFile()).getCanonicalPath()); } catch (IOException e) { // shouldn't happen throw new AppletSecurityException("checkread.exception2", e.toString()); } if (realPath.startsWith(dir)) { return; } } throw new AppletSecurityException("checkread", file, realPath); } } /** * Checks to see if the current context or the indicated context are * both allowed to read the given file name. * @param file the system dependent file name * @param context the alternate execution context which must also * be checked * @exception SecurityException If the file is not found. */ public void checkRead(String file, Object context) { checkRead(file); if (context != null) { checkRead(file, (URL) context); } } /** * Check if an applet can write a particular file. */ public synchronized void checkWrite(String file) { if (inApplet()) { if (!initACL) { initializeACLs(); } if (writeACL == null) { return; } String realPath = null; try { realPath = (new File(file)).getCanonicalPath(); } catch (IOException e) { throw new AppletSecurityException("checkwrite.exception", e.getMessage(), file); } for (int i = writeACL.length ; i-- > 0 ;) { if (realPath.startsWith(writeACL[i])) { return; } } throw new AppletSecurityException("checkwrite", file, realPath); } } /** * Check if an applet can delete a particular file. */ public synchronized void checkDelete(String file) { checkWrite(file); } /** * Applets are not allowed to open file descriptors unless * it is done through a socket, in which case other access * restrictions still apply. */ public synchronized void checkRead(FileDescriptor fd) { if ((inApplet() && !inClass("java.net.SocketInputStream")) || (!fd.valid()) ) { throw new AppletSecurityException("checkread.fd"); } } /** * Applets are not allowed to open file descriptors unless * it is done through a socket, in which case other access * restrictions still apply. */ public synchronized void checkWrite(FileDescriptor fd) { if ( (inApplet() && !inClass("java.net.SocketOutputStream")) || (!fd.valid()) ) { throw new AppletSecurityException("checkwrite.fd"); } } /** * Applets can only listen on unpriveleged ports > 1024 * A port of 0 denotes an ephemeral system-assigned port * Which will be outside this range. Note that java sockets * take an int and ports are really a u_short, but range * checking is done in ServerSocket & DatagramSocket, so the port policy * cannot be subverted by ints that wrap around to an illegal u_short. */ public synchronized void checkListen(int port) { if (inApplet() && port > 0 && port < PRIVELEGED_PORT) { throw new AppletSecurityException("checklisten", String.valueOf(port)); } } /** * Applets can accept connectionions on unpriveleged ports, from * any hosts they can also connect to (typically host-of-origin * only, depending on the network security setting). */ public synchronized void checkAccept(String host, int port) { if (inApplet() && port < PRIVELEGED_PORT) { throw new AppletSecurityException("checkaccept", host, String.valueOf(port)); } checkConnect(host, port); } /** * Check if an applet can connect to the given host:port. */ public synchronized void checkConnect(String host, int port) { ClassLoader loader = currentClassLoader(); if (loader == null) { // Not called from an applet, so it is ok return; } // REMIND: This is only appropriate for our protocol handlers. int depth = classDepth("sun.net.www.http.HttpClient"); if (depth > 1) { // Called through our http protocol handler return; } if (loader instanceof AppletClassLoader) { AppletClassLoader appletLoader = (AppletClassLoader)loader; checkConnect(appletLoader.base.getHost(), host); } else { throw new AppletSecurityException("checkconnect.unknown"); } } /** * Checks to see if the applet and the indicated execution context * are both allowed to connect to the indicated host and port. */ public void checkConnect(String host, int port, Object context) { checkConnect(host, port); if (context != null) { checkConnect(((URL) context).getHost(), host); } } public synchronized void checkConnect(String fromHost, String toHost, boolean trustP) { if (fromHost == null) { return; } switch (networkMode) { case NETWORK_NONE: throw new AppletSecurityException("checkconnect.networknone", fromHost, toHost); case NETWORK_HOST: /* * The policy here is as follows: * * - if the strings match, and we know the IP address for it * we allow the connection. The calling code downstream will * substitute the IP in their request to the proxy if needed. * - if the strings don't match, and we can get the IP of * both hosts then * - if the IPs match, we allow the connection * - if they don't we throw an exception * - if the string match works and we don't know the IP address * then we consult the trustProxy property, and if that is true, * we allow the connection. * set inCheck so InetAddress knows it doesn't have to * check security. */ try { inCheck = true; InetAddress toHostAddr, fromHostAddr; if (!fromHost.equals(toHost)) { try { // the only time we allow non-matching strings // is when IPs and the IPs match. toHostAddr = InetAddress.getByName(toHost); fromHostAddr = InetAddress.getByName(fromHost); if (fromHostAddr.equals(toHostAddr)) { return; } else { throw new AppletSecurityException("checkconnect.networkhost1", toHost, fromHost); } } catch (UnknownHostException e) { throw new AppletSecurityException("checkconnect.networkhost2", toHost, fromHost); } } else { try { toHostAddr = InetAddress.getByName(toHost); // strings match: if we have IP, we're homefree, // otherwise we check the properties. return; // getBoolean really defaults to false. } catch (UnknownHostException e) { if (trustP) { return; } else { throw new AppletSecurityException("checkconnect.networkhost3", toHost); } } } } finally { inCheck = false; } case NETWORK_UNRESTRICTED: return; } throw new AppletSecurityException("checkconnect", fromHost, toHost); } /** * Check if an applet from a host can connect to another * host. This usually means that you need to determine whether * the hosts are inside or outside the firewall. For now applets * can only access the host they came from. */ public synchronized void checkConnect(String fromHost, String toHost) { checkConnect(fromHost, toHost, Boolean.getBoolean("trustProxy")); } /** * Check if applet is allowed to use (join/leave/send/receive) * IP multicast. */ public void checkMulticast(InetAddress maddr) { if (inApplet()) { throw new AppletSecurityException("checkMulticast"); } } /** * Check if applet is allowed to use (join/leave/send/receive) * IP multicast. */ public void checkMulticast(InetAddress maddr, byte ttl) { if (inApplet()) { throw new AppletSecurityException("checkMulticast"); } } /** * Checks to see if top-level windows can be created by the caller. */ public synchronized boolean checkTopLevelWindow(Object window) { if (inClassLoader()) { /* XXX: this used to return depth > 3. However, this lets */ /* some applets create frames without warning strings. */ return false; } return true; } /** * Check if an applet can access a package. */ public synchronized void checkPackageAccess(String pkg) { if (!inClassLoader()) return; int i = pkg.indexOf('.'); while (i > 0) { String subpkg = pkg.substring(0,i); if (Boolean.getBoolean("package.restrict.access." + subpkg)) { throw new AppletSecurityException("checkpackageaccess", pkg); } i = pkg.indexOf('.',i+1); } } /** * Check if an applet can define classes in a package. */ public synchronized void checkPackageDefinition(String pkg) { if (!inClassLoader()) return; int i = pkg.indexOf('.'); while (i > 0) { String subpkg = pkg.substring(0,i); if (Boolean.getBoolean("package.restrict.definition." + subpkg)) { throw new AppletSecurityException("checkpackagedefinition", pkg); } i = pkg.indexOf('.',i+1); } } /** * Check if an applet can set a networking-related object factory. */ public synchronized void checkSetFactory() { if (inApplet() && !inClass("sun.net.www.MimeTable")) { throw new AppletSecurityException("cannotsetfactory"); } } /** * Check if client is allowed to reflective access to a member or * a set of members for the specified class. Once initial access * is granted, the reflected members can be queried for * identifying information, but can only be used * (via get, set, invoke, or newInstance) with standard Java * language access control. * *The policy is to deny untrusted clients access to * declared members of classes other than those loaded * via the same class loader. All other accesses are granted. * * XXX: Should VerifyClassAccess here? Should Class.forName do it? */ public void checkMemberAccess(Class clazz, int which) { if (which != java.lang.reflect.Member.PUBLIC) { ClassLoader currentLoader = currentClassLoader(); if (currentLoader != null && (classLoaderDepth() <= 3)) { /* Client is an untrusted class loaded by currentLoader */ if (currentLoader != clazz.getClassLoader()) { throw new AppletSecurityException("checkmemberaccess"); } } } } /** * Checks to see if an applet can initiate a print job request. */ public void checkPrintJobAccess() { if (inApplet()) { throw new AppletSecurityException("checkgetprintjob"); } } /** * Checks to see if an applet can get System Clipboard access. */ public void checkSystemClipboardAccess() { if (inApplet()) { throw new AppletSecurityException("checksystemclipboardaccess"); } } /** * Checks to see if an applet can get EventQueue access. */ public void checkAwtEventQueueAccess() { if (inApplet()) { throw new AppletSecurityException("checkawteventqueueaccess"); } } /** * Checks to see if an applet can perform a given operation. */ public void checkSecurityAccess(String action) { if (inApplet()) { throw new AppletSecurityException("checksecurityaccess", action); } } /** * Returns the thread group of the applet. We consult the classloader * if there is one. */ public ThreadGroup getThreadGroup() { /* First we check if any classloaded thing is on the stack. */ ClassLoader loader = currentClassLoader(); if (loader != null && (loader instanceof AppletClassLoader)) { AppletClassLoader appletLoader = (AppletClassLoader)loader; return appletLoader.getThreadGroup(); } else { return super.getThreadGroup(); } } public void debug(String s) { if (debug) { System.err.println(s); } } }
University of Virginia Department of Computer Science CS 551: Security and Privacy on the Internet |
David Evans evans@virginia.edu |