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