001: /*
002: * @(#)SecurePnutsImpl.java 1.3 05/05/09
003: *
004: * Copyright (c) 1997-2005 Sun Microsystems, Inc. All Rights Reserved.
005: *
006: * See the file "LICENSE.txt" for information on usage and redistribution
007: * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
008: */
009: package pnuts.security;
010:
011: import java.io.File;
012: import java.io.FileNotFoundException;
013: import java.io.FilePermission;
014: import java.io.IOException;
015: import java.net.JarURLConnection;
016: import java.net.SocketPermission;
017: import java.net.URL;
018: import java.net.URLConnection;
019: import java.security.AccessControlContext;
020: import java.security.AccessController;
021: import java.security.CodeSource;
022: import java.security.Permission;
023: import java.security.PermissionCollection;
024: import java.security.Permissions;
025: import java.security.Policy;
026: import java.security.PrivilegedAction;
027: import java.security.ProtectionDomain;
028: import java.security.cert.Certificate;
029:
030: import pnuts.ext.ImplementationAdapter;
031: import pnuts.lang.Context;
032: import pnuts.lang.Implementation;
033: import pnuts.lang.PnutsImpl;
034: import pnuts.lang.Runtime;
035: import pnuts.lang.SimpleNode;
036:
037: /**
038: * A PnutsImpl subclass that execute scripts in an access control context in
039: * Java2 Security.
040: *
041: * <pre>e.g.
042: * context.setImplementation(new SecurePnutsImpl(new CompilerPnutsImpl(), codesource))
043: * </pre>
044: */
045: public class SecurePnutsImpl extends ImplementationAdapter {
046:
047: private CodeSource codeSource;
048:
049: /**
050: * A Constructor
051: *
052: * @param impl a PnutsImpl object
053: *
054: * @deprecated replaced by SecurePnutsImpl(Implementation)
055: */
056: public SecurePnutsImpl(PnutsImpl impl) {
057: this (impl, null);
058: }
059:
060: /**
061: * A Constructor
062: *
063: * @param impl a PnutsImpl object
064: */
065: public SecurePnutsImpl(Implementation impl) {
066: this (impl, null);
067: }
068:
069: /**
070: * A Constructor
071: *
072: * @param impl the base implementation
073: * @param codeSource a CodeSource object which indicates the source of the expression
074: * execute by eval(String, Context).
075: *
076: * @deprecated replaced by SecurePnutsImpl(Implementation, CodeSource)
077: */
078: public SecurePnutsImpl(PnutsImpl impl, CodeSource codeSource) {
079: super (impl);
080: this .codeSource = codeSource;
081: }
082:
083: /**
084: * A Constructor
085: *
086: * @param impl the base implementation
087: * @param codeSource a CodeSource object which indicates the source of the expression
088: * execute by eval(String, Context).
089: */
090: public SecurePnutsImpl(Implementation impl, CodeSource codeSource) {
091: super (impl);
092: this .codeSource = codeSource;
093: }
094:
095: /**
096: * Evaluate an expreesion
097: *
098: * @param expr the expression to be evaluated
099: * @param context the context in which the expression is evaluated
100: * @return the result of the evaluation
101: */
102: public Object eval(final String expr, final Context context) {
103: CodeSource cs = codeSource;
104: if (cs == null) {
105: cs = new CodeSource(null, (Certificate[]) null);
106: }
107: AccessControlContext acc = getAccessControlContext(cs);
108: return AccessController.doPrivileged(new PrivilegedAction() {
109: public Object run() {
110: return SecurePnutsImpl.super .eval(expr, context);
111: }
112: }, acc);
113: }
114:
115: /**
116: * Evaluate a parsed script
117: *
118: * @param node the parsed script
119: * @param context the context in which the script is evaluated
120: * @return the result of the evaluation
121: */
122: public Object accept(final SimpleNode node, final Context context) {
123: CodeSource cs = codeSource;
124: if (cs == null) {
125: cs = new CodeSource(null, (Certificate[]) null);
126: }
127: AccessControlContext acc = getAccessControlContext(cs);
128: return AccessController.doPrivileged(new PrivilegedAction() {
129: public Object run() {
130: return SecurePnutsImpl.super .accept(node, context);
131: }
132: }, acc);
133: }
134:
135: /**
136: * Load a script file using classloader
137: *
138: * @param file the name of the script
139: * @param context the context in which the script is executed
140: * @return the result of the evaluation
141: */
142: public Object load(final String file, final Context context)
143: throws FileNotFoundException {
144: URL url = (URL) AccessController
145: .doPrivileged(new PrivilegedAction() {
146: public Object run() {
147: return Runtime.getScriptURL(file, context);
148: }
149: });
150: if (url == null) {
151: throw new FileNotFoundException(file);
152: }
153: boolean completed = false;
154: try {
155: provide(file, context);
156: Object ret = load(url, context);
157: completed = true;
158: return ret;
159: } finally {
160: if (!completed) {
161: revoke(file, context);
162: }
163: }
164: }
165:
166: /**
167: * Load a script file from local file system
168: *
169: * @param filename
170: * the file name of the script
171: * @param context
172: * the context in which the expression is evaluated
173: * @return the result of the evaluation
174: */
175: public Object loadFile(String filename, Context context)
176: throws FileNotFoundException {
177: URL scriptURL = null;
178: try {
179: File f = new File(filename);
180: if (!f.exists()) {
181: throw new FileNotFoundException(filename);
182: }
183: scriptURL = Runtime.fileToURL(f);
184: } catch (IOException e1) {
185: throw new FileNotFoundException(filename);
186: }
187: return load(scriptURL, context);
188: }
189:
190: /**
191: * Load a script file from a URL
192: *
193: * @param scriptURL the URL of the script
194: * @param context the context in which the script is executed
195: */
196: public Object load(final URL scriptURL, final Context context) {
197: CodeSource cs = codeSource;
198: if (cs == null) {
199: cs = getCodeSource(scriptURL);
200: }
201: AccessControlContext acc = getAccessControlContext(cs);
202: return AccessController.doPrivileged(new PrivilegedAction() {
203: public Object run() {
204: return SecurePnutsImpl.super .load(scriptURL, context);
205: }
206: }, acc);
207: }
208:
209: private static CodeSource getCodeSource(URL scriptURL) {
210: final URL url = scriptURL;
211: return (CodeSource) AccessController
212: .doPrivileged(new PrivilegedAction() {
213: public Object run() {
214: try {
215: URLConnection con = url.openConnection();
216: if (con instanceof JarURLConnection) {
217: JarURLConnection jcon = (JarURLConnection) con;
218: return new CodeSource(jcon
219: .getJarFileURL(), jcon
220: .getCertificates());
221: }
222: } catch (IOException e) {
223: }
224: return new CodeSource(url, (Certificate[]) null);
225: }
226: });
227: }
228:
229: /**
230: * Gets permission declared in the policy file
231: *
232: * @param codesource the CodeSource of the script
233: * @return the permissions for the script
234: */
235: protected PermissionCollection getPolicyPermissions(
236: final CodeSource codesource) {
237: PermissionCollection perms = (PermissionCollection) AccessController
238: .doPrivileged(new PrivilegedAction() {
239: public Object run() {
240: Policy policy = Policy.getPolicy();
241: if (policy != null) {
242: return policy.getPermissions(codesource);
243: } else {
244: return null;
245: }
246: }
247: });
248: if (perms == null) {
249: perms = new Permissions();
250: }
251: return perms;
252: }
253:
254: /**
255: * Add Applet sand-box permissions to the specified PermissionCollection.
256: *
257: * @param codebase the codebase of the script
258: * @param perms the base PermissionCollection
259: */
260: protected void addSandBoxPermissions(URL codebase,
261: PermissionCollection perms) {
262: Permission p = null;
263: try {
264: if (codebase != null) {
265: p = codebase.openConnection().getPermission();
266: }
267: } catch (java.io.IOException ioe) {
268: }
269:
270: if (p instanceof FilePermission) {
271: String path = p.getName();
272: int endIndex = path.lastIndexOf(File.separatorChar);
273: perms.add(p);
274:
275: if (endIndex != -1) {
276: path = path.substring(0, endIndex + 1);
277: if (path.endsWith(File.separator)) {
278: path += "-";
279: }
280: perms.add(new FilePermission(path, "read"));
281: }
282: } else if (codebase != null) {
283: String host = codebase.getHost();
284: if (host == null) {
285: host = "localhost";
286: }
287: perms.add(new SocketPermission(host, "connect, accept"));
288: if (p != null) {
289: perms.add(p);
290: }
291: }
292: }
293:
294: /**
295: * Returns permissions from policy file, plus Applet's sand-box permissions.
296: * A subclass may override this method to define a custom security policy.
297: *
298: * @param codesource the CodeSource of the script
299: * @return the PermissionCollection for the script
300: */
301: protected PermissionCollection getPermissions(CodeSource codesource) {
302: PermissionCollection perms = getPolicyPermissions(codesource);
303: addSandBoxPermissions(codesource.getLocation(), perms);
304: return perms;
305: }
306:
307: private AccessControlContext getAccessControlContext(
308: CodeSource codesource) {
309: PermissionCollection perms = getPermissions(codesource);
310: ProtectionDomain domain = new ProtectionDomain(codesource,
311: perms);
312: return new AccessControlContext(
313: new ProtectionDomain[] { domain });
314: }
315:
316: CodeSource getCodeSource() {
317: return codeSource;
318: }
319:
320: public String toString() {
321: return getClass().getName() + "[" + getBaseImpl() + ", "
322: + codeSource + "]";
323: }
324: }
|