001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans;
043:
044: import java.io.FileDescriptor;
045: import java.lang.reflect.Field;
046: import java.lang.reflect.Method;
047: import java.net.InetAddress;
048: import java.net.URL;
049: import java.net.UnknownHostException;
050: import java.security.AccessControlContext;
051: import java.security.AccessController;
052: import java.security.AllPermission;
053: import java.security.Permission;
054: import java.security.PrivilegedActionException;
055: import java.security.PrivilegedExceptionAction;
056: import java.util.ArrayList;
057: import java.util.HashSet;
058: import java.util.Iterator;
059: import java.util.List;
060: import java.util.Set;
061: import org.openide.util.Lookup;
062:
063: /** NetBeans security manager implementation.
064: * @author Ales Novak, Jesse Glick
065: */
066: public class TopSecurityManager extends SecurityManager {
067:
068: private static boolean check = !Boolean
069: .getBoolean("netbeans.security.nocheck"); // NOI18N
070:
071: private Permission allPermission;
072:
073: /* JVMPI sometimes deadlocks sync getForeignClassLoader
074: and Class.forName
075: */
076: private static final Class<?> classLoaderClass = ClassLoader.class;
077: private static final Class URLClass = URL.class;
078: private static final Class runtimePermissionClass = RuntimePermission.class;
079: private static final Class accessControllerClass = AccessController.class;
080: private static SecurityManager fsSecManager;
081:
082: private static List<SecurityManager> delegates = new ArrayList<SecurityManager>();
083:
084: /** Register a delegate security manager that can handle some checks for us.
085: * Currently only checkExit and checkTopLevelWindow are supported.
086: * @param sm the delegate to register
087: * @throws SecurityException without RuntimePermission "TopSecurityManager.register"
088: */
089: public static void register(SecurityManager sm)
090: throws SecurityException {
091: /* if (check) {
092: try {
093: AccessController.checkPermission(new RuntimePermission("TopSecurityManager.register")); // NOI18N
094: } catch (SecurityException se) {
095: // Something is probably wrong; debug it better.
096: ProtectionDomain pd = sm.getClass().getProtectionDomain();
097: CodeSource cs = pd.getCodeSource();
098: System.err.println("Code source of attempted secman: " + (cs != null ? cs.getLocation().toExternalForm() : "<none>")); // NOI18N
099: System.err.println("Its permissions: " + pd); // NOI18N
100: throw se;
101: }
102: }
103: */
104: synchronized (delegates) {
105: if (delegates.contains(sm))
106: throw new SecurityException();
107: delegates.add(sm);
108: if (fsSecManager == null) {
109: for (Lookup.Item<SecurityManager> item : Lookup
110: .getDefault().lookupResult(
111: SecurityManager.class).allItems()) {
112: if (item != null
113: && "org.netbeans.modules.masterfs.filebasedfs.utils.FileChangedManager"
114: .equals(item.getId())) {//NOI18N
115: fsSecManager = item.getInstance();
116: break;
117: }
118: }
119: assert fsSecManager != null;
120: }
121: }
122: }
123:
124: /** Unregister a delegate security manager.
125: * @param sm the delegate to unregister
126: * @throws SecurityException without RuntimePermission "TopSecurityManager.unregister"
127: */
128: public static void unregister(SecurityManager sm)
129: throws SecurityException {
130: /* if (check) {
131: AccessController.checkPermission(new RuntimePermission("TopSecurityManager.unregister")); // NOI18N
132: }
133: */
134: synchronized (delegates) {
135: if (!delegates.contains(sm))
136: throw new SecurityException();
137: delegates.remove(sm);
138: }
139: }
140:
141: /**
142: * constructs new TopSecurityManager
143: */
144: public TopSecurityManager() {
145: allPermission = new AllPermission();
146: }
147:
148: public @Override
149: void checkExit(int status) throws SecurityException {
150: if (!check) {
151: return;
152: }
153:
154: synchronized (delegates) {
155: Iterator it = delegates.iterator();
156: while (it.hasNext()) {
157: ((SecurityManager) it.next()).checkExit(status);
158: }
159: }
160:
161: PrivilegedCheck.checkExit(status, this );
162: }
163:
164: SecurityManager getSecurityManager() {
165: if (fsSecManager == null) {
166: synchronized (delegates) {
167: return fsSecManager;
168: }
169: }
170: return fsSecManager;
171: }
172:
173: private void notifyDelete(String file) {
174: SecurityManager s = getSecurityManager();
175: if (s != null) {
176: s.checkDelete(file);
177: }
178: }
179:
180: private void notifyWrite(String file) {
181: SecurityManager s = getSecurityManager();
182: if (s != null) {
183: s.checkWrite(file);
184: }
185: }
186:
187: private static boolean officialExit = false;
188:
189: /** Can be called from core classes to exit the system.
190: * Direct calls to System.exit will not be honored, for safety.
191: * @param status the status code to exit with
192: * @see "#20751"
193: */
194: public static void exit(int status) {
195: officialExit = true;
196: System.exit(status);
197: }
198:
199: final void checkExitImpl(int status, AccessControlContext acc)
200: throws SecurityException {
201: if (!officialExit) {
202: throw new ExitSecurityException(
203: "Illegal attempt to exit early"); // NOI18N
204: }
205:
206: super .checkExit(status);
207: }
208:
209: public @Override
210: boolean checkTopLevelWindow(Object window) {
211: synchronized (delegates) {
212: for (SecurityManager sm : delegates) {
213: sm.checkTopLevelWindow(window);
214: }
215: }
216:
217: return super .checkTopLevelWindow(window);
218: }
219:
220: /* XXX probably unnecessary:
221: // Hack against permissions of Launcher$AppLoader.
222: public void checkPackageAccess(String pckg) {
223: if (pckg == null) return;
224: if (pckg.startsWith("sun.")) { // NOI18N
225: if (inClazz("sun.misc.Launcher") || inClazz("java.lang.Class")) { // NOI18N
226: return;
227: }
228: }
229: super.checkPackageAccess(pckg);
230: }
231:
232: private boolean inClazz(String s) {
233: Class[] classes = getClassContext();
234: int i = 0;
235: for (; (i < classes.length) && (classes[i] == TopSecurityManager.class); i++);
236: if (i == classes.length) {
237: return false;
238: }
239: return classes[i].getName().startsWith(s);
240: }
241: */
242:
243: /** Performance - all props accessible */
244: public @Override
245: final void checkPropertyAccess(String x) {
246: if ("netbeans.debug.exceptions".equals(x)) { // NOI18N
247: // Get rid of this old system property.
248: Class[] ctxt = getClassContext();
249: for (int i = 0; i < ctxt.length; i++) {
250: Class c = ctxt[i];
251: if (c != TopSecurityManager.class && c != System.class
252: && c != Boolean.class) {
253: String n = c.getName();
254: synchronized (warnedClassesNDE) {
255: if (warnedClassesNDE.add(n)) {
256: System.err
257: .println("Warning: use of system property netbeans.debug.exceptions in "
258: + n
259: + " has been obsoleted in favor of java.util.logging.Logger"); // NOI18N
260: }
261: }
262: break;
263: }
264: }
265: }
266: if ("netbeans.home".equals(x)) { // NOI18N
267: // Get rid of this old system property.
268: Class[] ctxt = getClassContext();
269: for (int i = 0; i < ctxt.length; i++) {
270: Class c = ctxt[i];
271: if (c != TopSecurityManager.class && c != System.class
272: && c != Boolean.class) {
273: String n = c.getName();
274: synchronized (warnedClassesNH) {
275: if (warnedClassesNH.add(n)) {
276: System.err
277: .println("Warning: use of system property netbeans.home in "
278: + n
279: + " has been obsoleted in favor of InstalledFileLocator"); // NOI18N
280: }
281: }
282: break;
283: }
284: }
285: }
286:
287: if ("javax.xml.parsers.SAXParserFactory".equals(x)) {
288: if (Thread
289: .currentThread()
290: .getContextClassLoader()
291: .getResource(
292: "org/netbeans/core/startup/SAXFactoryImpl.class") != null)
293: return;
294: throw new SecurityException("");
295: }
296:
297: if ("javax.xml.parsers.DocumentBuilderFactory".equals(x)) {
298: if (Thread
299: .currentThread()
300: .getContextClassLoader()
301: .getResource(
302: "org/netbeans/core/startup/DOMFactoryImpl.class") != null)
303: return;
304: throw new SecurityException("");
305: }
306:
307: return;
308: }
309:
310: private final Set<String> warnedClassesNDE = new HashSet<String>(25);
311: private static final Set<String> warnedClassesNH = new HashSet<String>(
312: 25);
313: static {
314: warnedClassesNH.add("org.netbeans.core.LookupCache"); // NOI18N
315: warnedClassesNH.add("org.netbeans.updater.UpdateTracking"); // NOI18N
316: warnedClassesNH
317: .add("org.netbeans.core.ui.ProductInformationPanel"); // #47429; NOI18N
318: warnedClassesNH.add("org.netbeans.lib.uihandler.LogFormatter");
319: }
320:
321: /* ----------------- private methods ------------- */
322:
323: /**
324: * The method is empty. This is not "secure", but on the other hand,
325: * it reduces performance penalty of startup about 10%
326: */
327: public @Override
328: void checkRead(String file) {
329: // XXX reconsider!
330: }
331:
332: public @Override
333: void checkRead(FileDescriptor fd) {
334: }
335:
336: public @Override
337: void checkWrite(FileDescriptor fd) {
338: }
339:
340: /** The method has awful performance in super class */
341: public @Override
342: void checkDelete(String file) {
343: notifyDelete(file);
344: try {
345: checkPermission(allPermission);
346: return;
347: } catch (SecurityException e) {
348: super .checkDelete(file);
349: }
350: }
351:
352: /** The method has awful performance in super class */
353: public @Override
354: void checkWrite(String file) {
355: notifyWrite(file);
356: try {
357: checkPermission(allPermission);
358: return;
359: } catch (SecurityException e) {
360: super .checkWrite(file);
361: }
362: }
363:
364: /** Checks connect */
365: public @Override
366: void checkConnect(String host, int port) {
367: if (!check) {
368: return;
369: }
370:
371: try {
372: checkPermission(allPermission);
373: return;
374: } catch (SecurityException e) {
375: }
376:
377: try {
378: super .checkConnect(host, port);
379: return;
380: } catch (SecurityException e) {
381: }
382:
383: PrivilegedCheck.checkConnect(host, port, this );
384: }
385:
386: final void checkConnectImpl(String host, int port) {
387: Class insecure = getInsecureClass();
388: if (insecure != null) {
389: URL ctx = getClassURL(insecure);
390: if (ctx != null) {
391: try {
392: String fromHost = ctx.getHost();
393: InetAddress ia2 = InetAddress.getByName(host);
394: InetAddress ia3 = InetAddress.getByName(fromHost);
395: if (ia2.equals(ia3)) {
396: return;
397: }
398: } catch (UnknownHostException e) { // ignore
399: e.printStackTrace();
400: }
401: }
402: throw new SecurityException();
403: }
404: }
405:
406: public @Override
407: void checkConnect(String s, int port, Object context) {
408: checkConnect(s, port);
409: }
410:
411: public @Override
412: void checkPermission(Permission perm) {
413: checkSetSecurityManager(perm);
414:
415: //
416: // part of makeSwingUseSpecialClipboard that makes it work on
417: // JDK 1.5
418: //
419: if (perm instanceof java.awt.AWTPermission) {
420: if ("accessClipboard".equals(perm.getName())) { // NOI18N
421: ThreadLocal<Object> t;
422: synchronized (TopSecurityManager.class) {
423: t = CLIPBOARD_FORBIDDEN;
424: }
425: if (t == null) {
426: return;
427: }
428:
429: if (t.get() != null) {
430: t.set(this );
431: throw new SecurityException();
432: } else {
433: checkWhetherAccessedFromSwingTransfer();
434: }
435: }
436: }
437: return;
438: }
439:
440: public @Override
441: void checkPermission(Permission perm, Object context) {
442: checkSetSecurityManager(perm);
443: return;
444: }
445:
446: /** Prohibits to set another SecurityManager */
447: private static void checkSetSecurityManager(Permission perm) {
448: if (runtimePermissionClass.isInstance(perm)) {
449: if ("setSecurityManager".equals(perm.getName())) { // NOI18N - hardcoded in java.lang
450: throw new SecurityException();
451: }
452: }
453: }
454:
455: //
456: // public void checkMemberAccess(Class clazz, int which) {
457: // if ((which == java.lang.reflect.Member.PUBLIC) ||
458: // javax.swing.text.JTextComponent.class.isAssignableFrom(clazz)) {
459: // return;
460: // } else {
461: // super.checkMemberAccess(clazz, which);
462: // }
463: // }
464: //
465: private Class getInsecureClass() {
466:
467: Class[] ctx = getClassContext();
468: boolean firstACClass = false;
469:
470: LOOP: for (int i = 0; i < ctx.length; i++) {
471:
472: if (ctx[i] == accessControllerClass) {
473: // privileged action is on the stack before an untrusted class loader
474: // #3950
475: if (firstACClass) {
476: return null;
477: } else {
478: firstACClass = true;
479: continue LOOP;
480: }
481: } else if (ctx[i].getClassLoader() != null) {
482:
483: if (isSecureClass(ctx[i])) {
484: if (classLoaderClass.isAssignableFrom(ctx[i])) {
485: return null;
486: } else {
487: // OK process next one
488: continue LOOP;
489: }
490: }
491:
492: return ctx[i];
493: } else if (classLoaderClass.isAssignableFrom(ctx[i])) { // cloader == null
494: return null; // foreign classloader wants to do work...
495: }
496: }
497:
498: return null;
499: }
500:
501: /** Checks if the class is loaded through the nbfs URL */
502: static boolean isSecureClass(final Class clazz) {
503: URL source = getClassURL(clazz);
504: if (source != null) {
505: return isSecureProtocol(source.getProtocol());
506: } else {
507: return true;
508: }
509: }
510:
511: /** @return a protocol through which was the class loaded (file://...) or null
512: */
513: static URL getClassURL(Class clazz) {
514: java.security.CodeSource cs = clazz.getProtectionDomain()
515: .getCodeSource();
516: if (cs != null) {
517: URL url = cs.getLocation();
518: return url;
519: } else { // PROXY CLASS?
520: return null;
521: }
522: }
523:
524: static Field getUrlField(Class clazz) {
525: if (urlField == null) {
526: try {
527: Field[] fds = clazz.getDeclaredFields();
528: for (int i = 0; i < fds.length; i++) {
529: if (fds[i].getType() == URLClass) {
530: fds[i].setAccessible(true);
531: urlField = fds[i];
532: break;
533: }
534: }
535: } catch (Exception e) {
536: e.printStackTrace();
537: }
538: }
539: return urlField;
540: }
541:
542: private static Field urlField;
543:
544: /** @return Boolean.TRUE iff the string is a safe protocol (file, nbfs, ...) */
545: static boolean isSecureProtocol(String protocol) {
546: if (protocol.equals("http") || // NOI18N
547: protocol.equals("ftp") || // NOI18N
548: protocol.equals("rmi")) { // NOI18N
549: return false;
550: } else {
551: return true;
552: }
553: }
554:
555: // Workaround for bug
556: //
557: // http://developer.java.sun.com/developer/bugParade/bugs/4818143.html
558: //
559: // sun.awt.datatransfer.ClipboardTransferable.getClipboardData() can hang
560: // for very long time (maxlong == eternity). We tries to avoid the hang by
561: // access the system clipboard from a separate thread. If the hang happens
562: // the thread will wait for the system clipboard forever but not the whole
563: // IDE. See also NbClipboard
564:
565: private static ThreadLocal<Object> CLIPBOARD_FORBIDDEN;
566:
567: /** Convinces Swing components that they should use special clipboard
568: * and not Toolkit.getSystemClipboard.
569: *
570: * @param clip clipboard to use
571: */
572: public static void makeSwingUseSpecialClipboard(
573: java.awt.datatransfer.Clipboard clip) {
574: try {
575: synchronized (TopSecurityManager.class) {
576: assert System.getSecurityManager() instanceof TopSecurityManager : "Our manager has to be active: "
577: + System.getSecurityManager(); // NOI18N
578: if (CLIPBOARD_FORBIDDEN != null) {
579: return;
580: }
581: CLIPBOARD_FORBIDDEN = new ThreadLocal<Object>();
582: CLIPBOARD_FORBIDDEN.set(clip);
583: }
584:
585: javax.swing.JComponent source = new javax.swing.JPanel();
586: javax.swing.TransferHandler.getPasteAction()
587: .actionPerformed(
588: new java.awt.event.ActionEvent(source, 0,
589: ""));
590: javax.swing.TransferHandler.getCopyAction()
591: .actionPerformed(
592: new java.awt.event.ActionEvent(source, 0,
593: ""));
594: javax.swing.TransferHandler.getCutAction().actionPerformed(
595: new java.awt.event.ActionEvent(source, 0, ""));
596: Object forb = CLIPBOARD_FORBIDDEN.get();
597: CLIPBOARD_FORBIDDEN.set(null);
598: if (!(forb instanceof TopSecurityManager)) {
599: System.err
600: .println("Cannot install our clipboard to swing components, TopSecurityManager is not the security manager: "
601: + forb); // NOI18N
602: return;
603: }
604:
605: Class<?> appContextClass = Class
606: .forName("sun.awt.AppContext"); // NOI18N
607: Method getAppContext = appContextClass
608: .getMethod("getAppContext"); // NOI18N
609: Object appContext = getAppContext.invoke(null,
610: new Object[0]);
611:
612: Class actionClass = javax.swing.TransferHandler
613: .getCopyAction().getClass();
614: java.lang.reflect.Field sandboxKeyField = actionClass
615: .getDeclaredField("SandboxClipboardKey"); // NOI18N
616: sandboxKeyField.setAccessible(true);
617: Object value = sandboxKeyField.get(null);
618:
619: Method put = appContextClass.getMethod("put", Object.class,
620: Object.class); // NOI18N
621: put.invoke(appContext, new Object[] { value, clip });
622: } catch (ThreadDeath ex) {
623: throw ex;
624: } catch (Throwable t) {
625: t.printStackTrace();
626: } finally {
627: CLIPBOARD_FORBIDDEN.set(null);
628: }
629: }
630:
631: /** the class that needs to be non accessible */
632: private static Class transferHandlerTransferAction;
633:
634: /** Throws exception if accessed from javax.swing.TransferHandler class
635: */
636: private void checkWhetherAccessedFromSwingTransfer()
637: throws SecurityException {
638: if (transferHandlerTransferAction == null) {
639: try {
640: transferHandlerTransferAction = Class
641: .forName("javax.swing.TransferHandler$TransferAction"); // NOI18N
642: } catch (ClassNotFoundException ex) {
643: ex.printStackTrace();
644: throw new SecurityException(ex.getMessage());
645: }
646: }
647: Class[] arr = getClassContext();
648: for (int i = 0; i < arr.length; i++) {
649: if (arr[i] == transferHandlerTransferAction) {
650: throw new SecurityException(
651: "All swing access to clipboard should be redirected to ExClipboard"); // NOI18N
652: }
653: }
654: }
655:
656: private static final class PrivilegedCheck implements
657: PrivilegedExceptionAction<Object> {
658: int action;
659: TopSecurityManager tsm;
660:
661: // exit
662: int status;
663: AccessControlContext acc;
664:
665: // connect
666: String host;
667: int port;
668:
669: public PrivilegedCheck(int action, TopSecurityManager tsm) {
670: this .action = action;
671: this .tsm = tsm;
672:
673: if (action == 0) {
674: acc = AccessController.getContext();
675: }
676: }
677:
678: public Object run() throws Exception {
679: switch (action) {
680: case 0:
681: tsm.checkExitImpl(status, acc);
682: break;
683: case 1:
684: tsm.checkConnectImpl(host, port);
685: break;
686: default:
687: }
688: return null;
689: }
690:
691: static void checkExit(int status, TopSecurityManager tsm) {
692: PrivilegedCheck pea = new PrivilegedCheck(0, tsm);
693: pea.status = status;
694: check(pea);
695: }
696:
697: static void checkConnect(String host, int port,
698: TopSecurityManager tsm) {
699: PrivilegedCheck pea = new PrivilegedCheck(1, tsm);
700: pea.host = host;
701: pea.port = port;
702: check(pea);
703: }
704:
705: private static void check(PrivilegedCheck action) {
706: try {
707: AccessController.doPrivileged(action);
708: } catch (PrivilegedActionException e) {
709: Exception orig = e.getException();
710: if (orig instanceof RuntimeException) {
711: throw ((RuntimeException) orig);
712: }
713: orig.printStackTrace();
714: }
715: }
716: }
717:
718: }
|