From 1a01dce6c42b398cdd68d405828774a3ab366456 Mon Sep 17 00:00:00 2001 From: Sven Gothel Date: Tue, 11 Jun 2013 16:25:48 +0200 Subject: Bug 752: Review Code Vulnerabilities (Permission Checks of new exposed code and privileged access) This review focuses on how we perform permission checks, or better - do we circumvent some assuming full privileges ? Some native methods do need extra permission validation, i.e. loading native libraries. Further more AccessController.doPrivileged(..) shall not cover generic code exposing a critical feature to the user. Further more .. we should rely on the SecuritManager, i.e. AccessControlContext's 'checkPermission(Permission)' code to comply w/ fine grained permission access. It is also possible to have full permission w/o having any certificates (-> policy file). +++ We remove implicit AccessController.doPrivileged(..) from within our trusted code for generic methods, like Property access, temp. files. +++ SecurityUtil: - Remove 'getCommonAccessControlContext(Class clz)', which returned a local AccessControlContext for later restriction if the passed class contains all certificates as the 'trusted' GlueGen class has. - Simply expose convenient permission check methods relying on SecurityManager / AccessControlContext. PropertyAccess: - 'protected static void addTrustedPrefix(..)' requires AllPermissions if SecurityManager is installed. - Remove implicit doPrivileged(..) triggered by passed AccessControlContext instance, only leave it for trusted prefixes. IOUtil: - Remove all doPrivileged(..) - Elevation shall be performed by caller. DynamicLinker: - 'public long openLibraryLocal(..)' and 'public long openLibraryGlobal(..)' may throw SecurityException, if a SecurityManager is installed and the dyn. link permission is not granted in the calling code. Implemented in their respective Unix, OSX and Windows manifestation. Caller has to elevate privileges via 'doPrivileged(..) {}' ! +++ Tests: - Property access - File access - Native library loading Manual Applet test (unsigned, but w/ SecurityManager and policy file): > gluegen/test/applet Applet has been tested w/ signed JAR w/ Firefox and Java7 on GNU/Linux as well. Manual Application test (unsigned, but w/ SecurityManager and policy file): com.jogamp.junit.sec.TestSecIOUtil01 - Run w/ SecurityManager and policy file: - gluegen/scripts/runtest-secmgr.sh - Run w/o SecurityManager: - gluegen/scripts/runtest.sh --- .../com/jogamp/common/jvm/JNILibLoaderBase.java | 2 +- src/java/com/jogamp/common/os/DynamicLinker.java | 4 +- src/java/com/jogamp/common/os/Platform.java | 112 ++++----- src/java/com/jogamp/common/util/IOUtil.java | 100 ++++---- .../com/jogamp/common/util/PropertyAccess.java | 67 +++--- src/java/com/jogamp/common/util/SecurityUtil.java | 141 +++++++++--- .../jogamp/common/util/cache/TempFileCache.java | 8 +- src/java/com/jogamp/gluegen/Logging.java | 5 +- src/java/jogamp/common/Debug.java | 28 +-- .../jogamp/common/os/MacOSXDynamicLinkerImpl.java | 7 +- .../jogamp/common/os/UnixDynamicLinkerImpl.java | 7 +- .../jogamp/common/os/WindowsDynamicLinkerImpl.java | 6 +- src/junit/com/jogamp/junit/sec/Applet01.java | 254 +++++++++++++++++++++ .../com/jogamp/junit/sec/TestSecIOUtil01.java | 209 +++++++++++++++++ 14 files changed, 726 insertions(+), 224 deletions(-) create mode 100644 src/junit/com/jogamp/junit/sec/Applet01.java create mode 100644 src/junit/com/jogamp/junit/sec/TestSecIOUtil01.java (limited to 'src') diff --git a/src/java/com/jogamp/common/jvm/JNILibLoaderBase.java b/src/java/com/jogamp/common/jvm/JNILibLoaderBase.java index 5621396..456c35a 100644 --- a/src/java/com/jogamp/common/jvm/JNILibLoaderBase.java +++ b/src/java/com/jogamp/common/jvm/JNILibLoaderBase.java @@ -386,7 +386,7 @@ public class JNILibLoaderBase { } } if(null==launcherClass) { - String launcherClassName = PropertyAccess.getProperty("jnlp.launcher.class", false, null); + String launcherClassName = PropertyAccess.getProperty("jnlp.launcher.class", false); if(null!=launcherClassName) { try { launcherClass = Class.forName(launcherClassName); diff --git a/src/java/com/jogamp/common/os/DynamicLinker.java b/src/java/com/jogamp/common/os/DynamicLinker.java index a0da9fb..32aa7eb 100644 --- a/src/java/com/jogamp/common/os/DynamicLinker.java +++ b/src/java/com/jogamp/common/os/DynamicLinker.java @@ -46,8 +46,8 @@ public interface DynamicLinker { public static final boolean DEBUG = NativeLibrary.DEBUG; public static final boolean DEBUG_LOOKUP = NativeLibrary.DEBUG_LOOKUP; - public long openLibraryGlobal(String pathname, boolean debug); - public long openLibraryLocal(String pathname, boolean debug); + public long openLibraryGlobal(String pathname, boolean debug) throws SecurityException; + public long openLibraryLocal(String pathname, boolean debug) throws SecurityException; public long lookupSymbol(long libraryHandle, String symbolName); public long lookupSymbolGlobal(String symbolName); public void closeLibrary(long libraryHandle); diff --git a/src/java/com/jogamp/common/os/Platform.java b/src/java/com/jogamp/common/os/Platform.java index aa9bccd..0ae3cbb 100644 --- a/src/java/com/jogamp/common/os/Platform.java +++ b/src/java/com/jogamp/common/os/Platform.java @@ -162,49 +162,69 @@ public class Platform extends PlatformPropsImpl { /** true if AWT is available and not in headless mode, otherwise false. */ public static final boolean AWT_AVAILABLE; - private static final URI platformClassJarURI; + private static final boolean isRunningFromJarURL; static { - PlatformPropsImpl.initSingleton(); // just documenting the order of static initialization - - { - URI _platformClassJarURI; - try { - _platformClassJarURI = JarUtil.getJarURI(Platform.class.getName(), Platform.class.getClassLoader()); - } catch (Exception e) { - _platformClassJarURI = null; - } - platformClassJarURI = _platformClassJarURI; - } - - USE_TEMP_JAR_CACHE = (OS_TYPE != OSType.ANDROID) && isRunningFromJarURL() && - Debug.getBooleanProperty(useTempJarCachePropName, true, true); + final boolean[] _isRunningFromJarURL = new boolean[] { false }; + final boolean[] _USE_TEMP_JAR_CACHE = new boolean[] { false }; + final boolean[] _AWT_AVAILABLE = new boolean[] { false }; + + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { - AWT_AVAILABLE = AccessController.doPrivileged(new PrivilegedAction() { - public Boolean run() { - // load GluegenRT native library - loadGlueGenRTImpl(); + PlatformPropsImpl.initSingleton(); // documenting the order of static initialization - // JVM bug workaround - JVMUtil.initSingleton(); // requires gluegen-rt, one-time init. + final ClassLoader cl = Platform.class.getClassLoader(); - // detect AWT availability - boolean awtAvailable = false; + final URI platformClassJarURI; { - final ClassLoader cl = Platform.class.getClassLoader(); - if( !Debug.getBooleanProperty("java.awt.headless", true) && - ReflectionUtil.isClassAvailable(ReflectionUtil.AWTNames.ComponentClass, cl) && - ReflectionUtil.isClassAvailable(ReflectionUtil.AWTNames.GraphicsEnvironmentClass, cl) ) { - try { - awtAvailable = false == ((Boolean)ReflectionUtil.callStaticMethod(ReflectionUtil.AWTNames.GraphicsEnvironmentClass, ReflectionUtil.AWTNames.isHeadlessMethod, null, null, cl)).booleanValue(); - } catch (Throwable t) { } + URI _platformClassJarURI = null; + try { + _platformClassJarURI = JarUtil.getJarURI(Platform.class.getName(), cl); + } catch (Exception e) { } + platformClassJarURI = _platformClassJarURI; + } + _isRunningFromJarURL[0] = null != platformClassJarURI; + + _USE_TEMP_JAR_CACHE[0] = ( OS_TYPE != OSType.ANDROID ) && ( null != platformClassJarURI ) && + Debug.getBooleanProperty(useTempJarCachePropName, true, true); + + // load GluegenRT native library + if(_USE_TEMP_JAR_CACHE[0] && TempJarCache.initSingleton()) { + String nativeJarName = null; + URI jarUriRoot = null; + URI nativeJarURI = null; + try { + final String jarName = JarUtil.getJarBasename( platformClassJarURI ); + final String nativeJarBasename = jarName.substring(0, jarName.indexOf(".jar")); // ".jar" already validated w/ JarUtil.getJarBasename(..) + nativeJarName = nativeJarBasename+"-natives-"+PlatformPropsImpl.os_and_arch+".jar"; + jarUriRoot = JarUtil.getURIDirname( JarUtil.getJarSubURI( platformClassJarURI ) ); + nativeJarURI = JarUtil.getJarFileURI(jarUriRoot, nativeJarName); + TempJarCache.bootstrapNativeLib(Platform.class, libBaseName, nativeJarURI); + } catch (Exception e0) { + // IllegalArgumentException, IOException + System.err.println("Catched "+e0.getClass().getSimpleName()+": "+e0.getMessage()+", while TempJarCache.bootstrapNativeLib() of "+nativeJarURI+" ("+jarUriRoot+" + "+nativeJarName+")"); } } - return new Boolean(awtAvailable); - } - }).booleanValue(); - - + DynamicLibraryBundle.GlueJNILibLoader.loadLibrary(libBaseName, false, cl); + + // JVM bug workaround + JVMUtil.initSingleton(); // requires gluegen-rt, one-time init. + + // AWT Headless determination + if( !Debug.getBooleanProperty("java.awt.headless", true) && + ReflectionUtil.isClassAvailable(ReflectionUtil.AWTNames.ComponentClass, cl) && + ReflectionUtil.isClassAvailable(ReflectionUtil.AWTNames.GraphicsEnvironmentClass, cl) ) { + try { + _AWT_AVAILABLE[0] = false == ((Boolean)ReflectionUtil.callStaticMethod(ReflectionUtil.AWTNames.GraphicsEnvironmentClass, ReflectionUtil.AWTNames.isHeadlessMethod, null, null, cl)).booleanValue(); + } catch (Throwable t) { } + } + return null; + } } ); + isRunningFromJarURL = _isRunningFromJarURL[0]; + USE_TEMP_JAR_CACHE = _USE_TEMP_JAR_CACHE[0]; + AWT_AVAILABLE = _AWT_AVAILABLE[0]; + MachineDescription md = MachineDescriptionRuntime.getRuntime(); if(null == md) { MachineDescription.StaticConfig smd = MachineDescriptionRuntime.getStatic(); @@ -228,27 +248,7 @@ public class Platform extends PlatformPropsImpl { * @return true if we're running from a Jar URL, otherwise false */ public static final boolean isRunningFromJarURL() { - return null != platformClassJarURI; - } - - private static final void loadGlueGenRTImpl() { - if(USE_TEMP_JAR_CACHE && TempJarCache.initSingleton()) { - String nativeJarName = null; - URI jarUriRoot = null; - URI nativeJarURI = null; - try { - final String jarName = JarUtil.getJarBasename(platformClassJarURI); - final String nativeJarBasename = jarName.substring(0, jarName.indexOf(".jar")); // ".jar" already validated w/ JarUtil.getJarBasename(..) - nativeJarName = nativeJarBasename+"-natives-"+PlatformPropsImpl.os_and_arch+".jar"; - jarUriRoot = JarUtil.getURIDirname( JarUtil.getJarSubURI( platformClassJarURI ) ); - nativeJarURI = JarUtil.getJarFileURI(jarUriRoot, nativeJarName); - TempJarCache.bootstrapNativeLib(Platform.class, libBaseName, nativeJarURI); - } catch (Exception e0) { - // IllegalArgumentException, IOException - System.err.println("Catched "+e0.getClass().getSimpleName()+": "+e0.getMessage()+", while TempJarCache.bootstrapNativeLib() of "+nativeJarURI+" ("+jarUriRoot+" + "+nativeJarName+")"); - } - } - DynamicLibraryBundle.GlueJNILibLoader.loadLibrary(libBaseName, false, Platform.class.getClassLoader()); + return isRunningFromJarURL; } /** diff --git a/src/java/com/jogamp/common/util/IOUtil.java b/src/java/com/jogamp/common/util/IOUtil.java index 46d6b24..2f0c77f 100644 --- a/src/java/com/jogamp/common/util/IOUtil.java +++ b/src/java/com/jogamp/common/util/IOUtil.java @@ -33,12 +33,10 @@ import java.io.BufferedOutputStream; import java.io.Closeable; import java.io.File; import java.io.FileOutputStream; +import java.io.FilePermission; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.security.AccessControlContext; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.lang.reflect.Constructor; import java.net.URI; import java.net.URISyntaxException; @@ -817,20 +815,12 @@ public class IOUtil { * @param dir the directory to process * @param create true if the directory shall be created if not existing * @param executable true if the user intents to launch executables from the temporary directory, otherwise false. - * @param acc The security {@link AccessControlContext} to create directories and test executability * @throws SecurityException if file creation and process execution is not allowed within the current security context */ - public static File testDir(final File dir, final boolean create, final boolean executable, AccessControlContext acc) + public static File testDir(final File dir, final boolean create, final boolean executable) throws SecurityException { - if( null != acc ) { - return AccessController.doPrivileged(new PrivilegedAction() { - public File run() { - return testDirImpl(dir, create, executable); - } }, acc); - } else { - return testDirImpl(dir, create, executable); - } + return testDirImpl(dir, create, executable); } private static boolean isStringSet(String s) { return null != s && 0 < s.length(); } @@ -873,9 +863,35 @@ public class IOUtil { return tmpBaseDir; } - private static File getTempDirImpl(boolean executable) + /** + * Returns a platform independent writable directory for temporary files + * consisting of the platform's {@code temp-root} + {@link #tmpSubDir}, + * e.g. {@code /tmp/jogamp_0000/}. + *

+ * On standard Java the {@code temp-root} folder is specified by java.io.tempdir. + *

+ *

+ * On Android the {@code temp-root} folder is relative to the applications local folder + * (see {@link Context#getDir(String, int)}) is returned, if + * the Android application/activity has registered it's Application Context + * via {@link jogamp.common.os.android.StaticContext.StaticContext#init(Context, ClassLoader) StaticContext.init(..)}. + * This allows using the temp folder w/o the need for sdcard + * access, which would be the java.io.tempdir location on Android! + *

+ *

+ * In case {@code temp-root} is the users home folder, + * a dot is being prepended to {@link #tmpSubDir}, i.e.: {@code /home/user/.jogamp_0000/}. + *

+ * @param executable true if the user intents to launch executables from the temporary directory, otherwise false. + * @throws RuntimeException if no temporary directory could be determined + * @throws SecurityException if access to java.io.tmpdir is not allowed within the current security context + * + * @see PropertyAccess#getProperty(String, boolean) + * @see Context#getDir(String, int) + */ + public static File getTempDir(final boolean executable) throws SecurityException, RuntimeException - { + { if(!tempRootSet) { // volatile: ok synchronized(IOUtil.class) { if(!tempRootSet) { @@ -889,8 +905,8 @@ public class IOUtil { } } - final String java_io_tmpdir = PropertyAccess.getProperty(java_io_tmpdir_propkey, false, null); - final String user_home = PropertyAccess.getProperty(user_home_propkey, false, null); + final String java_io_tmpdir = PropertyAccess.getProperty(java_io_tmpdir_propkey, false); + final String user_home = PropertyAccess.getProperty(user_home_propkey, false); final String xdg_cache_home; { @@ -951,60 +967,22 @@ public class IOUtil { if(null == r) { throw new RuntimeException("Could not determine a temporary directory"); } + final FilePermission fp = new FilePermission(r.getAbsolutePath(), "read,write,delete"); + SecurityUtil.checkPermission(fp); return r; } private static File tempRootExec = null; // writeable and executable private static File tempRootNoexec = null; // writeable, maybe executable private static volatile boolean tempRootSet = false; - /** - * Returns a platform independent writable directory for temporary files - * consisting of the platform's {@code temp-root} + {@link #tmpSubDir}, - * e.g. {@code /tmp/jogamp_0000/}. - *

- * On standard Java the {@code temp-root} folder is specified by java.io.tempdir. - *

- *

- * On Android the {@code temp-root} folder is relative to the applications local folder - * (see {@link Context#getDir(String, int)}) is returned, if - * the Android application/activity has registered it's Application Context - * via {@link jogamp.common.os.android.StaticContext.StaticContext#init(Context, ClassLoader) StaticContext.init(..)}. - * This allows using the temp folder w/o the need for sdcard - * access, which would be the java.io.tempdir location on Android! - *

- *

- * In case {@code temp-root} is the users home folder, - * a dot is being prepended to {@link #tmpSubDir}, i.e.: {@code /home/user/.jogamp_0000/}. - *

- * @param executable true if the user intents to launch executables from the temporary directory, otherwise false. - * @param acc The security {@link AccessControlContext} to access properties, environment vars, create directories and test executability - * @throws SecurityException if access to java.io.tmpdir is not allowed within the current security context - * @throws RuntimeException if no temporary directory could be determined - * - * @see PropertyAccess#getProperty(String, boolean, java.security.AccessControlContext) - * @see Context#getDir(String, int) - */ - public static File getTempDir(final boolean executable, AccessControlContext acc) - throws SecurityException, RuntimeException - { - if( null != acc ) { - return AccessController.doPrivileged(new PrivilegedAction() { - public File run() { - return getTempDirImpl(executable); - } }, acc); - } else { - return getTempDirImpl(executable); - } - } - /** * Utilizing {@link File#createTempFile(String, String, File)} using - * {@link #getTempRoot(AccessControlContext, boolean)} as the directory parameter, ie. location + * {@link #getTempDir(boolean)} as the directory parameter, ie. location * of the root temp folder. * * @see File#createTempFile(String, String) * @see File#createTempFile(String, String, File) - * @see #getTempRoot(AccessControlContext, boolean) + * @see #getTempDir(boolean) * * @param prefix * @param suffix @@ -1014,10 +992,10 @@ public class IOUtil { * @throws IOException * @throws SecurityException */ - public static File createTempFile(String prefix, String suffix, boolean executable, AccessControlContext acc) + public static File createTempFile(String prefix, String suffix, boolean executable) throws IllegalArgumentException, IOException, SecurityException { - return File.createTempFile( prefix, suffix, getTempDir(executable, acc) ); + return File.createTempFile( prefix, suffix, getTempDir(executable) ); } public static void close(Closeable stream, boolean throwRuntimeException) throws RuntimeException { diff --git a/src/java/com/jogamp/common/util/PropertyAccess.java b/src/java/com/jogamp/common/util/PropertyAccess.java index 51b9533..dde6b50 100644 --- a/src/java/com/jogamp/common/util/PropertyAccess.java +++ b/src/java/com/jogamp/common/util/PropertyAccess.java @@ -48,12 +48,13 @@ public class PropertyAccess { // 'jogamp.' and maybe other trusted prefixes will be added later via 'addTrustedPrefix()' } - 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); - } + /** + * @param prefix New prefix to be registered as trusted. + * @throws AccessControlException as thrown by {@link SecurityUtil#checkAllPermissions()}. + */ + protected static final void addTrustedPrefix(String prefix) throws AccessControlException { + SecurityUtil.checkAllPermissions(); + trustedPrefixes.add(prefix); } public static final boolean isTrusted(String propertyKey) { @@ -65,11 +66,11 @@ public class PropertyAccess { } } - /** @see #getProperty(String, boolean, AccessControlContext) */ - public static final int getIntProperty(final String property, final boolean jnlpAlias, final AccessControlContext acc, int defaultValue) { + /** @see #getProperty(String, boolean) */ + public static final int getIntProperty(final String property, final boolean jnlpAlias, int defaultValue) { int i=defaultValue; try { - final String sv = PropertyAccess.getProperty(property, jnlpAlias, acc); + final String sv = PropertyAccess.getProperty(property, jnlpAlias); if(null!=sv) { i = Integer.valueOf(sv).intValue(); } @@ -77,11 +78,11 @@ public class PropertyAccess { return i; } - /** @see #getProperty(String, boolean, AccessControlContext) */ - public static final long getLongProperty(final String property, final boolean jnlpAlias, final AccessControlContext acc, long defaultValue) { + /** @see #getProperty(String, boolean) */ + public static final long getLongProperty(final String property, final boolean jnlpAlias, long defaultValue) { long l=defaultValue; try { - final String sv = PropertyAccess.getProperty(property, jnlpAlias, acc); + final String sv = PropertyAccess.getProperty(property, jnlpAlias); if(null!=sv) { l = Long.valueOf(sv).longValue(); } @@ -89,23 +90,23 @@ public class PropertyAccess { 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) */ + public static final boolean getBooleanProperty(final String property, final boolean jnlpAlias) { + return Boolean.valueOf(PropertyAccess.getProperty(property, jnlpAlias)).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); + /** @see #getProperty(String, boolean) */ + public static final boolean getBooleanProperty(final String property, final boolean jnlpAlias, boolean defaultValue) { + final String valueS = PropertyAccess.getProperty(property, jnlpAlias); 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) */ + public static final boolean isPropertyDefined(final String property, final boolean jnlpAlias) { + return (PropertyAccess.getProperty(property, jnlpAlias) != null) ? true : false; } /** @@ -119,8 +120,6 @@ public class PropertyAccess { * @param propertyKey the property name to query. * @param jnlpAlias true if a fallback attempt to query the JNLP aliased trusted property 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 @@ -129,7 +128,7 @@ public class PropertyAccess { * * @see System#getProperty(String) */ - public static final String getProperty(final String propertyKey, final boolean jnlpAlias, final AccessControlContext acc) + public static final String getProperty(final String propertyKey, final boolean jnlpAlias) throws SecurityException, NullPointerException, IllegalArgumentException { if(null == propertyKey) { throw new NullPointerException("propertyKey is NULL"); @@ -138,23 +137,13 @@ public class PropertyAccess { throw new IllegalArgumentException("propertyKey is empty"); } String s=null; - // int cause = 0; if( isTrusted(propertyKey) ) { // 'trusted' property (jnlp., javaws., jogamp., ..) s = getTrustedPropKey(propertyKey); - // cause = null != s ? 1 : 0; } else { - if( null != acc ) { - s = AccessController.doPrivileged(new PrivilegedAction() { - public String run() { - return System.getProperty(propertyKey); - } }, acc); - // cause = null != s ? 2 : 0; - } else { - s = System.getProperty(propertyKey); - // cause = null != s ? 3 : 0; - } + // may throw SecurityException, AccessControlerException + s = System.getProperty(propertyKey); } if( null == s && jnlpAlias ) { // Try 'jnlp.' aliased property .. @@ -162,11 +151,8 @@ public class PropertyAccess { // Properties within the namespace "jnlp." or "javaws." should be considered trusted, // i.e. always granted w/o special privileges. s = getTrustedPropKey(jnlp_prefix + propertyKey); - // cause = null != s ? 4 : 0; } - } - // System.err.println("Prop: <"+propertyKey+"> = <"+s+">, cause "+cause); - + } return s; } @@ -177,7 +163,6 @@ public class PropertyAccess { 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 index 4583201..4d7aa5d 100644 --- a/src/java/com/jogamp/common/util/SecurityUtil.java +++ b/src/java/com/jogamp/common/util/SecurityUtil.java @@ -27,30 +27,127 @@ */ package com.jogamp.common.util; -import java.security.AccessControlContext; import java.security.AccessController; +import java.security.AllPermission; import java.security.CodeSource; +import java.security.Permission; 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; + private static final SecurityManager securityManager; + private static final Permission allPermissions; + private static final boolean DEBUG = false; static { - localACC = AccessController.doPrivileged(new PrivilegedAction() { - public AccessControlContext run() { - return AccessController.getContext(); - } } ); - localCerts = getCerts(SecurityUtil.class); + allPermissions = new AllPermission(); + securityManager = System.getSecurityManager(); + + if( DEBUG ) { + final boolean hasAllPermissions; + { + final ProtectionDomain insecPD = AccessController.doPrivileged(new PrivilegedAction() { + public ProtectionDomain run() { + return SecurityUtil.class.getProtectionDomain(); + } } ); + boolean _hasAllPermissions; + try { + insecPD.implies(allPermissions); + _hasAllPermissions = true; + } catch( SecurityException ace ) { + _hasAllPermissions = false; + } + hasAllPermissions = _hasAllPermissions; + } + + System.err.println("SecurityUtil: Has SecurityManager: "+ ( null != securityManager ) ) ; + System.err.println("SecurityUtil: Has AllPermissions: "+hasAllPermissions); + final Certificate[] certs = AccessController.doPrivileged(new PrivilegedAction() { + public Certificate[] run() { + return getCerts(SecurityUtil.class); + } } ); + System.err.println("SecurityUtil: Cert count: "+ ( null != certs ? certs.length : 0 )); + if( null != certs ) { + for(int i=0; itrue if no {@link SecurityManager} has been installed + * or the installed {@link SecurityManager}'s checkPermission(new AllPermission()) + * passes. Otherwise method returns false. + */ + public static final boolean hasAllPermissions() { + return hasPermission(allPermissions); + } + + /** + * Returns true if no {@link SecurityManager} has been installed + * or the installed {@link SecurityManager}'s checkPermission(perm) + * passes. Otherwise method returns false. + */ + public static final boolean hasPermission(Permission perm) { + try { + checkPermission(perm); + return true; + } catch( SecurityException ace ) { + return false; + } + } + + /** + * Throws an {@link SecurityException} if an installed {@link SecurityManager} + * does not permit the requested {@link AllPermission}. + */ + public static final void checkAllPermissions() throws SecurityException { + checkPermission(allPermissions); + } + + /** + * Throws an {@link SecurityException} if an installed {@link SecurityManager} + * does not permit the requested {@link Permission}. + */ + public static final void checkPermission(Permission perm) throws SecurityException { + if( null != securityManager ) { + securityManager.checkPermission(perm); + } + } + + /** + * Returns true if no {@link SecurityManager} has been installed + * or the installed {@link SecurityManager}'s checkLink(libName) + * passes. Otherwise method returns false. + */ + public static final boolean hasLinkPermission(String libName) { + try { + checkLinkPermission(libName); + return true; + } catch( SecurityException ace ) { + return false; + } } - public static final Certificate[] getCerts(final Class clz) { - final ProtectionDomain pd = AccessController.doPrivileged(new PrivilegedAction() { - public ProtectionDomain run() { - return clz.getProtectionDomain(); - } } ); + /** + * Throws an {@link SecurityException} if an installed {@link SecurityManager} + * does not permit to dynamically link the given libName. + */ + public static final void checkLinkPermission(String libName) throws SecurityException { + if( null != securityManager ) { + securityManager.checkLink(libName); + } + } + + /** + * @param clz + * @return + * @throws SecurityException if the caller has no permission to access the ProtectedDomain of the given class. + */ + public static final Certificate[] getCerts(final Class clz) throws SecurityException { + final ProtectionDomain pd = 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; @@ -72,21 +169,5 @@ public class SecurityUtil { 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 755ed7b..32f5c2a 100644 --- a/src/java/com/jogamp/common/util/cache/TempFileCache.java +++ b/src/java/com/jogamp/common/util/cache/TempFileCache.java @@ -33,10 +33,8 @@ 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; @@ -72,8 +70,6 @@ public class TempFileCache { private File individualTmpDir; static { - final AccessControlContext acc = SecurityUtil.getCommonAccessControlContext(TempFileCache.class); - // Global Lock ! synchronized (System.out) { // Create / initialize the temp root directory, starting the Reaper @@ -81,8 +77,8 @@ public class TempFileCache { // exception, set an error code. File _tmpBaseDir = null; try { - _tmpBaseDir = new File(IOUtil.getTempDir(true /* executable */, acc), tmpDirPrefix); - _tmpBaseDir = IOUtil.testDir(_tmpBaseDir, true /* create */, false /* executable */, acc); // executable already checked + _tmpBaseDir = new File(IOUtil.getTempDir(true /* executable */), tmpDirPrefix); + _tmpBaseDir = IOUtil.testDir(_tmpBaseDir, true /* create */, false /* executable */); // executable already checked } catch (Exception ex) { System.err.println("Warning: Catched Exception while retrieving temp base directory:"); ex.printStackTrace(); diff --git a/src/java/com/jogamp/gluegen/Logging.java b/src/java/com/jogamp/gluegen/Logging.java index 87d6196..d8c1b5a 100644 --- a/src/java/com/jogamp/gluegen/Logging.java +++ b/src/java/com/jogamp/gluegen/Logging.java @@ -38,7 +38,6 @@ import java.util.logging.LogRecord; import java.util.logging.Logger; import com.jogamp.common.util.PropertyAccess; -import com.jogamp.common.util.SecurityUtil; /** * @@ -48,11 +47,11 @@ public class Logging { static void init() { final String packageName = Logging.class.getPackage().getName(); - final String property = PropertyAccess.getProperty(packageName+".level", true, SecurityUtil.getCommonAccessControlContext(Logging.class)); + final String property = PropertyAccess.getProperty(packageName+".level", true); Level level; if(property != null) { level = Level.parse(property); - }else{ + } else { level = Level.WARNING; } diff --git a/src/java/jogamp/common/Debug.java b/src/java/jogamp/common/Debug.java index 19e2fd7..b8a32d8 100644 --- a/src/java/jogamp/common/Debug.java +++ b/src/java/jogamp/common/Debug.java @@ -1,5 +1,6 @@ /* - * Copyright (c) 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2010 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 @@ -39,6 +40,9 @@ package jogamp.common; +import java.security.AccessController; +import java.security.PrivilegedAction; + import com.jogamp.common.util.PropertyAccess; /** Helper routines for logging and debugging. */ @@ -49,28 +53,16 @@ public class Debug extends PropertyAccess { private static final boolean debugAll; static { - PropertyAccess.addTrustedPrefix("jogamp.", Debug.class); + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + PropertyAccess.addTrustedPrefix("jogamp."); + return null; + } } ); verbose = isPropertyDefined("jogamp.verbose", true); debugAll = isPropertyDefined("jogamp.debug", true); } - public static final boolean isPropertyDefined(final String property, final boolean jnlpAlias) { - return PropertyAccess.isPropertyDefined(property, jnlpAlias, null); - } - - public static final boolean getBooleanProperty(final String property, final boolean jnlpAlias) { - return PropertyAccess.getBooleanProperty(property, jnlpAlias, null); - } - - public static final boolean getBooleanProperty(final String property, final boolean jnlpAlias, boolean defaultValue) { - return PropertyAccess.getBooleanProperty(property, jnlpAlias, null, defaultValue); - } - - public static final long getLongProperty(final String property, final boolean jnlpAlias, long defaultValue) { - return PropertyAccess.getLongProperty(property, jnlpAlias, null, defaultValue); - } - public static boolean verbose() { return verbose; } diff --git a/src/java/jogamp/common/os/MacOSXDynamicLinkerImpl.java b/src/java/jogamp/common/os/MacOSXDynamicLinkerImpl.java index 4eb381f..95f7e63 100644 --- a/src/java/jogamp/common/os/MacOSXDynamicLinkerImpl.java +++ b/src/java/jogamp/common/os/MacOSXDynamicLinkerImpl.java @@ -3,6 +3,7 @@ package jogamp.common.os; import com.jogamp.common.os.DynamicLinker; +import com.jogamp.common.util.SecurityUtil; public class MacOSXDynamicLinkerImpl implements DynamicLinker { @@ -28,7 +29,7 @@ public class MacOSXDynamicLinkerImpl implements DynamicLinker { // --- Begin CustomJavaCode .cfg declarations - public long openLibraryLocal(String pathname, boolean debug) { + public long openLibraryLocal(String pathname, boolean debug) throws SecurityException { // Note we use RTLD_LOCAL visibility to _NOT_ allow this functionality to // be used to pre-resolve dependent libraries of JNI code without // requiring that all references to symbols in those libraries be @@ -36,10 +37,11 @@ public class MacOSXDynamicLinkerImpl implements DynamicLinker { // other words, one can actually link against the library instead of // having to dlsym all entry points. System.loadLibrary() uses // RTLD_LOCAL visibility so can't be used for this purpose. + SecurityUtil.checkLinkPermission(pathname); return dlopen(pathname, RTLD_LAZY | RTLD_LOCAL); } - public long openLibraryGlobal(String pathname, boolean debug) { + public long openLibraryGlobal(String pathname, boolean debug) throws SecurityException { // Note we use RTLD_GLOBAL visibility to allow this functionality to // be used to pre-resolve dependent libraries of JNI code without // requiring that all references to symbols in those libraries be @@ -47,6 +49,7 @@ public class MacOSXDynamicLinkerImpl implements DynamicLinker { // other words, one can actually link against the library instead of // having to dlsym all entry points. System.loadLibrary() uses // RTLD_LOCAL visibility so can't be used for this purpose. + SecurityUtil.checkLinkPermission(pathname); return dlopen(pathname, RTLD_LAZY | RTLD_GLOBAL); } diff --git a/src/java/jogamp/common/os/UnixDynamicLinkerImpl.java b/src/java/jogamp/common/os/UnixDynamicLinkerImpl.java index 29998bd..2258dfa 100644 --- a/src/java/jogamp/common/os/UnixDynamicLinkerImpl.java +++ b/src/java/jogamp/common/os/UnixDynamicLinkerImpl.java @@ -3,6 +3,7 @@ package jogamp.common.os; import com.jogamp.common.os.DynamicLinker; +import com.jogamp.common.util.SecurityUtil; public class UnixDynamicLinkerImpl implements DynamicLinker { @@ -27,7 +28,7 @@ public class UnixDynamicLinkerImpl implements DynamicLinker { // --- Begin CustomJavaCode .cfg declarations - public long openLibraryLocal(String pathname, boolean debug) { + public long openLibraryLocal(String pathname, boolean debug) throws SecurityException { // Note we use RTLD_GLOBAL visibility to _NOT_ allow this functionality to // be used to pre-resolve dependent libraries of JNI code without // requiring that all references to symbols in those libraries be @@ -35,10 +36,11 @@ public class UnixDynamicLinkerImpl implements DynamicLinker { // other words, one can actually link against the library instead of // having to dlsym all entry points. System.loadLibrary() uses // RTLD_LOCAL visibility so can't be used for this purpose. + SecurityUtil.checkLinkPermission(pathname); return dlopen(pathname, RTLD_LAZY | RTLD_LOCAL); } - public long openLibraryGlobal(String pathname, boolean debug) { + public long openLibraryGlobal(String pathname, boolean debug) throws SecurityException { // Note we use RTLD_GLOBAL visibility to allow this functionality to // be used to pre-resolve dependent libraries of JNI code without // requiring that all references to symbols in those libraries be @@ -46,6 +48,7 @@ public class UnixDynamicLinkerImpl implements DynamicLinker { // other words, one can actually link against the library instead of // having to dlsym all entry points. System.loadLibrary() uses // RTLD_LOCAL visibility so can't be used for this purpose. + SecurityUtil.checkLinkPermission(pathname); return dlopen(pathname, RTLD_LAZY | RTLD_GLOBAL); } diff --git a/src/java/jogamp/common/os/WindowsDynamicLinkerImpl.java b/src/java/jogamp/common/os/WindowsDynamicLinkerImpl.java index e7f5b52..eb02584 100644 --- a/src/java/jogamp/common/os/WindowsDynamicLinkerImpl.java +++ b/src/java/jogamp/common/os/WindowsDynamicLinkerImpl.java @@ -3,6 +3,7 @@ package jogamp.common.os; import com.jogamp.common.os.DynamicLinker; +import com.jogamp.common.util.SecurityUtil; public class WindowsDynamicLinkerImpl implements DynamicLinker { @@ -20,13 +21,14 @@ public class WindowsDynamicLinkerImpl implements DynamicLinker { // --- Begin CustomJavaCode .cfg declarations - public long openLibraryLocal(String libraryName, boolean debug) { + public long openLibraryLocal(String libraryName, boolean debug) throws SecurityException { // How does that work under Windows ? // Don't know .. so it's an alias for the time being return openLibraryGlobal(libraryName, debug); } - public long openLibraryGlobal(String libraryName, boolean debug) { + public long openLibraryGlobal(String libraryName, boolean debug) throws SecurityException { + SecurityUtil.checkLinkPermission(libraryName); long handle = LoadLibraryW(libraryName); if(0==handle && debug) { int err = GetLastError(); diff --git a/src/junit/com/jogamp/junit/sec/Applet01.java b/src/junit/com/jogamp/junit/sec/Applet01.java new file mode 100644 index 0000000..12e8f48 --- /dev/null +++ b/src/junit/com/jogamp/junit/sec/Applet01.java @@ -0,0 +1,254 @@ +/** + * Copyright 2013 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.junit.sec; + +import java.applet.Applet; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.net.URISyntaxException; +import java.net.URL; +import java.security.AccessControlException; + +import com.jogamp.common.os.MachineDescription; +import com.jogamp.common.os.NativeLibrary; +import com.jogamp.common.os.Platform; +import com.jogamp.common.util.IOUtil; +import com.jogamp.common.util.JarUtil; + +/** + * Applet: Provoke AccessControlException while writing to file! + */ +@SuppressWarnings("serial") +public class Applet01 extends Applet { + static final String java_io_tmpdir_propkey = "java.io.tmpdir"; + static final String java_home_propkey = "java.home"; + static final String os_name_propkey = "os.name"; + + static final String tfilename = "test.bin" ; + static final MachineDescription machine = Platform.getMachineDescription(); + static final int tsz = machine.pageSizeInBytes(); + + static final boolean usesSecurityManager; + + static { + if( null == System.getSecurityManager() ) { + usesSecurityManager = false; + System.err.println("No SecurityManager Installed"); + } else { + usesSecurityManager = true; + System.err.println("SecurityManager Already Installed"); + } + } + + static void testPropImpl(String propKey, boolean isSecure) { + isSecure |= !usesSecurityManager; + + Exception se0 = null; + try { + String p0 = System.getProperty(propKey); + System.err.println(propKey+": "+p0); + } catch (AccessControlException e) { + se0 = e; + if( !isSecure ) { + System.err.println("Expected exception for insecure property <"+propKey+">"); + System.err.println("Message: "+se0.getMessage()); + } else { + System.err.println("Unexpected exception for secure property <"+propKey+">"); + se0.printStackTrace(); + } + } + if( isSecure ) { + if( null != se0 ) { + throw new Error("AccessControlException thrown on secure property <"+propKey+">", se0); + } + } else { + if( null == se0 ) { + throw new Error("AccessControlException not thrown on secure property <"+propKey+">"); + } + } + } + + static void testTempDirImpl(boolean isSecure) { + isSecure |= !usesSecurityManager; + + Exception se0 = null; + try { + File tmp = IOUtil.getTempDir(true); + System.err.println("Temp: "+tmp); + } catch (AccessControlException e) { + se0 = e; + if( !isSecure ) { + System.err.println("Expected exception for insecure temp dir"); + System.err.println("Message: "+se0.getMessage()); + } else { + System.err.println("Unexpected exception for secure temp dir"); + se0.printStackTrace(); + } + } + if( isSecure ) { + if( null != se0 ) { + throw new Error("AccessControlException thrown on secure temp dir", se0); + } + } else { + if( null == se0 ) { + throw new Error("AccessControlException not thrown on secure temp dir"); + } + } + } + + private void testWriteFile() { + AccessControlException sec01 = null; + try { + File tmp = IOUtil.getTempDir(true); + System.err.println("Temp: "+tmp); + byte[] orig = new byte[tsz]; + final File tfile = new File(tmp, tfilename); + final OutputStream tout = new BufferedOutputStream(new FileOutputStream(tfile)); + for(int i=0; i clazz = this.getClass(); + URL libURL = clazz.getResource("/libtest1.so"); + if( null != libURL ) { + libBaseName = "libtest1.so"; + } else { + libURL = clazz.getResource("/test1.dll"); + if( null != libURL ) { + libBaseName = "test1.dll"; + } + } + System.err.println("Untrusted Library (URL): "+libURL); + + String libDir1 = null; + if( null != libURL ) { + try { + libDir1 = JarUtil.getJarSubURI(libURL.toURI()).getPath(); + } catch (Exception e) { + e.printStackTrace(); + } + if( null != libDir1 ) { + System.err.println("libDir1.1: "+libDir1); + try { + libDir1= IOUtil.getParentOf(libDir1); + } catch (URISyntaxException e) { + e.printStackTrace(); + } + System.err.println("libDir1.2: "+libDir1); + } + } + System.err.println("Untrusted Library Dir1 (abs): "+libDir1); + final String absLib = libDir1 + "natives/" + libBaseName; + Exception sec01 = null; + try { + NativeLibrary nlib = NativeLibrary.open(absLib, cl); + System.err.println("NativeLibrary: "+nlib); + } catch (SecurityException e) { + sec01 = e; + if( usesSecurityManager ) { + System.err.println("Expected exception for loading native library"); + System.err.println("Message: "+sec01.getMessage()); + } else { + System.err.println("Unexpected exception for loading native library"); + sec01.printStackTrace(); + } + } + if( !usesSecurityManager ) { + if( null != sec01 ) { + throw new Error("SecurityException thrown on loading native library", sec01); + } + } else { + if( null == sec01 ) { + throw new Error("SecurityException not thrown on loading native library"); + } + } + } + + public void init() { + + } + + public void start() { + Platform.initSingleton(); + + { + testPropImpl(os_name_propkey, true); + } + System.err.println("p0: OK"); + { + testPropImpl(java_home_propkey, false); + } + System.err.println("p1: OK"); + { + testPropImpl(java_io_tmpdir_propkey, false); + } + System.err.println("p2: OK"); + { + testTempDirImpl(false); + } + System.err.println("temp0: OK"); + + testWriteFile(); + System.err.println("writeFile: OK"); + + testOpenLibrary(true); + System.err.println("lib0: OK"); + } + + public void stop() { + + } +} diff --git a/src/junit/com/jogamp/junit/sec/TestSecIOUtil01.java b/src/junit/com/jogamp/junit/sec/TestSecIOUtil01.java new file mode 100644 index 0000000..c47e2df --- /dev/null +++ b/src/junit/com/jogamp/junit/sec/TestSecIOUtil01.java @@ -0,0 +1,209 @@ +/** + * Copyright 2013 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.junit.sec; + +import java.net.URISyntaxException; +import java.net.URL; +import java.security.AccessControlException; +import java.io.File; +import java.io.IOException; + +import junit.framework.Assert; + +import org.junit.BeforeClass; +import org.junit.Test; + +import com.jogamp.common.os.NativeLibrary; +import com.jogamp.common.os.Platform; +import com.jogamp.common.util.IOUtil; +import com.jogamp.common.util.JarUtil; +import com.jogamp.junit.util.JunitTracer; + +public class TestSecIOUtil01 extends JunitTracer { + static final String java_io_tmpdir_propkey = "java.io.tmpdir"; + static final String java_home_propkey = "java.home"; + static final String os_name_propkey = "os.name"; + static final boolean usesSecurityManager; + + static { + if( null == System.getSecurityManager() ) { + usesSecurityManager = false; + System.err.println("No SecurityManager Installed"); + } else { + usesSecurityManager = true; + System.err.println("SecurityManager Already Installed"); + } + } + + @BeforeClass + public static void setup() throws IOException { + Platform.initSingleton(); + } + + static void testPropImpl01(String propKey, boolean isSecure) { + isSecure |= !usesSecurityManager; + + Exception se0 = null; + try { + String p0 = System.getProperty(propKey); + System.err.println(propKey+": "+p0); + } catch (AccessControlException e) { + se0 = e; + if( !isSecure ) { + System.err.println("Expected exception for insecure property <"+propKey+">"); + System.err.println("Message: "+se0.getMessage()); + } else { + System.err.println("Unexpected exception for secure property <"+propKey+">"); + se0.printStackTrace(); + } + } + if( isSecure ) { + Assert.assertNull("AccessControlException thrown on secure property <"+propKey+">", se0); + } else { + Assert.assertNotNull("AccessControlException not thrown on insecure property <"+propKey+">", se0); + } + } + + @Test + public void testProp00_Temp() { + testPropImpl01(os_name_propkey, true); + } + + @Test + public void testProp01_Temp() { + testPropImpl01(java_home_propkey, false); + } + + @Test + public void testProp02_Temp() { + testPropImpl01(java_io_tmpdir_propkey, false); + } + + static void testTempDirImpl(boolean isSecure) { + isSecure |= !usesSecurityManager; + + Exception se0 = null; + try { + File tmp = IOUtil.getTempDir(true); + System.err.println("Temp: "+tmp); + } catch (AccessControlException e) { + se0 = e; + if( !isSecure ) { + System.err.println("Expected exception for insecure temp dir"); + System.err.println("Message: "+se0.getMessage()); + } else { + System.err.println("Unexpected exception for secure temp dir"); + se0.printStackTrace(); + } + } + if( isSecure ) { + Assert.assertNull("AccessControlException thrown on secure temp dir", se0); + } else { + Assert.assertNotNull("AccessControlException not thrown on insecure temp dir", se0); + } + } + + @Test + public void testTempDir00() { + testTempDirImpl(false); + } + + private void testOpenLibraryImpl(boolean global) { + final ClassLoader cl = getClass().getClassLoader(); + System.err.println("CL "+cl); + + String libBaseName = null; + final Class clazz = this.getClass(); + URL libURL = clazz.getResource("/libtest1.so"); + if( null != libURL ) { + libBaseName = "libtest1.so"; + } else { + libURL = clazz.getResource("/test1.dll"); + if( null != libURL ) { + libBaseName = "test1.dll"; + } + } + System.err.println("Untrusted Library (URL): "+libURL); + + String libDir1 = null; + if( null != libURL ) { + try { + libDir1 = JarUtil.getJarSubURI(libURL.toURI()).getPath(); + } catch (Exception e) { + e.printStackTrace(); + } + if( null != libDir1 ) { + System.err.println("libDir1.1: "+libDir1); + try { + libDir1= IOUtil.getParentOf(libDir1); + } catch (URISyntaxException e) { + e.printStackTrace(); + } + System.err.println("libDir1.2: "+libDir1); + } + } + System.err.println("Untrusted Library Dir1 (abs): "+libDir1); + final String absLib = libDir1 + "natives/" + libBaseName; + Exception se0 = null; + try { + NativeLibrary nlib = NativeLibrary.open(absLib, cl); + System.err.println("NativeLibrary: "+nlib); + } catch (SecurityException e) { + se0 = e; + if( usesSecurityManager ) { + System.err.println("Expected exception for loading native library"); + System.err.println("Message: "+se0.getMessage()); + } else { + System.err.println("Unexpected exception for loading native library"); + se0.printStackTrace(); + } + } + if( !usesSecurityManager ) { + Assert.assertNull("SecurityException thrown on loading native library", se0); + } else { + Assert.assertNotNull("SecurityException not thrown on loading native library", se0); + } + } + + public void testOpenLibrary() { + testOpenLibraryImpl(true); + } + + public static void main(String args[]) throws IOException { + TestSecIOUtil01.setup(); + + TestSecIOUtil01 aa = new TestSecIOUtil01(); + aa.testProp00_Temp(); + aa.testProp01_Temp(); + aa.testProp02_Temp(); + aa.testTempDir00(); + aa.testOpenLibrary(); + } + +} -- cgit v1.2.3