001 package groovy.lang; 002 003 import groovy.security.GroovyCodeSourcePermission; 004 005 import java.io.ByteArrayInputStream; 006 import java.io.File; 007 import java.io.FileInputStream; 008 import java.io.FileNotFoundException; 009 import java.io.IOException; 010 import java.io.InputStream; 011 import java.net.MalformedURLException; 012 import java.net.URL; 013 import java.security.AccessController; 014 import java.security.CodeSource; 015 import java.security.PrivilegedActionException; 016 import java.security.PrivilegedExceptionAction; 017 import java.security.cert.Certificate; 018 019 /** 020 * CodeSource wrapper class that allows specific security policies to be associated with a class 021 * compiled from groovy source. 022 * 023 * @author Steve Goetze 024 */ 025 public class GroovyCodeSource { 026 027 /** 028 * The codeSource to be given the generated class. This can be used by policy file 029 * grants to administer security. 030 */ 031 private CodeSource codeSource; 032 /** The name given to the generated class */ 033 private String name; 034 /** The groovy source to be compiled and turned into a class */ 035 private InputStream inputStream; 036 /** The certificates used to sign the items from the codesource */ 037 Certificate[] certs; 038 039 private File file; 040 041 public GroovyCodeSource(String script, String name, String codeBase) { 042 this(new ByteArrayInputStream(script.getBytes()), name, codeBase); 043 } 044 045 /** 046 * Construct a GroovyCodeSource for an inputStream of groovyCode that has an 047 * unknown provenance -- meaning it didn't come from a File or a URL (e.g. a String). 048 * The supplied codeBase will be used to construct a File URL that should match up 049 * with a java Policy entry that determines the grants to be associated with the 050 * class that will be built from the InputStream. 051 * 052 * The permission groovy.security.GroovyCodeSourcePermission will be used to determine if the given codeBase 053 * may be specified. That is, the current Policy set must have a GroovyCodeSourcePermission that implies 054 * the codeBase, or an exception will be thrown. This is to prevent callers from hijacking 055 * existing codeBase policy entries unless explicitly authorized by the user. 056 */ 057 public GroovyCodeSource(InputStream inputStream, String name, String codeBase) { 058 this.inputStream = inputStream; 059 this.name = name; 060 SecurityManager sm = System.getSecurityManager(); 061 if (sm != null) { 062 sm.checkPermission(new GroovyCodeSourcePermission(codeBase)); 063 } 064 try { 065 this.codeSource = new CodeSource(new URL("file", "", codeBase), (java.security.cert.Certificate[])null); 066 } catch (MalformedURLException murle) { 067 throw new RuntimeException("A CodeSource file URL cannot be constructed from the supplied codeBase: " + codeBase); 068 } 069 } 070 071 /** 072 * Package private constructor called by GroovyClassLoader for signed jar entries 073 */ 074 GroovyCodeSource(InputStream inputStream, String name, final File path, final Certificate[] certs) { 075 this.inputStream = inputStream; 076 this.name = name; 077 try { 078 this.codeSource = (CodeSource) AccessController.doPrivileged( new PrivilegedExceptionAction() { 079 public Object run() throws MalformedURLException { 080 //toURI().toURL() will encode, but toURL() will not. 081 return new CodeSource(path.toURI().toURL(), certs); 082 } 083 }); 084 } catch (PrivilegedActionException pae) { 085 //shouldn't happen 086 throw new RuntimeException("Could not construct a URL from: " + path); 087 } 088 } 089 090 public GroovyCodeSource(final File file) throws FileNotFoundException { 091 if (!file.exists()) 092 throw new FileNotFoundException(file.toString() + " (" + file.getAbsolutePath() + ")"); 093 else { 094 try { 095 if (!file.canRead()) 096 throw new RuntimeException(file.toString() + " can not be read. Check the read permisson of the file \"" + file.toString() + "\" (" + file.getAbsolutePath() + ")."); 097 } 098 catch (SecurityException e) { 099 throw e; 100 } 101 } 102 103 //this.inputStream = new FileInputStream(file); 104 this.file = file; 105 this.inputStream = null; 106 //The calls below require access to user.dir - allow here since getName() and getCodeSource() are 107 //package private and used only by the GroovyClassLoader. 108 try { 109 Object[] info = (Object[]) AccessController.doPrivileged( new PrivilegedExceptionAction() { 110 public Object run() throws MalformedURLException { 111 Object[] info = new Object[2]; 112 info[0] = file.getAbsolutePath(); 113 //toURI().toURL() will encode, but toURL() will not. 114 info[1] = new CodeSource(file.toURI().toURL(), (Certificate[]) null); 115 return info; 116 } 117 }); 118 this.name = (String) info[0]; 119 this.codeSource = (CodeSource) info[1]; 120 } catch (PrivilegedActionException pae) { 121 throw new RuntimeException("Could not construct a URL from: " + file); 122 } 123 } 124 125 public GroovyCodeSource(URL url) throws IOException { 126 if (url == null) { 127 throw new RuntimeException("Could not construct a GroovyCodeSource from a null URL"); 128 } 129 this.inputStream = url.openStream(); 130 this.name = url.toExternalForm(); 131 this.codeSource = new CodeSource(url, (java.security.cert.Certificate[])null); 132 } 133 134 CodeSource getCodeSource() { 135 return codeSource; 136 } 137 138 public InputStream getInputStream() { 139 try { 140 if (file!=null) return new FileInputStream(file); 141 } catch (FileNotFoundException fnfe) {} 142 return inputStream; 143 } 144 145 public String getName() { 146 return name; 147 } 148 149 public File getFile() { 150 return file; 151 } 152 }