diff options
Diffstat (limited to 'src/java/com/jogamp/common/util')
-rw-r--r-- | src/java/com/jogamp/common/util/IOUtil.java | 53 | ||||
-rw-r--r-- | src/java/com/jogamp/common/util/JarUtil.java | 22 | ||||
-rw-r--r-- | src/java/com/jogamp/common/util/PropertyAccess.java | 178 | ||||
-rw-r--r-- | src/java/com/jogamp/common/util/SecurityUtil.java | 92 | ||||
-rw-r--r-- | src/java/com/jogamp/common/util/cache/TempFileCache.java | 10 | ||||
-rw-r--r-- | src/java/com/jogamp/common/util/cache/TempJarCache.java | 6 | ||||
-rw-r--r-- | src/java/com/jogamp/common/util/locks/Lock.java | 7 |
7 files changed, 322 insertions, 46 deletions
diff --git a/src/java/com/jogamp/common/util/IOUtil.java b/src/java/com/jogamp/common/util/IOUtil.java index cdbeab6..0ae8521 100644 --- a/src/java/com/jogamp/common/util/IOUtil.java +++ b/src/java/com/jogamp/common/util/IOUtil.java @@ -36,15 +36,13 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.security.AccessController; +import java.security.AccessControlContext; import java.net.JarURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.nio.ByteBuffer; -import jogamp.common.Debug; -import jogamp.common.PropertyAccess; import jogamp.common.os.android.StaticContext; import android.content.Context; @@ -55,7 +53,10 @@ import com.jogamp.common.os.MachineDescription; import com.jogamp.common.os.Platform; public class IOUtil { - private static final boolean DEBUG = Debug.isPropertyDefined("jogamp.debug.IOUtil", true, AccessController.getContext()); + private static final boolean DEBUG = PropertyAccess.isPropertyDefined("jogamp.debug.IOUtil", true); + + /** Std. temporary directory property key <code>java.io.tmpdir</code> */ + public static final String java_io_tmpdir_propkey = "java.io.tmpdir"; private IOUtil() {} @@ -475,12 +476,12 @@ public class IOUtil { /** * Utilizing {@link File#createTempFile(String, String, File)} using - * {@link #getTempRoot()} as the directory parameter, ie. location + * {@link #getTempRoot(AccessControlContext)} as the directory parameter, ie. location * of the root temp folder. * * @see File#createTempFile(String, String) * @see File#createTempFile(String, String, File) - * @see #getTempRoot() + * @see #getTempRoot(AccessControlContext) * * @param prefix * @param suffix @@ -489,15 +490,18 @@ public class IOUtil { * @throws IOException * @throws SecurityException */ - public static File createTempFile(String prefix, String suffix) + public static File createTempFile(String prefix, String suffix, AccessControlContext acc) throws IllegalArgumentException, IOException, SecurityException - { - return File.createTempFile( prefix, suffix, getTempRoot() ); + { + return File.createTempFile( prefix, suffix, getTempRoot(acc) ); } /** + * Returns a platform independent writable directory for temporary files. + * <p> * On standard Java, the folder specified by <code>java.io.tempdir</code> * is returned. + * </p> * <p> * On Android a <code>temp</code> folder relative to the applications local folder * (see {@link Context#getDir(String, int)}) is returned, if @@ -506,32 +510,39 @@ public class IOUtil { * This allows using the temp folder w/o the need for <code>sdcard</code> * access, which would be the <code>java.io.tempdir</code> location on Android! * </p> - * <p> - * The purpose of this <code>wrapper</code> is to allow unique code to be used - * for both platforms w/o the need to handle extra permissions. - * </p> - * - * @throws SecurityException - * @throws RuntimeException + * @param acc The security {@link AccessControlContext} to access <code>java.io.tmpdir</code> * + * @throws SecurityException if access to <code>java.io.tmpdir</code> is not allowed within the current security context + * @throws RuntimeException is the property <code>java.io.tmpdir</code> or the resulting temp directory is invalid + * + * @see PropertyAccess#getProperty(String, boolean, java.security.AccessControlContext) * @see StaticContext#setContext(Context) * @see Context#getDir(String, int) */ - public static File getTempRoot() + public static File getTempRoot(AccessControlContext acc) throws SecurityException, RuntimeException { if(AndroidVersion.isAvailable) { final Context ctx = StaticContext.getContext(); if(null != ctx) { final File tmpRoot = ctx.getDir("temp", Context.MODE_WORLD_READABLE); + if(null==tmpRoot|| !tmpRoot.isDirectory() || !tmpRoot.canWrite()) { + throw new RuntimeException("Not a writable directory: '"+tmpRoot+"', retrieved Android static context"); + } if(DEBUG) { System.err.println("IOUtil.getTempRoot(Android): temp dir: "+tmpRoot.getAbsolutePath()); } return tmpRoot; } } - final String tmpRootName = PropertyAccess.getProperty("java.io.tmpdir", false, AccessController.getContext()); + final String tmpRootName = PropertyAccess.getProperty(java_io_tmpdir_propkey, false, acc); + if(null == tmpRootName || 0 == tmpRootName.length()) { + throw new RuntimeException("Property '"+java_io_tmpdir_propkey+"' value is empty: <"+tmpRootName+">"); + } final File tmpRoot = new File(tmpRootName); + if(null==tmpRoot || !tmpRoot.isDirectory() || !tmpRoot.canWrite()) { + throw new RuntimeException("Not a writable directory: '"+tmpRoot+"', retrieved by propery '"+java_io_tmpdir_propkey+"'"); + } if(DEBUG) { System.err.println("IOUtil.getTempRoot(isAndroid: "+AndroidVersion.isAvailable+"): temp dir: "+tmpRoot.getAbsolutePath()); } @@ -552,7 +563,7 @@ public class IOUtil { * } * } * </pre> - * The <code>tempRootDir</code> is retrieved by {@link #getTempRoot()}. + * The <code>tempRootDir</code> is retrieved by {@link #getTempRoot(AccessControlContext)}. * <p> * The iteration through [000000-999999] ensures that the code is multi-user save. * </p> @@ -561,10 +572,10 @@ public class IOUtil { * @throws IOException * @throws SecurityException */ - public static File getTempDir(String tmpDirPrefix) + public static File getTempDir(String tmpDirPrefix, AccessControlContext acc) throws IOException, SecurityException { - final File tempRoot = IOUtil.getTempRoot(); + final File tempRoot = IOUtil.getTempRoot(acc); for(int i = 0; i<=999999; i++) { final String tmpDirSuffix = String.format("_%06d", i); // 6 digits for iteration diff --git a/src/java/com/jogamp/common/util/JarUtil.java b/src/java/com/jogamp/common/util/JarUtil.java index bd63a56..85a10ce 100644 --- a/src/java/com/jogamp/common/util/JarUtil.java +++ b/src/java/com/jogamp/common/util/JarUtil.java @@ -539,26 +539,20 @@ public class JarUtil { // InputStream in order to be able to get its certificates InputStream is = jar.getInputStream(entry); - while (is.read(buf) > 0) { } - is.close(); + try { + while (is.read(buf) > 0) { } + } finally { + is.close(); + } // Get the certificates for the JAR entry - Certificate[] nativeCerts = entry.getCertificates(); + final Certificate[] nativeCerts = entry.getCertificates(); if (nativeCerts == null || nativeCerts.length == 0) { throw new SecurityException("no certificate for " + entry.getName() + " in " + jar.getName()); } - int checked = 0; - for (int i = 0; i < rootCerts.length; i++) { - for (int j = 0; j < nativeCerts.length; j++) { - if (nativeCerts[j].equals(rootCerts[i])){ - checked++; - break; - } - } - } - if( checked != rootCerts.length ) { - throw new SecurityException("not all certificates match, only "+checked+" out of "+rootCerts.length+" for " + entry.getName() + " in " + jar.getName()); + if( !SecurityUtil.equals(rootCerts, nativeCerts) ) { + throw new SecurityException("certificates not equal for " + entry.getName() + " in " + jar.getName()); } } } diff --git a/src/java/com/jogamp/common/util/PropertyAccess.java b/src/java/com/jogamp/common/util/PropertyAccess.java new file mode 100644 index 0000000..f0d49e0 --- /dev/null +++ b/src/java/com/jogamp/common/util/PropertyAccess.java @@ -0,0 +1,178 @@ +/** + * Copyright 2012 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.common.util; + +import java.security.*; +import java.util.HashSet; + + +/** Helper routines for accessing properties. */ +public class PropertyAccess { + /** trusted build-in property prefix 'jnlp.' */ + public static final String jnlp_prefix = "jnlp." ; + /** trusted build-in property prefix 'javaws.' */ + public static final String javaws_prefix = "javaws."; + + static final HashSet<String> trustedPrefixes; + + static { + trustedPrefixes = new HashSet<String>(); + trustedPrefixes.add(javaws_prefix); + trustedPrefixes.add(jnlp_prefix); + } + + public static final void addTrustedPrefix(String prefix, Class<?> certClass) { + if(SecurityUtil.equalsLocalCert(certClass)) { + trustedPrefixes.add(prefix); + } else { + throw new SecurityException("Illegal Access - prefix "+prefix+", with cert class "+certClass); + } + } + + public static final boolean isTrusted(String propertyKey) { + int dot1 = propertyKey.indexOf('.'); + if(0<=dot1) { + return trustedPrefixes.contains(propertyKey.substring(0, dot1+1)); + } else { + return false; + } + } + + /** @see #getProperty(String, boolean, AccessControlContext) */ + public static final int getIntProperty(final String property, final boolean jnlpAlias, final AccessControlContext acc, int defaultValue) { + int i=defaultValue; + try { + final String sv = PropertyAccess.getProperty(property, jnlpAlias, acc); + if(null!=sv) { + i = Integer.valueOf(sv).intValue(); + } + } catch (NumberFormatException nfe) {} + return i; + } + + /** @see #getProperty(String, boolean, AccessControlContext) */ + public static final long getLongProperty(final String property, final boolean jnlpAlias, final AccessControlContext acc, long defaultValue) { + long l=defaultValue; + try { + final String sv = PropertyAccess.getProperty(property, jnlpAlias, acc); + if(null!=sv) { + l = Long.valueOf(sv).longValue(); + } + } catch (NumberFormatException nfe) {} + return l; + } + + /** @see #getProperty(String, boolean, AccessControlContext) */ + public static final boolean getBooleanProperty(final String property, final boolean jnlpAlias, final AccessControlContext acc) { + return Boolean.valueOf(PropertyAccess.getProperty(property, jnlpAlias, acc)).booleanValue(); + } + + /** @see #getProperty(String, boolean, AccessControlContext) */ + public static final boolean getBooleanProperty(final String property, final boolean jnlpAlias, final AccessControlContext acc, boolean defaultValue) { + final String valueS = PropertyAccess.getProperty(property, jnlpAlias, acc); + if(null != valueS) { + return Boolean.valueOf(valueS).booleanValue(); + } + return defaultValue; + } + + /** @see #getProperty(String, boolean, AccessControlContext) */ + public static final boolean isPropertyDefined(final String property, final boolean jnlpAlias, final AccessControlContext acc) { + return (PropertyAccess.getProperty(property, jnlpAlias, acc) != null) ? true : false; + } + + /** @see #getProperty(String, boolean, AccessControlContext) */ + public static final boolean isPropertyDefined(final String property, final boolean jnlpAlias) { + return (PropertyAccess.getProperty(property, jnlpAlias, null) != null) ? true : false; + } + + /** + * Query the property with the name <code>propertyKey</code>. + * <p> + * If <code>jnlpAlias</code> is <code>true</code> and the plain <code>propertyKey</code> + * could not be resolved, an attempt to resolve the JNLP aliased <i>trusted property</i> is made.<br> + * Example: For the propertyName <code>OneTwo</code>, the jnlp alias name is <code>jnlp.OneTwo</code>, which is considered trusted.<br> + * </p> + * + * @param propertyKey the property name to query. + * @param jnlpAlias true if a fallback attempt to query the JNLP aliased <i>trusted property</i> shall be made, + * otherwise false. + * @param acc the AccessControlerContext to be used for privileged access to the system property, or null. + * + * @return the property value if exists, or null + * + * @throws NullPointerException if the property name is null + * @throws IllegalArgumentException if the property name is of length 0 + * @throws SecurityException if access is not allowed to the given <code>propertyKey</code> + * + * @see System#getProperty(String) + */ + public static final String getProperty(final String propertyKey, final boolean jnlpAlias, final AccessControlContext acc) + throws SecurityException, NullPointerException, IllegalArgumentException { + if(null == propertyKey) { + throw new NullPointerException("propertyKey is NULL"); + } + if(0 == propertyKey.length()) { + throw new IllegalArgumentException("propertyKey is empty"); + } + if(isTrusted(propertyKey)) { + return getTrustedPropKey(propertyKey); + } + String s=null; + if( null!=acc ) { + s = AccessController.doPrivileged(new PrivilegedAction<String>() { + public String run() { + return System.getProperty(propertyKey); + } }, acc); + } else { + s = System.getProperty(propertyKey); + } + if(null==s && jnlpAlias) { + // Properties within the namespace "jnlp." or "javaws." should be considered trusted, + // i.e. always granted w/o special priviledges. + // FIXME: Nevertheless we use this class AccessControlContext to ensure access + // on all supported implementations. + return getTrustedPropKey(jnlp_prefix + propertyKey); + } + return s; + } + + private static final String getTrustedPropKey(final String propertyKey) { + return AccessController.doPrivileged(new PrivilegedAction<String>() { + public String run() { + try { + return System.getProperty(propertyKey); + } catch (SecurityException se) { + throw new SecurityException("Could not access trusted property '"+propertyKey+"'", se); + + } + } + }); + } +} diff --git a/src/java/com/jogamp/common/util/SecurityUtil.java b/src/java/com/jogamp/common/util/SecurityUtil.java new file mode 100644 index 0000000..4583201 --- /dev/null +++ b/src/java/com/jogamp/common/util/SecurityUtil.java @@ -0,0 +1,92 @@ +/** + * Copyright 2012 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.common.util; + +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.CodeSource; +import java.security.PrivilegedAction; +import java.security.ProtectionDomain; +import java.security.cert.Certificate; + +public class SecurityUtil { + /* package private */ static final AccessControlContext localACC; + /* package private */ static final Certificate[] localCerts; + + static { + localACC = AccessController.doPrivileged(new PrivilegedAction<AccessControlContext>() { + public AccessControlContext run() { + return AccessController.getContext(); + } } ); + localCerts = getCerts(SecurityUtil.class); + } + + public static final Certificate[] getCerts(final Class<?> clz) { + final ProtectionDomain pd = AccessController.doPrivileged(new PrivilegedAction<ProtectionDomain>() { + public ProtectionDomain run() { + return clz.getProtectionDomain(); + } } ); + final CodeSource cs = (null != pd) ? pd.getCodeSource() : null; + final Certificate[] certs = (null != cs) ? cs.getCertificates() : null; + return (null != certs && certs.length>0) ? certs : null; + } + + public static final boolean equals(Certificate[] a, Certificate[] b) { + if(a == b) { + return true; + } + if(a==null || b==null) { + return false; + } + if(a.length != b.length) { + return false; + } + + int i = 0; + while( i < a.length && a[i].equals(b[i]) ) { + i++; + } + return i == a.length; + } + + public static final boolean equalsLocalCert(Certificate[] b) { + return equals(localCerts, b); + } + + public static final boolean equalsLocalCert(Class<?> clz) { + return equalsLocalCert(getCerts(clz)); + } + + public static final AccessControlContext getCommonAccessControlContext(Class<?> clz) { + if(equalsLocalCert(clz)) { + return localACC; + } else { + return null; + } + } +} diff --git a/src/java/com/jogamp/common/util/cache/TempFileCache.java b/src/java/com/jogamp/common/util/cache/TempFileCache.java index c3b24aa..51c698e 100644 --- a/src/java/com/jogamp/common/util/cache/TempFileCache.java +++ b/src/java/com/jogamp/common/util/cache/TempFileCache.java @@ -33,8 +33,10 @@ import java.io.FilenameFilter; import java.io.IOException; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; +import java.security.AccessControlContext; import com.jogamp.common.util.IOUtil; +import com.jogamp.common.util.SecurityUtil; import jogamp.common.Debug; @@ -70,14 +72,16 @@ public class TempFileCache { private File individualTmpDir; static { + final AccessControlContext acc = SecurityUtil.getCommonAccessControlContext(TempFileCache.class); + // Global Lock ! - synchronized (System.out) { + synchronized (System.out) { // Create / initialize the temp root directory, starting the Reaper // thread to reclaim old installations if necessary. If we get an // exception, set an error code. File _tmpBaseDir = null; - try { - _tmpBaseDir = IOUtil.getTempDir(tmpDirPrefix); // Retrieve the tmpbase directory. + try { + _tmpBaseDir = IOUtil.getTempDir(tmpDirPrefix, acc); // Retrieve the tmpbase directory. } catch (Exception ex) { System.err.println("Warning: Catched Exception while retrieving temp base directory:"); ex.printStackTrace(); diff --git a/src/java/com/jogamp/common/util/cache/TempJarCache.java b/src/java/com/jogamp/common/util/cache/TempJarCache.java index 162b151..96e68df 100644 --- a/src/java/com/jogamp/common/util/cache/TempJarCache.java +++ b/src/java/com/jogamp/common/util/cache/TempJarCache.java @@ -50,6 +50,7 @@ import jogamp.common.Debug; import com.jogamp.common.os.NativeLibrary; import com.jogamp.common.util.IOUtil; import com.jogamp.common.util.JarUtil; +import com.jogamp.common.util.SecurityUtil; public class TempJarCache { private static final boolean DEBUG = Debug.debug("TempJarCache"); @@ -391,9 +392,8 @@ public class TempJarCache { if(null == certClass) { throw new IllegalArgumentException("certClass is null"); } - final Certificate[] rootCerts = - certClass.getProtectionDomain().getCodeSource().getCertificates(); - if( null != rootCerts && rootCerts.length>0 ) { + final Certificate[] rootCerts = SecurityUtil.getCerts(certClass); + if( null != rootCerts ) { // Only validate the jarFile's certs with ours, if we have any. // Otherwise we may run uncertified JARs (application). // In case one tries to run uncertified JARs, the wrapping applet/JNLP diff --git a/src/java/com/jogamp/common/util/locks/Lock.java b/src/java/com/jogamp/common/util/locks/Lock.java index b2d6acc..cf3fd01 100644 --- a/src/java/com/jogamp/common/util/locks/Lock.java +++ b/src/java/com/jogamp/common/util/locks/Lock.java @@ -29,9 +29,6 @@ package com.jogamp.common.util.locks; import jogamp.common.Debug; -import jogamp.common.PropertyAccess; - -import java.security.AccessController; /** * Specifying a thread blocking lock implementation @@ -39,7 +36,7 @@ import java.security.AccessController; public interface Lock { /** Enable via the property <code>jogamp.debug.Lock.TraceLock</code> */ - public static final boolean TRACE_LOCK = PropertyAccess.isPropertyDefined("jogamp.debug.Lock.TraceLock", true, AccessController.getContext()); + public static final boolean TRACE_LOCK = Debug.isPropertyDefined("jogamp.debug.Lock.TraceLock", true); /** Enable via the property <code>jogamp.debug.Lock</code> */ public static final boolean DEBUG = Debug.debug("Lock"); @@ -52,7 +49,7 @@ public interface Lock { * and defaults to {@link #DEFAULT_TIMEOUT}.<br> * It can be overridden via the system property <code>jogamp.common.utils.locks.Lock.timeout</code>. */ - public static final long TIMEOUT = PropertyAccess.getLongProperty("jogamp.common.utils.locks.Lock.timeout", true, AccessController.getContext(), DEFAULT_TIMEOUT); + public static final long TIMEOUT = Debug.getLongProperty("jogamp.common.utils.locks.Lock.timeout", true, DEFAULT_TIMEOUT); /** * Blocking until the lock is acquired by this Thread or {@link #TIMEOUT} is reached. |