1 // Copyright (C) 2001-2003 Jon A. Maxwell (JAM) 2 // 3 // This library is free software; you can redistribute it and/or 4 // modify it under the terms of the GNU Lesser General Public 5 // License as published by the Free Software Foundation; either 6 // version 2.1 of the License, or (at your option) any later version. 7 // 8 // This library is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 // Lesser General Public License for more details. 12 // 13 // You should have received a copy of the GNU Lesser General Public 14 // License along with this library; if not, write to the Free Software 15 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 16 17 package net.sourceforge.jnlp.services; 18 19 import java.lang.reflect.InvocationHandler; 20 import java.lang.reflect.InvocationTargetException; 21 import java.lang.reflect.Method; 22 import java.security.AccessController; 23 import java.security.PrivilegedAction; 24 import java.security.PrivilegedActionException; 25 import java.security.PrivilegedExceptionAction; 26 27 import javax.jnlp.BasicService; 28 import javax.jnlp.ClipboardService; 29 import javax.jnlp.DownloadService; 30 import javax.jnlp.ExtensionInstallerService; 31 import javax.jnlp.FileOpenService; 32 import javax.jnlp.FileSaveService; 33 import javax.jnlp.PersistenceService; 34 import javax.jnlp.PrintService; 35 import javax.jnlp.ServiceManager; 36 import javax.jnlp.SingleInstanceService; 37 import javax.jnlp.UnavailableServiceException; 38 39 import net.sourceforge.jnlp.JNLPFile; 40 import net.sourceforge.jnlp.config.DeploymentConfiguration; 41 import net.sourceforge.jnlp.runtime.ApplicationInstance; 42 import net.sourceforge.jnlp.runtime.JNLPRuntime; 43 import net.sourceforge.jnlp.security.SecurityDialogs; 44 import net.sourceforge.jnlp.security.SecurityDialogs.AccessType; 45 import net.sourceforge.jnlp.util.logging.OutputController; 46 47 /** 48 * Provides static methods to interact useful for using the JNLP 49 * services. 50 * 51 * @author <a href="mailto:jmaxwell@users.sourceforge.net">Jon A. Maxwell (JAM)</a> - initial author 52 * @author <a href="mailto:jsumali@redhat.com">Joshua Sumali</a> 53 * @version $Revision: 1.8 $ 54 */ 55 public class ServiceUtil { 56 57 /** 58 * @return the BasicService reference, or null if the service is 59 * unavailable. 60 */ getBasicService()61 public static BasicService getBasicService() { 62 return (BasicService) getService("javax.jnlp.BasicService"); 63 } 64 65 /** 66 * @return the ClipboardService reference, or null if the service is 67 * unavailable. 68 */ getClipboardService()69 public static ClipboardService getClipboardService() { 70 return (ClipboardService) getService("javax.jnlp.ClipboardService"); 71 } 72 73 /** 74 * @return the DownloadService reference, or null if the service is 75 * unavailable. 76 */ getDownloadService()77 public static DownloadService getDownloadService() { 78 return (DownloadService) getService("javax.jnlp.DownloadService"); 79 } 80 81 /** 82 * @return the ExtensionInstallerService reference, or null if the service is 83 * unavailable. 84 */ getExtensionInstallerService()85 public static ExtensionInstallerService getExtensionInstallerService() { 86 return (ExtensionInstallerService) getService("javax.jnlp.ExtensionInstallerService"); 87 } 88 89 /** 90 * @return the FileOpenService reference, or null if the service is 91 * unavailable. 92 */ getFileOpenService()93 public static FileOpenService getFileOpenService() { 94 return (FileOpenService) getService("javax.jnlp.FileOpenService"); 95 } 96 97 /** 98 * @return the FileSaveService reference, or null if the service is 99 * unavailable. 100 */ getFileSaveService()101 public static FileSaveService getFileSaveService() { 102 return (FileSaveService) getService("javax.jnlp.FileSaveService"); 103 } 104 105 /** 106 * @return the PersistenceService reference, or null if the service is 107 * unavailable. 108 */ getPersistenceService()109 public static PersistenceService getPersistenceService() { 110 return (PersistenceService) getService("javax.jnlp.PersistenceService"); 111 } 112 113 /** 114 * @return the PrintService reference, or null if the service is 115 * unavailable. 116 */ getPrintService()117 public static PrintService getPrintService() { 118 return (PrintService) getService("javax.jnlp.PrintService"); 119 } 120 121 /** 122 * @return the SingleInstanceService reference, or null if the service is 123 * unavailable. 124 */ getSingleInstanceService()125 public static SingleInstanceService getSingleInstanceService() { 126 return (SingleInstanceService) getService("javax.jnlp.SingleInstanceService"); 127 } 128 129 /** 130 * Checks that this application (represented by the jnlp) isnt already running 131 * @param jnlpFile the {@link JNLPFile} that specifies the application 132 * 133 * @throws InstanceExistsException if an instance of this application already exists 134 */ checkExistingSingleInstance(JNLPFile jnlpFile)135 public static void checkExistingSingleInstance(JNLPFile jnlpFile) { 136 ExtendedSingleInstanceService esis = (ExtendedSingleInstanceService) getSingleInstanceService(); 137 esis.checkSingleInstanceRunning(jnlpFile); 138 } 139 140 /** 141 * Returns the service, or null instead of an UnavailableServiceException 142 */ getService(String name)143 private static Object getService(String name) { 144 try { 145 return ServiceManager.lookup(name); 146 } catch (UnavailableServiceException ex) { 147 return null; 148 } 149 } 150 151 /** 152 * Creates a Proxy object implementing the specified interface 153 * when makes all calls in the security context of the system 154 * classes (ie, AllPermissions). This means that the services 155 * must be more than extremely careful in the operations they 156 * perform. 157 */ createPrivilegedProxy(Class<?> iface, final Object receiver)158 static Object createPrivilegedProxy(Class<?> iface, final Object receiver) { 159 return java.lang.reflect.Proxy.newProxyInstance(XServiceManagerStub.class.getClassLoader(), 160 new Class<?>[] { iface }, 161 new PrivilegedHandler(receiver)); 162 } 163 164 /** 165 * calls the object's method using privileged access 166 */ 167 private static class PrivilegedHandler implements InvocationHandler { 168 private final Object receiver; 169 PrivilegedHandler(Object receiver)170 PrivilegedHandler(Object receiver) { 171 this.receiver = receiver; 172 } 173 174 @Override invoke(Object proxy, final Method method, final Object[] args)175 public Object invoke(Object proxy, final Method method, final Object[] args) throws Throwable { 176 if (JNLPRuntime.isDebug()) { 177 OutputController.getLogger().log(OutputController.Level.ERROR_DEBUG, "call privileged method: " + method.getName()); 178 if (args != null) { 179 for (Object arg : args) { 180 OutputController.getLogger().log(OutputController.Level.ERROR_DEBUG, " arg: " + arg); 181 } 182 } 183 } 184 185 PrivilegedExceptionAction<Object> invoker = new PrivilegedExceptionAction<Object>() { 186 @Override 187 public Object run() throws Exception { 188 return method.invoke(receiver, args); 189 } 190 }; 191 192 try { 193 Object result = AccessController.doPrivileged(invoker); 194 195 OutputController.getLogger().log(OutputController.Level.ERROR_DEBUG, " result: " + result); 196 197 return result; 198 } catch (PrivilegedActionException e) { 199 // Any exceptions thrown by the actual methods are wrapped by a 200 // InvocationTargetException, which is further wrapped by the 201 // PrivilegedActionException. Lets unwrap them to make the 202 // proxy transparent to the callers 203 if (e.getCause() instanceof InvocationTargetException) { 204 throw e.getCause().getCause(); 205 } else { 206 throw e.getCause(); 207 } 208 } 209 210 } 211 }; 212 213 /** 214 * Returns whether the app requesting a JNLP service has the right permissions. 215 * If it doesn't, user is prompted for permissions. This method should only be 216 * used for JNLP API related permissions. 217 * 218 * @param type the type of access being requested 219 * @param extras extra Strings (usually) that are passed to the dialog for 220 * message formatting. 221 * @return true if the access was granted, false otherwise. 222 */ checkAccess(AccessType type, Object... extras)223 public static boolean checkAccess(AccessType type, Object... extras) { 224 return checkAccess(null, type, extras); 225 } 226 227 /** 228 * Returns whether the app requesting a JNLP service has the right permissions. 229 * If it doesn't, user is prompted for permissions. This method should only be 230 * used for JNLP API related permissions. 231 * 232 * @param app the application which is requesting the check. If null, the current 233 * application is used. 234 * @param type the type of access being requested 235 * @param extras extra Strings (usually) that are passed to the dialog for 236 * message formatting. 237 * @return true if the access was granted, false otherwise. 238 */ checkAccess(ApplicationInstance app, AccessType type, Object... extras)239 public static boolean checkAccess(ApplicationInstance app, AccessType type, 240 Object... extras) { 241 242 boolean trusted = isSigned(app); 243 244 if (!trusted) { 245 246 if (!shouldPromptUser()) { 247 return false; 248 } 249 if (app == null) { 250 app = JNLPRuntime.getApplication(); 251 } 252 253 final AccessType tmpType = type; 254 final Object[] tmpExtras = extras; 255 final ApplicationInstance tmpApp = app; 256 257 //We need to do this to allow proper icon loading for unsigned 258 //applets, otherwise permissions won't be granted to load icons 259 //from resources.jar. 260 Boolean b = AccessController.doPrivileged(new PrivilegedAction<Boolean>() { 261 @Override 262 public Boolean run() { 263 boolean b = SecurityDialogs.showAccessWarningDialogB(tmpType, 264 tmpApp.getJNLPFile(), tmpExtras); 265 return b; 266 } 267 }); 268 269 return b; 270 } 271 272 return true; //allow 273 } 274 275 /** 276 * Returns whether the current runtime configuration allows prompting the 277 * user for JNLP permissions. 278 * 279 * @return true if the user should be prompted for JNLP API related permissions. 280 */ shouldPromptUser()281 private static boolean shouldPromptUser() { 282 return AccessController.doPrivileged(new PrivilegedAction<Boolean >() { 283 @Override 284 public Boolean run() { 285 return Boolean.valueOf(JNLPRuntime.getConfiguration() 286 .getProperty(DeploymentConfiguration.KEY_SECURITY_PROMPT_USER_FOR_JNLP)); 287 } 288 }); 289 } 290 291 /** 292 * Returns whether the app requesting a JNLP service is a trusted 293 * application 294 * 295 * @param app 296 * the application which is requesting the check. If null, the 297 * current application is used. 298 * @return true, if the app is a trusted application; false otherwise 299 */ 300 301 public static boolean isSigned(ApplicationInstance app) { 302 303 if (app == null) { 304 app = JNLPRuntime.getApplication(); 305 } 306 307 StackTraceElement[] stack = Thread.currentThread().getStackTrace(); 308 309 for (StackTraceElement stack1 : stack) { 310 Class<?> c = null; 311 try { 312 c = Class.forName(stack1.getClassName()); 313 } catch (Exception e1) { 314 OutputController.getLogger().log(e1); 315 try { 316 c = Class.forName(stack1.getClassName(), false, app.getClassLoader()); 317 }catch (Exception e2) { 318 OutputController.getLogger().log(e2); 319 } 320 } 321 // Everything up to the desired class/method must be trusted 322 if (c == null || // class not found 323 (c.getProtectionDomain().getCodeSource() != null && // class is not in bootclasspath 324 c.getProtectionDomain().getCodeSource().getCodeSigners() == null) // class is trusted 325 ) { 326 return false; 327 } 328 } 329 return true; 330 } 331 332 } 333