Mercurial > hg > release > icedtea6-1.6
changeset 689:a2497d56461b cacao
2008-02-05 Joshua Sumali <jsumali@redhat.com>
* tools/netx/jnlp/Launcher.java: Removed unused line.
* tools/netx/jnlp/resources/Messages.properties: Added security messages
for security dialogs.
* tools/netx/jnlp/runtime/ApplicationInstance.java: Added signing field.
* tools/netx/jnlp/runtime/Boot.java: Use jar root default.jnlp
* tools/netx/jnlp/runtime/JNLPClassLoader.java: Added security warning
dialogs when running signed code.
* tools/netx/jnlp/runtime/JNLPRuntime.java: Enable code signing
verification by default.
* tools/netx/jnlp/services/ServiceUtil.java: Added enum for different
access types that may need permissions.
(checkAccess(AccessType)): New method.
* tools/netx/jnlp/services/XDownloadService.java: Fixed typo.
* tools/netx/jnlp/services/XExtensionInstallerService.java: Likewise.
* tools/netx/jnlp/services/XServiceManagerStub.java: Added support for new
services.
* tools/netx/jnlp/tools/JarSigner.java:
(verifyJars): New method.
(allVerified): Likewise.
(anyJarsSigned): Likewise.
(getDetails): Likewise.
(getCerts): Likewise.
(verifyJar): Keep track of CertPaths used for CertsInfoPane
* tools/netx/jnlp/security/CertsInfoPane.java: New file.
* tools/netx/jnlp/security/MoreInfoPane.java: Likewise.
* tools/netx/jnlp/security/SecurityDialogUI.java: Likewise.
* tools/netx/jnlp/security/SecurityWarningDialog.java: Likewise.
* tools/netx/jnlp/security/SecurityWarningOptionPane.java: Likewise.
* tools/netx/jnlp/services/XClipboardService.java: Likewise.
* tools/netx/jnlp/services/XFileOpenService.java: Likewise.
* tools/netx/jnlp/services/XFileSaveService.java: Likewise.
* tools/netx/jnlp/tools/CharacterEncoder.java: Likewise.
* tools/netx/jnlp/tools/HexDumpEncoder.java: Likewise.
* ChangeLog: Added fkung's 2 latest ChangeLog entries.
line wrap: on
line diff
--- a/ChangeLog Tue Feb 05 12:26:45 2008 -0500 +++ b/ChangeLog Tue Feb 05 15:58:12 2008 -0500 @@ -1,3 +1,40 @@ +2008-02-05 Joshua Sumali <jsumali@redhat.com> + + * tools/netx/jnlp/Launcher.java: Removed unused line. + * tools/netx/jnlp/resources/Messages.properties: Added security messages + for security dialogs. + * tools/netx/jnlp/runtime/ApplicationInstance.java: Added signing field. + * tools/netx/jnlp/runtime/Boot.java: Use jar root default.jnlp + * tools/netx/jnlp/runtime/JNLPClassLoader.java: Added security warning + dialogs when running signed code. + * tools/netx/jnlp/runtime/JNLPRuntime.java: Enable code signing + verification by default. + * tools/netx/jnlp/services/ServiceUtil.java: Added enum for different + access types that may need permissions. + (checkAccess(AccessType)): New method. + * tools/netx/jnlp/services/XDownloadService.java: Fixed typo. + * tools/netx/jnlp/services/XExtensionInstallerService.java: Likewise. + * tools/netx/jnlp/services/XServiceManagerStub.java: Added support for new + services. + * tools/netx/jnlp/tools/JarSigner.java: + (verifyJars): New method. + (allVerified): Likewise. + (anyJarsSigned): Likewise. + (getDetails): Likewise. + (getCerts): Likewise. + (verifyJar): Keep track of CertPaths used for CertsInfoPane + * tools/netx/jnlp/security/CertsInfoPane.java: New file. + * tools/netx/jnlp/security/MoreInfoPane.java: Likewise. + * tools/netx/jnlp/security/SecurityDialogUI.java: Likewise. + * tools/netx/jnlp/security/SecurityWarningDialog.java: Likewise. + * tools/netx/jnlp/security/SecurityWarningOptionPane.java: Likewise. + * tools/netx/jnlp/services/XClipboardService.java: Likewise. + * tools/netx/jnlp/services/XFileOpenService.java: Likewise. + * tools/netx/jnlp/services/XFileSaveService.java: Likewise. + * tools/netx/jnlp/tools/CharacterEncoder.java: Likewise. + * tools/netx/jnlp/tools/HexDumpEncoder.java: Likewise. + * ChangeLog: Added fkung's 2 latest ChangeLog entries. + 2008-02-05 Lillian Angel <langel@redhat.com> * Makefile.am @@ -38,6 +75,38 @@ * acinclude.m4: Updated for libgcj-4.3.0.jar. * AUTHORS: Added Bernhard Rosenkränzer. +2008-02-02 Francis Kung <fkung@redhat.com> + + * tools/netx/jnlp/Parser.java: Re-added Node class. + * tools/netx/jnlp/runtime/AppletEnvironment.java: Removed unused import. + +2008-02-02 Francis Kung <fkung@redhat.com> + + * tools/netx/jnlp/JNLPFile.java: Added empty protected constructor. + * tools/netx/jnlp/Launcher.java + (launch(JNLPFile)): Delegate to new method. + (launch(JNLPFile, Container)): New method. + (launchApplet): Added Container parameter. + (createApplet): Likewise. + (TgThread): Added private Container field. + (TgThread.constructor(JNLPFile)): Delegate to new constructor. + (TgThread.constructor(JNLPFile, Container)): New method. + (TgThread.run): Launch applet with container argument. + * tools/netx/jnlp/runtime/AppletEnvironment.java + (Frame): Renamed field to... + (Container): New field. + (AppletEnvironment(JNLPFile, AppletInstance, Container)): New method. + (AppletEnvironment(JNLPFile, AppletInstance)): Delegate to new method. + (getAppletFrame): Return Container instead of Frame. + (startApplet): Replace Frame with Container. + (appletResize): Likewise. + (getParameter): Add lower-case check. + * tools/netx/jnlp/runtime/AppletInstance.java + (AppletInstance(JNLPFile, ThreadGroup, ClassLoader, Applet, Container)): + New method. + (setResizable): Only resize if the container is a Frame. + (isResizable): Return false if container is not a Frame. + 2008-02-01 Gary Benson <gbenson@redhat.com> * patches/icedtea-lib64.patch: Fixed to silence warning.
--- a/tools/netx/jnlp/Launcher.java Tue Feb 05 12:26:45 2008 -0500 +++ b/tools/netx/jnlp/Launcher.java Tue Feb 05 15:58:12 2008 -0500 @@ -429,7 +429,6 @@ return app; } catch (Exception ex) { - //throw launchError(new LaunchException(file, ex, R("LSFatal"), R("CLInit"), R("LInitApplet"), R("LInitAppletInfo"))); throw new LaunchException(file, ex, R("LSFatal"), R("CLInit"), R("LInitApplet"), R("LInitAppletInfo")); } }
--- a/tools/netx/jnlp/resources/Messages.properties Tue Feb 05 12:26:45 2008 -0500 +++ b/tools/netx/jnlp/resources/Messages.properties Tue Feb 05 15:58:12 2008 -0500 @@ -1,5 +1,5 @@ # Default (English) UI messages for netx -# L=Launcher, B=Boot, P=Parser, C=cache +# L=Launcher, B=Boot, P=Parser, C=cache S=security # # General NullParameter=Null parameter @@ -56,7 +56,7 @@ JInvalidExtensionDescriptor=Extension does not refer to a component or installer (name={1}, location={2}). LNotVerified=Jars not verified. - +LCancelOnUserRequest=Canceled on user request. LFatalVerification=A fatal error occurred while trying to verify jars. LFatalVerificationInfo= @@ -130,3 +130,25 @@ CChooseCacheInfo=Netx needs a location for storing cache files. CChooseCacheDir=Cache directory +# Security +SFileReadAccess=The application has requested read access to a file on the machine. Do you want to allow this action? +SFileWriteAccess=The application has requested write access to a file on the machine. Do you want to allow this action? +SSigUnverified=The application's digital signature cannot be verified. Do you want to run the application? +SSigVerified=The application's digital signature has been verified. Do you want to run the application? +SUntrustedSource=The digital signature could not be verified by a trusted source. Only run if you trust the origin of the application. +STrustedSource=The digital signature has been validated by a trusted source. +SClipboardReadAccess=The application has requested read-only access to the system clipboard. Do you want to allow this action? +SClipboardWriteAccess=The application has requested write-only access to the system clipboard. Do you want to allow this action? +SPrinterAccess=The application has requested printer access. Do you want to allow this action? + +# Security - used for the More Information dialog +SBadKeyUsage=Resources contain entries whose signer certificate's KeyUsage extension doesn't allow code signing. +SBadExtendedKeyUsage=Resources contain entries whose signer certificate's ExtendedKeyUsage extension doesn't allow code signing. +SBadNetscapeCertType=Resources contain entries whose signer certificate's NetscapeCertType extension doesn't allow code signing. +SHasUnsignedEntry=Resources contain unsigned entries which have not been integrity-checked. +SHasExpiredCert=The digital signature has expired. +SHasExpiringCert=Resources contain entries whose signer certificate will expire within six months. +SNotYetValidCert=Resources contain entries whose signer certificate is not yet valid. +SRunWithoutRestrictions=This application will be run without the security restrictions normally provided by java. +SRunWithUntrustedCertificate=The digital signature was generated with an untrusted certificate. +
--- a/tools/netx/jnlp/runtime/ApplicationInstance.java Tue Feb 05 12:26:45 2008 -0500 +++ b/tools/netx/jnlp/runtime/ApplicationInstance.java Tue Feb 05 15:58:12 2008 -0500 @@ -59,6 +59,8 @@ /** list of application listeners */ private EventListenerList listeners = new EventListenerList(); + /** whether or not this application is signed */ + private boolean isSigned = false; /** * Create an application instance for the file. @@ -67,6 +69,7 @@ this.file = file; this.group = group; this.loader = loader; + this.isSigned = ((JNLPClassLoader) loader).getSigning(); } /** @@ -235,5 +238,11 @@ weakWindows.trimToSize(); } + /** + * Returns whether or not this jar is signed. + */ + public boolean isSigned() { + return isSigned; + } }
--- a/tools/netx/jnlp/runtime/Boot.java Tue Feb 05 12:26:45 2008 -0500 +++ b/tools/netx/jnlp/runtime/Boot.java Tue Feb 05 15:58:12 2008 -0500 @@ -64,7 +64,7 @@ /** the JNLP file to open if -jnlp not specified (null for no default) */ private static final String defaultFile = "jar:" + Boot.class.getProtectionDomain().getCodeSource().getLocation() - + "!/netx/jnlp/resources/default.jnlp"; + + "!/default.jnlp"; private static final String miniLicense = "\n" + " netx - an open-source JNLP client.\n"
--- a/tools/netx/jnlp/runtime/JNLPClassLoader.java Tue Feb 05 12:26:45 2008 -0500 +++ b/tools/netx/jnlp/runtime/JNLPClassLoader.java Tue Feb 05 15:58:12 2008 -0500 @@ -29,6 +29,8 @@ import netx.jnlp.cache.*; import netx.jnlp.*; import netx.jnlp.tools.JarSigner; +import netx.jnlp.services.*; +import netx.jnlp.security.*; /** * Classloader that takes it's resources from a JNLP file. If the @@ -96,6 +98,11 @@ /** all of the jar files that were not verified */ private ArrayList<String> unverifiedJars = null; + /** the jarsigner tool to verify our jars */ + private JarSigner js = null; + + private boolean signing = false; + /** * Create a new JNLPClassLoader from the specified file. * @@ -229,49 +236,49 @@ //Verify jars if the -verify option is passed. if (JNLPRuntime.isVerifying()) { - boolean allVerified; - + JarSigner js; waitForJars(initialJars); //download the jars first. try { - allVerified = verifyJars(initialJars); + js = verifyJars(initialJars); } catch (Exception e) { - //we caught an Exception from the JarSigner class. e.printStackTrace(); throw new LaunchException(null, null, R("LSFatal"), R("LCInit"), R("LFatalVerification"), R("LFatalVerificationInfo")); } - if (!allVerified) { - - String listOfVerifiedJars = "The following jars were verified:\n"; - String listOfUnverifiedJars = "The following jars were unverified:\n"; - - if (verifiedJars.size() != 0) - for (int i = 0; i < verifiedJars.size(); i++) - listOfVerifiedJars += verifiedJars.get(i) + "\n"; - - if (unverifiedJars.size() != 0) - for (int i = 0; i < unverifiedJars.size(); i++) - listOfUnverifiedJars += unverifiedJars.get(i) + "\n"; + //Case when at least one jar has some signing + if (js.anyJarsSigned()){ + signing = true; + //if there was some problem with the signing... + if (!js.allVerified()) { - //Open dialog, ask user if they still want to run the applet. - int i = JOptionPane.showConfirmDialog(null, - R("LNotVerifiedDialog")+"\n\n" - +listOfVerifiedJars+"\n" - +listOfUnverifiedJars+"\n" - +R("LAskToContinue"), - "Warning", JOptionPane.YES_NO_OPTION); - - if (i == 1) - throw new LaunchException(null, null, R("LSFatal"), - R("LCLaunching"), R("LNotVerified"), ""); + boolean b = SecurityWarningDialog.showWarningDialog( + SecurityWarningDialog.AccessType.UNVERIFIED, file, + js.getCerts(), js.getDetails()); + if (!b) + throw new LaunchException(null, null, R("LSFatal"), + R("LCLaunching"), R("LNotVerified"), ""); + } else { + //jar is completely verified, but we still need to show + //a dialog + + boolean b = SecurityWarningDialog.showWarningDialog( + SecurityWarningDialog.AccessType.VERIFIED, file, + js.getCerts(), js.getDetails()); + if (!b) + throw new LaunchException(null, null, R("LSFatal"), + R("LCLaunching"), R("LCancelOnUserRequest"), ""); + } + } else { + + signing = false; + //otherwise this jar is simply unsigned -- make sure to ask + //for permission on certain actions } - } - activateJars(initialJars); } @@ -518,36 +525,11 @@ * * @param jars the jars to be verified. */ - private boolean verifyJars(List<JARDesc> jars) throws Exception { + private JarSigner verifyJars(List<JARDesc> jars) throws Exception { - boolean allVerified = true; - - JarSigner js = new JarSigner(); - verifiedJars = new ArrayList<String>(); - unverifiedJars = new ArrayList<String>(); - - for (int i = 0; i < jars.size(); i++) { - - JARDesc jar = (JARDesc) jars.get(i); - - try { - String localFile = tracker.getCacheFile(jar.getLocation()).getAbsolutePath(); - boolean result = js.verifyJar(localFile); - - if (!result) { - allVerified = false; - unverifiedJars.add(localFile); - } else { - verifiedJars.add(localFile); - } - } catch (Exception e){ - //We may catch exceptions from using js.verifyJar(localFile). - e.printStackTrace(); - throw e; - } - } - - return allVerified; + js = new JarSigner(); + js.verifyJars(jars, tracker); + return js; } /** @@ -751,6 +733,10 @@ return file.getFileLocation().toString(); } + public boolean getSigning() { + return signing; + } + }
--- a/tools/netx/jnlp/runtime/JNLPRuntime.java Tue Feb 05 12:26:45 2008 -0500 +++ b/tools/netx/jnlp/runtime/JNLPRuntime.java Tue Feb 05 15:58:12 2008 -0500 @@ -81,7 +81,7 @@ private static boolean headless = false; /** whether we'll be checking for jar signing */ - private static boolean verify = false; + private static boolean verify = true; /** whether the runtime uses security */ private static boolean securityEnabled = true;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/netx/jnlp/security/CertsInfoPane.java Tue Feb 05 15:58:12 2008 -0500 @@ -0,0 +1,312 @@ +/* CertsInfoPane.java + Copyright (C) 2008 Red Hat, Inc. + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, version 2. + +IcedTea is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. +*/ + +package netx.jnlp.security; + +import java.util.ArrayList; +import java.util.Date; +import java.security.cert.CertPath; +import java.security.cert.X509Certificate; +import java.math.BigInteger; +import javax.security.auth.x500.X500Principal; +import sun.security.x509.*; +import javax.swing.*; +import javax.swing.event.*; +import javax.swing.table.*; +import java.awt.*; +import java.awt.event.*; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.StringSelection; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.TreeSelectionModel; +import netx.jnlp.tools.*; + +/** + * Provides the UI for the Certificate Info dialog. This dialog displays data from + * X509Certificate(s) used in jar signing. + * + * @author <a href="mailto:jsumali@redhat.com">Joshua Sumali</a> + */ +public class CertsInfoPane extends SecurityDialogUI { + + private ArrayList<CertPath> certs; + private JList list; + private JTree tree; + private JTable table; + private JTextArea output; + private ListSelectionModel listSelectionModel; + private ListSelectionModel tableSelectionModel; + private String[] certNames; + private String[] columnNames = { "Field", "Value" }; + private ArrayList<String[][]> certsData; + + public CertsInfoPane(JComponent x) { + super(x); + } + + /** + * Builds the JTree out of CertPaths. + */ + private void buildTree() { + //for now, we're only going to display the first signer, even though + //jars can be signed by multiple people. + CertPath firstPath = certs.get(0); + X509Certificate firstCert = + ((X509Certificate)firstPath.getCertificates().get(0)); + String subjectString = + getCN(firstCert.getSubjectX500Principal().getName()); + String issuerString = + getCN(firstCert.getIssuerX500Principal().getName()); + + DefaultMutableTreeNode top = + new DefaultMutableTreeNode(subjectString + + " (" + issuerString + ")"); + + //not self signed + if (firstPath.getCertificates().size() > 1) { + X509Certificate secondCert = + ((X509Certificate)firstPath.getCertificates().get(1)); + subjectString = + getCN(secondCert.getSubjectX500Principal().getName()); + issuerString = + getCN(secondCert.getIssuerX500Principal().getName()); + top.add(new DefaultMutableTreeNode(subjectString + + " (" + issuerString + ")")); + } + + tree = new JTree(top); + tree.getSelectionModel().setSelectionMode + (TreeSelectionModel.SINGLE_TREE_SELECTION); + tree.addTreeSelectionListener(new TreeSelectionHandler()); + } + + /** + * Constructs the GUI components of this UI + */ + protected void installComponents() { + certs = ((SecurityWarningDialog)optionPane).getCerts(); + buildTree(); + certNames = new String[certs.get(0).getCertificates().size()]; + certsData = new ArrayList<String[][]>(); + + for (int i = 0; i < certs.get(0).getCertificates().size(); i++) { + + X509Certificate c = (X509Certificate) certs.get(0).getCertificates().get(i); + + String version = ""+c.getVersion(); + String serialNumber = c.getSerialNumber().toString(); + String signatureAlg = c.getSigAlgName(); + String issuer = c.getIssuerX500Principal().toString(); + String validity = new CertificateValidity(c.getNotBefore(), + c.getNotAfter()).toString(); + String subject = c.getSubjectX500Principal().toString(); + + //convert our signature into a nice human-readable form. + HexDumpEncoder encoder = new HexDumpEncoder(); + String signature = encoder.encodeBuffer(c.getSignature()); + + String[][] cert = { {"Version", version}, + {"Serial", serialNumber}, + {"Signature Algorithm", signatureAlg}, + {"Issuer", issuer}, + {"Validity", validity}, + {"Subject", subject}, + {"Signature", signature} }; + certsData.add(cert); + certNames[i] = getCN(c.getSubjectX500Principal().getName()) + + " (" + getCN(c.getIssuerX500Principal().getName()) + ")"; + } + + /** + //List of Certs + list = new JList(certNames); + list.setSelectedIndex(0); //assuming there's at least 1 cert + listSelectionModel = list.getSelectionModel(); + listSelectionModel.addListSelectionListener(new ListSelectionHandler()); + JScrollPane listPane = new JScrollPane(list); + */ + JScrollPane listPane = new JScrollPane(tree); + + //Table of field-value pairs + DefaultTableModel tableModel = new DefaultTableModel(certsData.get(0), + columnNames); + table = new JTable(tableModel); + table.getTableHeader().setReorderingAllowed(false); + tableSelectionModel = table.getSelectionModel(); + tableSelectionModel.addListSelectionListener(new TableSelectionHandler()); + table.setFillsViewportHeight(true); + JScrollPane tablePane = new JScrollPane(table); + tablePane.setPreferredSize(new Dimension(500,200)); + + //Text area to display the larger values + output = new JTextArea(); + output.setEditable(false); + JScrollPane outputPane = new JScrollPane(output, + ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, + ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); + outputPane.setPreferredSize(new Dimension(500,200)); + + //split pane of the field-value pairs and textbox + JSplitPane rightSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, + tablePane, outputPane); + rightSplitPane.setDividerLocation(0.50); + rightSplitPane.setResizeWeight(0.50); + + JSplitPane mainPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, + listPane, rightSplitPane); + mainPane.setDividerLocation(0.30); + mainPane.setResizeWeight(0.30); + + JPanel buttonPane = new JPanel(new BorderLayout()); + JButton close = new JButton("Close"); + JButton copyToClipboard = new JButton("Copy to Clipboard"); + close.addActionListener(createButtonActionListener(0)); + copyToClipboard.addActionListener(new CopyToClipboardHandler()); + buttonPane.add(close, BorderLayout.EAST); + buttonPane.add(copyToClipboard, BorderLayout.WEST); + buttonPane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); + + optionPane.add(mainPane, BorderLayout.CENTER); + optionPane.add(buttonPane, BorderLayout.SOUTH); + } + + /** + * Extracts the CN field from a Certificate principal string. + */ + private String getCN(String principal) { + int start = principal.indexOf("CN="); + int end = principal.indexOf(",", start); + + if (end == -1) { + end = principal.length(); + } + + if (start >= 0) + return principal.substring(start+3, end); + else + return principal; + } + + /** + * Copies the currently selected certificate to the system Clipboard. + */ + private class CopyToClipboardHandler implements ActionListener { + public void actionPerformed(ActionEvent e) { + Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); + int certIndex = 0; + DefaultMutableTreeNode node = (DefaultMutableTreeNode) + tree.getLastSelectedPathComponent(); + if (node == null) return; + if (node.isRoot()) + certIndex = 0; + else if (node.isLeaf()) + certIndex = 1; + + String[][] cert = certsData.get(certIndex); + int rows = cert.length; + int cols = cert[0].length; + + String certString = ""; + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cols; j++) { + certString += cert[i][j]; + certString += " "; + } + certString += "\n"; + } + + clipboard.setContents(new StringSelection(certString), null); + } + } + + /** + * Updates the JTable when the JTree selection has changed. + */ + private class TreeSelectionHandler implements TreeSelectionListener { + public void valueChanged(TreeSelectionEvent e) { + DefaultMutableTreeNode node = (DefaultMutableTreeNode) + tree.getLastSelectedPathComponent(); + + if (node == null) return; + if (node.isRoot()) { + table.setModel(new DefaultTableModel(certsData.get(0), + columnNames)); + } else if (node.isLeaf()) { + table.setModel(new DefaultTableModel(certsData.get(1), + columnNames)); + } + } + } + + /** + * Updates the JTable when the selection on the list has changed. + */ + private class ListSelectionHandler implements ListSelectionListener { + public void valueChanged(ListSelectionEvent e) { + ListSelectionModel lsm = (ListSelectionModel) e.getSource(); + + int minIndex = lsm.getMinSelectionIndex(); + int maxIndex = lsm.getMaxSelectionIndex(); + + for (int i = minIndex; i <= maxIndex; i++) { + if (lsm.isSelectedIndex(i)) { + table.setModel(new DefaultTableModel(certsData.get(i), + columnNames)); + } + } + } + } + + /** + * Updates the JTextArea output when the selection on the JTable + * has changed. + */ + private class TableSelectionHandler implements ListSelectionListener { + public void valueChanged(ListSelectionEvent e) { + ListSelectionModel lsm = (ListSelectionModel) e.getSource(); + + int minIndex = lsm.getMinSelectionIndex(); + int maxIndex = lsm.getMaxSelectionIndex(); + + for (int i = minIndex; i <= maxIndex; i++) { + if (lsm.isSelectedIndex(i)) { + output.setText((String) table.getValueAt(i,1)); + } + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/netx/jnlp/security/MoreInfoPane.java Tue Feb 05 15:58:12 2008 -0500 @@ -0,0 +1,106 @@ +/* MoreInfoPane.java + Copyright (C) 2008 Red Hat, Inc. + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, version 2. + +IcedTea is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. +*/ + +package netx.jnlp.security; + +import java.awt.*; +import javax.swing.*; +import netx.jnlp.runtime.*; +import java.util.ArrayList; +import java.awt.event.ActionListener; +import java.awt.event.ActionEvent; +import javax.swing.border.Border; + +/** + * Provides the UI for the More Info dialog. This dialog shows details about an + * application's signing status. + * + * @author <a href="mailto:jsumali@redhat.com">Joshua Sumali</a> + */ +public class MoreInfoPane extends SecurityDialogUI { + + public MoreInfoPane(JComponent x) { + super(x); + } + + /** + * Constructs the GUI components of this UI + */ + protected void installComponents() { + ArrayList<String> details = + ((SecurityWarningDialog)optionPane).getDetails(); + + int numLabels = details.size(); + JPanel errorPanel = new JPanel(new GridLayout(numLabels,1)); + errorPanel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10)); + errorPanel.setPreferredSize(new Dimension(400, 70*(numLabels))); + + for (int i = 0; i < numLabels; i++) { + errorPanel.add(new JLabel(htmlWrap(details.get(i)))); + } + + JPanel buttonsPanel = new JPanel(new BorderLayout()); + JButton certDetails = new JButton("Cerificate Details"); + certDetails.addActionListener(new CertInfoButtonListener()); + JButton close = new JButton("Close"); + close.addActionListener(createButtonActionListener(0)); + buttonsPanel.add(certDetails, BorderLayout.WEST); + buttonsPanel.add(close, BorderLayout.EAST); + buttonsPanel.setBorder(BorderFactory.createEmptyBorder(15,15,15,15)); + + JPanel main = new JPanel(new BorderLayout()); + main.add(errorPanel, BorderLayout.NORTH); + main.add(buttonsPanel, BorderLayout.SOUTH); + + optionPane.add(main); + } + + /** + * Needed to get word-wrap working in JLabels. + */ + private String htmlWrap (String s) { + return "<html>"+s+"</html>"; + } + + private class CertInfoButtonListener implements ActionListener { + public void actionPerformed(ActionEvent e) { + SecurityWarningDialog.showCertInfoDialog( + ((SecurityWarningDialog)optionPane).getCerts(), + optionPane); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/netx/jnlp/security/SecurityDialogUI.java Tue Feb 05 15:58:12 2008 -0500 @@ -0,0 +1,196 @@ +/* SecurityDialogUI.java + Copyright (C) 2008 Red Hat, Inc. + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, version 2. + +IcedTea is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. +*/ + +package netx.jnlp.security; + +import java.awt.*; +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.plaf.OptionPaneUI; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeEvent; +import java.awt.event.ActionListener; +import java.awt.event.ActionEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.ComponentAdapter; +import netx.jnlp.runtime.JNLPRuntime; +import netx.jnlp.JNLPFile; + +/** + * Provides a base for JNLP warning dialogs. + * + * @author <a href="mailto:jsumali@redhat.com">Joshua Sumali</a> + */ +public abstract class SecurityDialogUI extends OptionPaneUI { + + /** The JOptionPane that we're providing the L&F for */ + protected JOptionPane optionPane; + + /** Component to receive focus when messaged with selectInitialValue. */ + Component initialFocusComponent; + + /** PropertyChangeListener for <code>optionPane</code> */ + private PropertyChangeListener propertyChangeListener; + private Handler handler; + + public SecurityDialogUI(JComponent x){ + optionPane = (JOptionPane)x; + } + + /** + * Installs the user interface for the SecurityWarningDialog. + */ + public void installUI(JComponent c) { + + //Only install the UI when type and file in SecurityWarningDialog + //have been set. + if (((SecurityWarningDialog)c).isInitialized()) { + setSystemLookAndFeel(); + optionPane = (JOptionPane)c; + optionPane.setLayout(new BorderLayout()); + installComponents(); + installListeners(); + } + } + + //Taken from javax.swing.plaf.basic.BasicOptionPaneUI + protected void installListeners() { + if ((propertyChangeListener = getHandler()) != null) + optionPane.addPropertyChangeListener(propertyChangeListener); + } + + //Taken from javax.swing.plaf.basic.BasicOptionPaneUI + protected void uninstallComponents() { + initialFocusComponent = null; + optionPane.removeAll(); + } + + //Taken from javax.swing.plaf.basic.BasicOptionPaneUI + private Handler getHandler() { + if (handler == null) + handler = new Handler(); + return handler; + } + + //Inherited from OptionPaneUI + //Modified from javax.swing.plaf.basic.BasicOptionPaneUI + public void selectInitialValue(JOptionPane op) { + if (initialFocusComponent != null) + initialFocusComponent.requestFocus(); + + if (initialFocusComponent instanceof JButton) { + JRootPane root = SwingUtilities.getRootPane(initialFocusComponent); + if (root != null) + root.setDefaultButton((JButton) initialFocusComponent); + } + } + + //Inherited from OptionPaneUI + public boolean containsCustomComponents(JOptionPane op) { + return false; + } + + //Taken from javax.swing.plaf.basic.BasicOptionPaneUI + protected ActionListener createButtonActionListener(int buttonIndex) { + return new ButtonActionListener(buttonIndex); + } + + private static String R(String key) { + return JNLPRuntime.getMessage(key); + } + + /** + * Needed to get word wrap working in JLabels. + */ + private String htmlWrap (String s) { + return "<html>"+s+"</html>"; + } + + //Taken from javax.swing.plaf.basic.BasicOptionPaneUI + private class Handler implements PropertyChangeListener { + public void propertyChange(PropertyChangeEvent e) { + if (e.getSource() == optionPane) { + String changeName = e.getPropertyName(); + if (changeName == JOptionPane.OPTIONS_PROPERTY || + changeName == JOptionPane.INITIAL_VALUE_PROPERTY || + changeName == JOptionPane.ICON_PROPERTY || + changeName == JOptionPane.MESSAGE_TYPE_PROPERTY || + changeName == JOptionPane.OPTION_TYPE_PROPERTY || + changeName == JOptionPane.MESSAGE_PROPERTY || + changeName == JOptionPane.SELECTION_VALUES_PROPERTY || + changeName == JOptionPane.INITIAL_SELECTION_VALUE_PROPERTY || + changeName == JOptionPane.WANTS_INPUT_PROPERTY) { + uninstallComponents(); + installComponents(); + optionPane.validate(); + } else if (changeName == "componentOrientation") { + ComponentOrientation o = (ComponentOrientation)e.getNewValue(); + JOptionPane op = (JOptionPane)e.getSource(); + if (o != (ComponentOrientation)e.getOldValue()) { + op.applyComponentOrientation(o); + } + } + } + } + } + + //Taken from javax.swing.plaf.basic.BasicOptionPaneUI + public class ButtonActionListener implements ActionListener { + protected int buttonIndex; + + public ButtonActionListener(int buttonIndex) { + this.buttonIndex = buttonIndex; + } + + public void actionPerformed(ActionEvent e) { + if (optionPane != null) { + optionPane.setValue(new Integer(buttonIndex)); + } + } + } + + private void setSystemLookAndFeel() { + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (Exception e) { + //don't worry if we can't. + } + } + + //this is for the different dialogs to fill in. + protected abstract void installComponents(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/netx/jnlp/security/SecurityWarningDialog.java Tue Feb 05 15:58:12 2008 -0500 @@ -0,0 +1,286 @@ +/* SecurityWarningDialog.java + Copyright (C) 2008 Red Hat, Inc. + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, version 2. + +IcedTea is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. +*/ + +package netx.jnlp.security; + +import netx.jnlp.JNLPFile; +import java.awt.*; +import javax.swing.*; +import java.awt.event.*; +import javax.swing.plaf.OptionPaneUI; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeEvent; +import java.util.ArrayList; +import java.security.cert.CertPath; + +/** + * Provides methods for showing security warning dialogs + * for a wide range of JNLP security issues. + * + * @author <a href="mailto:jsumali@redhat.com">Joshua Sumali</a> + */ +public class SecurityWarningDialog extends JOptionPane { + + /** Types of dialogs we can create */ + public static enum DialogType { + WARNING, + MORE_INFO, + CERT_INFO + } + /** The types of access which may need user permission. */ + public static enum AccessType { + READ_FILE, + WRITE_FILE, + CLIPBOARD_READ, + CLIPBOARD_WRITE, + PRINTER, + VERIFIED, + UNVERIFIED + } + + /** The type of dialog we want to show */ + private DialogType dialogType; + + /** The type of access that this dialog is for */ + private AccessType accessType; + + /** The application file assocated with this security warning */ + private JNLPFile file; + + /** The Certificates associated with this application */ + private ArrayList<CertPath> certs; + + /** Details of the signing */ + private ArrayList<String> details; + + /** Whether or not this object has been fully initialized */ + private boolean initialized = false; + + public SecurityWarningDialog(DialogType dialogType, AccessType accessType, + JNLPFile file, ArrayList<CertPath> certs, + ArrayList<String> details) { + this.dialogType = dialogType; + this.accessType = accessType; + this.file = file; + this.certs = certs; + this.details = details; + initialized = true; + updateUI(); + } + + public boolean isInitialized(){ + return initialized; + } + + public static boolean showWarningDialog(AccessType accessType, + JNLPFile file) { + return showWarningDialog(accessType, file, null, null); + } + + /** + * Shows a security warning dialog according to the specified type of + * access. If <code>type</code> is one of AccessType.VERIFIED or + * AccessType.UNVERIFIED, extra details will be available with regards + * to code signing and signing certificates. + * + * @param accessType the type of warning dialog to show + * @param file the JNLPFile associated with this warning + * @param certs the signing certificates assocated with this warning, + * if any. Can be null. + * @param details the extra details associated with this warning. Can be null. + */ + public static boolean showWarningDialog(AccessType accessType, + JNLPFile file, ArrayList<CertPath> certs, + ArrayList<String> details) { + SecurityWarningDialog swd = + new SecurityWarningDialog(DialogType.WARNING, accessType, file, + certs, details); + JDialog dialog = swd.createDialog(); + swd.selectInitialValue(); + dialog.setResizable(false); + centerDialog(dialog); + dialog.setVisible(true); + dialog.dispose(); + + Object selectedValue = swd.getValue(); + if (selectedValue == null) { + return false; + } else if (selectedValue instanceof Integer) { + if (((Integer)selectedValue).intValue() == 0) + return true; + else + return false; + } else { + return false; + } + } + + /** + * Shows more information regarding jar code signing + * + * @param certs the certificates used in this signing + * @param details the extra details regarding this signing + */ + public static void showMoreInfoDialog( + ArrayList<CertPath> certs, + ArrayList<String> details, JOptionPane parent) { + + SecurityWarningDialog swd = + new SecurityWarningDialog(DialogType.MORE_INFO, null, null, + certs, details); + JDialog dialog = swd.createDialog(); + dialog.setLocationRelativeTo(parent); + swd.selectInitialValue(); + dialog.setResizable(false); + dialog.setVisible(true); + dialog.dispose(); + } + + /** + * Displays CertPath information in a readable table format. + * + * @param certs the certificates used in signing. + */ + public static void showCertInfoDialog(ArrayList<CertPath> certs, + JOptionPane parent) { + SecurityWarningDialog swd = new SecurityWarningDialog(DialogType.CERT_INFO, + null, null, certs, null); + JDialog dialog = swd.createDialog(); + dialog.setLocationRelativeTo(parent); + swd.selectInitialValue(); + dialog.setResizable(true); + dialog.setVisible(true); + dialog.dispose(); + } + + //Modified from javax.swing.JOptionPane + private JDialog createDialog() { + String dialogTitle; + if (dialogType == DialogType.WARNING) + dialogTitle = "Warning - Security"; + else if (dialogType == DialogType.MORE_INFO) + dialogTitle = "More Information"; + else + dialogTitle = "Details - Certificate"; + + final JDialog dialog = new JDialog((Frame)null, dialogTitle, true); + + Container contentPane = dialog.getContentPane(); + contentPane.setLayout(new BorderLayout()); + contentPane.add(this, BorderLayout.CENTER); + dialog.pack(); + + WindowAdapter adapter = new WindowAdapter() { + private boolean gotFocus = false; + public void windowClosing(WindowEvent we) { + setValue(null); + } + public void windowGainedFocus(WindowEvent we) { + // Once window gets focus, set initial focus + if (!gotFocus) { + selectInitialValue(); + gotFocus = true; + } + } + }; + dialog.addWindowListener(adapter); + dialog.addWindowFocusListener(adapter); + + dialog.addComponentListener(new ComponentAdapter() { + public void componentShown(ComponentEvent ce) { + // reset value to ensure closing works properly + setValue(JOptionPane.UNINITIALIZED_VALUE); + } + }); + + addPropertyChangeListener( new PropertyChangeListener() { + public void propertyChange(PropertyChangeEvent event) { + // Let the defaultCloseOperation handle the closing + // if the user closed the window without selecting a button + // (newValue = null in that case). Otherwise, close the dialog. + if (dialog.isVisible() && + event.getSource() == SecurityWarningDialog.this && + (event.getPropertyName().equals(VALUE_PROPERTY) || + event.getPropertyName().equals(INPUT_VALUE_PROPERTY)) && + event.getNewValue() != null && + event.getNewValue() != JOptionPane.UNINITIALIZED_VALUE) { + dialog.setVisible(false); + } + } + }); + + return dialog; + } + + public AccessType getType() { + return accessType; + } + + public JNLPFile getFile() { + return file; + } + + public ArrayList<CertPath> getCerts() { + return certs; + } + + public ArrayList<String> getDetails() { + return details; + } + + /** + * Updates the UI using SecurityWarningOptionPane, instead of the + * basic dialog box. + */ + public void updateUI() { + + if (dialogType == DialogType.WARNING) + setUI((OptionPaneUI) new SecurityWarningOptionPane(this)); + else if (dialogType == DialogType.MORE_INFO) + setUI((OptionPaneUI) new MoreInfoPane(this)); + else if (dialogType == DialogType.CERT_INFO) + setUI((OptionPaneUI) new CertsInfoPane(this)); + } + + private static void centerDialog(JDialog dialog) { + Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); + Dimension dialogSize = dialog.getSize(); + + dialog.setLocation((screen.width - dialogSize.width)/2, + (screen.height - dialogSize.height)/2); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/netx/jnlp/security/SecurityWarningOptionPane.java Tue Feb 05 15:58:12 2008 -0500 @@ -0,0 +1,237 @@ +/* SecurityWarningOptionPane.java + Copyright (C) 2008 Red Hat, Inc. + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, version 2. + +IcedTea is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. +*/ + +package netx.jnlp.security; + +import java.awt.*; +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.plaf.OptionPaneUI; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeEvent; +import java.awt.event.ActionListener; +import java.awt.event.ActionEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.ComponentAdapter; +import sun.swing.DefaultLookup; +import netx.jnlp.runtime.JNLPRuntime; +import netx.jnlp.JNLPFile; + +/** + * Provides the look and feel for a SecurityWarningDialog. These dialogs are + * used to warn the user when either signed code (with or without signing + * issues) is going to be run, or when service permission (file, clipboard, + * printer, etc) is needed with unsigned code. + * + * @author <a href="mailto:jsumali@redhat.com">Joshua Sumali</a> + */ +public class SecurityWarningOptionPane extends SecurityDialogUI { + + public SecurityWarningOptionPane(JComponent x) { + super(x); + } + + /** + * Creates the actual GUI components, and adds it to <code>optionPane</code> + */ + protected void installComponents() { + SecurityWarningDialog.AccessType type = + ((SecurityWarningDialog)optionPane).getType(); + JNLPFile file = + ((SecurityWarningDialog)optionPane).getFile(); + + String name = ""; + String publisher = ""; + String from = ""; + + //We don't worry about exceptions when trying to fill in + //these strings -- we just want to fill in as many as possible. + try { + name = file.getInformation().getTitle(); + } catch (Exception e) { + } + + try { + publisher = file.getInformation().getVendor(); + } catch (Exception e) { + } + + try { + from = file.getInformation().getHomepage().toString(); + } catch (Exception e) { + } + + //Used to determine whether we need another JLabel on the bottom + //of our dialog. + boolean signingRelated = + (type == SecurityWarningDialog.AccessType.VERIFIED + || type == SecurityWarningDialog.AccessType.UNVERIFIED); + + //Top label + String topLabelText = ""; + String propertyName = ""; + switch (type) { + case VERIFIED: + topLabelText = R("SSigVerified"); + propertyName = "OptionPane.informationIcon"; + break; + case UNVERIFIED: + topLabelText = R("SSigUnverified"); + propertyName = "OptionPane.warningIcon"; + break; + case READ_FILE: + topLabelText = R("SFileReadAccess"); + propertyName = "OptionPane.warningIcon"; + break; + case WRITE_FILE: + topLabelText = R("SFileWriteAccess"); + propertyName = "OptionPane.warningIcon"; + break; + case CLIPBOARD_READ: + topLabelText = R("SClipboardReadAccess"); + propertyName = "OptionPane.warningIcon"; + break; + case CLIPBOARD_WRITE: + topLabelText = R("SClipboardWriteAccess"); + propertyName = "OptionPane.warningIcon"; + break; + case PRINTER: + topLabelText = R("SPrinterAccess"); + propertyName = "OptionPane.warningIcon"; + break; + } + //TODO: Get system icons and add them to our dialogs. + //Icon icon = (Icon)DefaultLookup.get(optionPane,this,propertyName); + JLabel topLabel = new JLabel(htmlWrap(topLabelText)); + topLabel.setFont(new Font(topLabel.getFont().toString(), + Font.BOLD, 12)); + JPanel topPanel = new JPanel(new BorderLayout()); + topPanel.setBackground(Color.WHITE); + topPanel.add(topLabel, BorderLayout.CENTER); + topPanel.setPreferredSize(new Dimension(400,60)); + topPanel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10)); + + //application info + JLabel nameLabel = new JLabel("Name: " + name); + nameLabel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); + JLabel publisherLabel = new JLabel("Publisher: " + publisher); + publisherLabel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); + JLabel fromLabel = new JLabel("From: " + from); + fromLabel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); + + //TODO: enable this checkbox once we have a way to + //keep track of our trusted signers. + JCheckBox alwaysTrust; + if (signingRelated) + alwaysTrust = new JCheckBox( + "Always trust content from this publisher"); + else + alwaysTrust = new JCheckBox("Always allow this action."); + alwaysTrust.setEnabled(false); + + JPanel infoPanel = new JPanel(new GridLayout(4,1)); + infoPanel.add(nameLabel); + infoPanel.add(publisherLabel); + infoPanel.add(fromLabel); + infoPanel.add(alwaysTrust); + infoPanel.setBorder(BorderFactory.createEmptyBorder(25,25,25,25)); + + //run and cancel buttons + JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + + String runButtonText = "Run"; + if (!signingRelated) + runButtonText = "Allow"; + + JButton run = new JButton(runButtonText); + JButton cancel = new JButton("Cancel"); + run.addActionListener(createButtonActionListener(0)); + cancel.addActionListener(createButtonActionListener(1)); + initialFocusComponent = cancel; + buttonPanel.add(run); + buttonPanel.add(cancel); + buttonPanel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10)); + + //all of the above + JPanel main = new JPanel(); + main.setLayout(new BoxLayout(main, BoxLayout.Y_AXIS)); + main.add(topPanel); + main.add(infoPanel); + main.add(buttonPanel); + + //optional bottom panel, for when we're dealing with signing + //(as opposed to file/clipboard/printer access). + if (signingRelated) { + + JLabel bottomLabel; + JButton moreInfo = new JButton("More information..."); + moreInfo.addActionListener(new MoreInfoButtonListener()); + if (type == SecurityWarningDialog.AccessType.VERIFIED) + bottomLabel = new JLabel(htmlWrap(R("STrustedSource"))); + else + bottomLabel = new JLabel(htmlWrap(R("SUntrustedSource"))); + + JPanel bottomPanel = new JPanel(); + bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.X_AXIS)); + bottomPanel.add(bottomLabel); + bottomPanel.add(moreInfo); + bottomPanel.setPreferredSize(new Dimension(500,50)); + bottomPanel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10)); + main.add(bottomPanel); + } + + optionPane.add(main, BorderLayout.CENTER); + } + + private static String R(String key) { + return JNLPRuntime.getMessage(key); + } + + protected String htmlWrap (String s) { + return "<html>"+s+"</html>"; + } + + private class MoreInfoButtonListener implements ActionListener { + public void actionPerformed(ActionEvent e) { + SecurityWarningDialog.showMoreInfoDialog( + ((SecurityWarningDialog)optionPane).getCerts(), + ((SecurityWarningDialog)optionPane).getDetails(), + optionPane); + } + } + +}
--- a/tools/netx/jnlp/services/ServiceUtil.java Tue Feb 05 12:26:45 2008 -0500 +++ b/tools/netx/jnlp/services/ServiceUtil.java Tue Feb 05 15:58:12 2008 -0500 @@ -1,15 +1,15 @@ // Copyright (C) 2001-2003 Jon A. Maxwell (JAM) -// +// // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. -// +// // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. -// +// // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. @@ -25,16 +25,32 @@ import javax.jnlp.*; import netx.jnlp.*; import netx.jnlp.runtime.*; +import netx.jnlp.security.SecurityWarningDialog; + +import javax.swing.JOptionPane; +import javax.swing.UIManager; /** * Provides static methods to interact useful for using the JNLP * services.<p> * * @author <a href="mailto:jmaxwell@users.sourceforge.net">Jon A. Maxwell (JAM)</a> - initial author - * @version $Revision: 1.8 $ + * @author <a href="mailto:jsumali@redhat.com">Joshua Sumali</a> + * @version $Revision: 1.8 $ */ public class ServiceUtil { + private static String R(String key) { + return JNLPRuntime.getMessage(key); + } + + /** + * Types of system access that may need permissions. + */ + public static enum AccessType { + READ_FILE, WRITE_FILE, CLIPBOARD_READ, CLIPBOARD_WRITE, PRINTER + } + /** * Returns the BasicService reference, or null if the service is * unavailable. @@ -120,12 +136,12 @@ */ static Object createPrivilegedProxy(Class iface, final Object receiver) { return java.lang.reflect.Proxy.newProxyInstance(XServiceManagerStub.class.getClassLoader(), - new Class[] { iface }, - new PrivilegedHandler(receiver)); + new Class[] { iface }, + new PrivilegedHandler(receiver)); } - /** - * calls the object's method using privileged access + /** + * calls the object's method using privileged access */ private static class PrivilegedHandler implements InvocationHandler { private final Object receiver; @@ -157,6 +173,27 @@ } }; + /** + * Returns whether the app requesting a service is signed. If the app is + * unsigned, the user is prompted with a dialog asking if the action + * should be allowed. + */ + static boolean checkAccess(SecurityWarningDialog.AccessType type) { + + ApplicationInstance app = JNLPRuntime.getApplication(); + if (app != null) { + if (!app.isSigned()) { + return SecurityWarningDialog.showWarningDialog(type, + app.getJNLPFile()); + } else if (app.isSigned()) { + + //just return true here regardless if the app + //has signing issues -- at this point the user would've + //already decided to run the app anyways. + return true; + } + } + return false; //deny + } } -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/netx/jnlp/services/XClipboardService.java Tue Feb 05 15:58:12 2008 -0500 @@ -0,0 +1,80 @@ +/* XClipboardService.java + Copyright (C) 2008 Red Hat, Inc. + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, version 2. + +IcedTea is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. +*/ + +package netx.jnlp.services; + +import javax.jnlp.*; +import netx.jnlp.*; +import java.awt.datatransfer.Transferable; +import java.awt.Toolkit; +import netx.jnlp.security.SecurityWarningDialog; + +/** + * The ClipboardService JNLP service. + * + * @author <a href="mailto:jsumali@redhat.com">Joshua Sumali</a> + */ +class XClipboardService implements ClipboardService { + + protected XClipboardService() { + } + + /** + * Returns the contents of the system clipboard. + */ + public java.awt.datatransfer.Transferable getContents(){ + + if (ServiceUtil.checkAccess(SecurityWarningDialog.AccessType.CLIPBOARD_READ)) { + Transferable t = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null); + return (Transferable) ServiceUtil.createPrivilegedProxy( + Transferable.class, t); + } else { + return null; + } + } + + /** + * Sets the contents of the system clipboard. + */ + public void setContents(java.awt.datatransfer.Transferable contents) { + if (ServiceUtil.checkAccess(SecurityWarningDialog.AccessType.CLIPBOARD_WRITE)) { + Toolkit.getDefaultToolkit().getSystemClipboard().setContents( + contents, null); + } + } + +}
--- a/tools/netx/jnlp/services/XDownloadService.java Tue Feb 05 12:26:45 2008 -0500 +++ b/tools/netx/jnlp/services/XDownloadService.java Tue Feb 05 15:58:12 2008 -0500 @@ -25,7 +25,7 @@ import netx.jnlp.*; /** - * The BasicService JNLP service. + * The DownloadService JNLP service. * * @author <a href="mailto:jmaxwell@users.sourceforge.net">Jon A. Maxwell (JAM)</a> - initial author * @version $Revision: 1.7 $
--- a/tools/netx/jnlp/services/XExtensionInstallerService.java Tue Feb 05 12:26:45 2008 -0500 +++ b/tools/netx/jnlp/services/XExtensionInstallerService.java Tue Feb 05 15:58:12 2008 -0500 @@ -25,7 +25,7 @@ import netx.jnlp.*; /** - * The BasicService JNLP service. + * The ExtensionInstallerService JNLP service. * * @author <a href="mailto:jmaxwell@users.sourceforge.net">Jon A. Maxwell (JAM)</a> - initial author * @version $Revision: 1.6 $
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/netx/jnlp/services/XFileOpenService.java Tue Feb 05 15:58:12 2008 -0500 @@ -0,0 +1,112 @@ +/* XFileOpenService.java + Copyright (C) 2008 Red Hat, Inc. + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, version 2. + +IcedTea is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. +*/ + +package netx.jnlp.services; + +import java.io.*; +import java.net.*; +import java.util.*; +import java.lang.ref.*; +import javax.jnlp.*; +import netx.jnlp.*; +import netx.jnlp.runtime.*; + +import javax.swing.JFileChooser; +import javax.swing.JOptionPane; +import java.security.*; +import netx.jnlp.security.SecurityWarningDialog; + +/** + * The FileOpenService JNLP service. + * + * @author <a href="mailto:jsumali@redhat.com">Joshua Sumali</a> + */ +class XFileOpenService implements FileOpenService { + + protected XFileOpenService() { + } + + /** + * Prompts the user to select a single file. + */ + public FileContents openFileDialog (java.lang.String pathHint, + java.lang.String[] extensions) throws java.io.IOException { + + if (ServiceUtil.checkAccess(SecurityWarningDialog.AccessType.READ_FILE)) { + + //open a file dialog here, let the user choose the file. + JFileChooser chooser = new JFileChooser(); + int chosen = chooser.showOpenDialog(null); + if (chosen == JFileChooser.APPROVE_OPTION) { + return (FileContents) ServiceUtil.createPrivilegedProxy( + FileContents.class, + new XFileContents(chooser.getSelectedFile())); + } else { + return null; + } + } else { + return null; + } + } + + /** + * Prompts the user to select one or more files. + */ + public FileContents[] openMultiFileDialog (java.lang.String pathHint, + java.lang.String[] extensions) throws java.io.IOException { + + if (ServiceUtil.checkAccess(SecurityWarningDialog.AccessType.WRITE_FILE)) { + JFileChooser chooser = new JFileChooser(); + chooser.setMultiSelectionEnabled(true); + int chosen = chooser.showOpenDialog(null); + + if (chosen == JFileChooser.APPROVE_OPTION) { + File[] files = chooser.getSelectedFiles(); + int length = files.length; + XFileContents[] xfiles = new XFileContents[length]; + for (int i = 0; i < length; i++) + xfiles[i] = new XFileContents(files[i]); + return (FileContents[]) ServiceUtil.createPrivilegedProxy( + FileContents.class, xfiles); + } else { + return null; + } + } else { + return null; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/netx/jnlp/services/XFileSaveService.java Tue Feb 05 15:58:12 2008 -0500 @@ -0,0 +1,139 @@ +/* XFileSaveService.java + Copyright (C) 2008 Red Hat, Inc. + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, version 2. + +IcedTea is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. +*/ + +package netx.jnlp.services; + +import java.io.*; +import java.net.*; +import java.util.*; +import java.lang.ref.*; +import javax.jnlp.*; +import netx.jnlp.*; +import netx.jnlp.security.SecurityWarningDialog; + +import javax.swing.JFileChooser; +import javax.swing.JOptionPane; +import java.security.*; + +/** + * The FileSaveService JNLP service. + * + * @author <a href="mailto:jsumali@redhat.com">Joshua Sumali</a> + */ +class XFileSaveService implements FileSaveService { + + protected XFileSaveService() { + } + + /** + * Prompts the user to save a file. + */ + public FileContents saveFileDialog(java.lang.String pathHint, + java.lang.String[] extensions, java.io.InputStream stream, + java.lang.String name) throws java.io.IOException { + + if (ServiceUtil.checkAccess(SecurityWarningDialog.AccessType.WRITE_FILE)) { + JFileChooser chooser = new JFileChooser(); + int chosen = chooser.showSaveDialog(null); + + if (chosen == JFileChooser.APPROVE_OPTION) { + writeToFile(stream, chooser.getSelectedFile()); + return (FileContents) ServiceUtil.createPrivilegedProxy( + FileContents.class, + new XFileContents(chooser.getSelectedFile())); + } else { + return null; + } + } else { + return null; + } + } + + /** + * Prompts the user to save a file, with an optional pre-set filename. + */ + public FileContents saveAsFileDialog(java.lang.String pathHint, + java.lang.String[] extensions, FileContents contents) throws java.io.IOException { + + if (ServiceUtil.checkAccess(SecurityWarningDialog.AccessType.WRITE_FILE)) { + JFileChooser chooser = new JFileChooser(); + chooser.setSelectedFile(new File(contents.getName())); + int chosen = chooser.showSaveDialog(null); + + if (chosen == JFileChooser.APPROVE_OPTION) { + writeToFile(contents.getInputStream(), + chooser.getSelectedFile()); + + return (FileContents) ServiceUtil.createPrivilegedProxy( + FileContents.class, + new XFileContents(chooser.getSelectedFile())); + } else { + return null; + } + } else { + return null; + } + } + + /** + * Writes actual file to disk. + */ + private void writeToFile(InputStream stream, File file) throws IOException { + if (!file.createNewFile()) { //file exists + boolean replace = (JOptionPane.showConfirmDialog(null, + file.getAbsolutePath() + " already exists.\n" + +"Do you want to replace it?", + "Warning - File Exists", JOptionPane.YES_NO_OPTION) == 0); + if (!replace) + return; + } else { + file.createNewFile(); + } + + if (file.canWrite()) { + FileOutputStream out = new FileOutputStream(file); + byte[] b = new byte[256]; + int read = 0; + while ((read = stream.read(b)) > 0) + out.write(b, 0, read); + out.flush(); + out.close(); + } else { + throw new IOException("Unable to open file for writing"); + } + } +}
--- a/tools/netx/jnlp/services/XServiceManagerStub.java Tue Feb 05 12:26:45 2008 -0500 +++ b/tools/netx/jnlp/services/XServiceManagerStub.java Tue Feb 05 15:58:12 2008 -0500 @@ -41,11 +41,14 @@ // run less code in the secure environment (or avoid privileged // actions by giving permission to the code source). - private static String serviceNames[] = { + private static String serviceNames[] = { "javax.jnlp.BasicService", // required "javax.jnlp.DownloadService", // required "javax.jnlp.ExtensionInstallerService", // required "javax.jnlp.PersistenceService", + "javax.jnlp.FileOpenService", + "javax.jnlp.FileSaveService", + "javax.jnlp.ClipboardService" }; private static Object services[] = { @@ -53,6 +56,9 @@ ServiceUtil.createPrivilegedProxy(DownloadService.class, new XDownloadService()), ServiceUtil.createPrivilegedProxy(ExtensionInstallerService.class, new XExtensionInstallerService()), ServiceUtil.createPrivilegedProxy(PersistenceService.class, new XPersistenceService()), + ServiceUtil.createPrivilegedProxy(FileOpenService.class, new XFileOpenService()), + ServiceUtil.createPrivilegedProxy(FileSaveService.class, new XFileSaveService()), + ServiceUtil.createPrivilegedProxy(ClipboardService.class, new XClipboardService()), };
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/netx/jnlp/tools/CharacterEncoder.java Tue Feb 05 15:58:12 2008 -0500 @@ -0,0 +1,354 @@ +/* + * Copyright 1995-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package netx.jnlp.tools; + +import java.io.InputStream; +import java.io.ByteArrayInputStream; +import java.io.OutputStream; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.io.IOException; +import java.nio.ByteBuffer; + + +/** + * This class defines the encoding half of character encoders. + * A character encoder is an algorithim for transforming 8 bit binary + * data into text (generally 7 bit ASCII or 8 bit ISO-Latin-1 text) + * for transmition over text channels such as e-mail and network news. + * + * The character encoders have been structured around a central theme + * that, in general, the encoded text has the form: + * + * <pre> + * [Buffer Prefix] + * [Line Prefix][encoded data atoms][Line Suffix] + * [Buffer Suffix] + * </pre> + * + * In the CharacterEncoder and CharacterDecoder classes, one complete + * chunk of data is referred to as a <i>buffer</i>. Encoded buffers + * are all text, and decoded buffers (sometimes just referred to as + * buffers) are binary octets. + * + * To create a custom encoder, you must, at a minimum, overide three + * abstract methods in this class. + * <DL> + * <DD>bytesPerAtom which tells the encoder how many bytes to + * send to encodeAtom + * <DD>encodeAtom which encodes the bytes sent to it as text. + * <DD>bytesPerLine which tells the encoder the maximum number of + * bytes per line. + * </DL> + * + * Several useful encoders have already been written and are + * referenced in the See Also list below. + * + * @author Chuck McManis + * @see CharacterDecoder; + * @see UCEncoder + * @see UUEncoder + * @see BASE64Encoder + */ +public abstract class CharacterEncoder { + + /** Stream that understands "printing" */ + protected PrintStream pStream; + + /** Return the number of bytes per atom of encoding */ + abstract protected int bytesPerAtom(); + + /** Return the number of bytes that can be encoded per line */ + abstract protected int bytesPerLine(); + + /** + * Encode the prefix for the entire buffer. By default is simply + * opens the PrintStream for use by the other functions. + */ + protected void encodeBufferPrefix(OutputStream aStream) throws IOException { + pStream = new PrintStream(aStream); + } + + /** + * Encode the suffix for the entire buffer. + */ + protected void encodeBufferSuffix(OutputStream aStream) throws IOException { + } + + /** + * Encode the prefix that starts every output line. + */ + protected void encodeLinePrefix(OutputStream aStream, int aLength) + throws IOException { + } + + /** + * Encode the suffix that ends every output line. By default + * this method just prints a <newline> into the output stream. + */ + protected void encodeLineSuffix(OutputStream aStream) throws IOException { + pStream.println(); + } + + /** Encode one "atom" of information into characters. */ + abstract protected void encodeAtom(OutputStream aStream, byte someBytes[], + int anOffset, int aLength) throws IOException; + + /** + * This method works around the bizarre semantics of BufferedInputStream's + * read method. + */ + protected int readFully(InputStream in, byte buffer[]) + throws java.io.IOException { + for (int i = 0; i < buffer.length; i++) { + int q = in.read(); + if (q == -1) + return i; + buffer[i] = (byte)q; + } + return buffer.length; + } + + /** + * Encode bytes from the input stream, and write them as text characters + * to the output stream. This method will run until it exhausts the + * input stream, but does not print the line suffix for a final + * line that is shorter than bytesPerLine(). + */ + public void encode(InputStream inStream, OutputStream outStream) + throws IOException { + int j; + int numBytes; + byte tmpbuffer[] = new byte[bytesPerLine()]; + + encodeBufferPrefix(outStream); + + while (true) { + numBytes = readFully(inStream, tmpbuffer); + if (numBytes == 0) { + break; + } + encodeLinePrefix(outStream, numBytes); + for (j = 0; j < numBytes; j += bytesPerAtom()) { + + if ((j + bytesPerAtom()) <= numBytes) { + encodeAtom(outStream, tmpbuffer, j, bytesPerAtom()); + } else { + encodeAtom(outStream, tmpbuffer, j, (numBytes)- j); + } + } + if (numBytes < bytesPerLine()) { + break; + } else { + encodeLineSuffix(outStream); + } + } + encodeBufferSuffix(outStream); + } + + /** + * Encode the buffer in <i>aBuffer</i> and write the encoded + * result to the OutputStream <i>aStream</i>. + */ + public void encode(byte aBuffer[], OutputStream aStream) + throws IOException { + ByteArrayInputStream inStream = new ByteArrayInputStream(aBuffer); + encode(inStream, aStream); + } + + /** + * A 'streamless' version of encode that simply takes a buffer of + * bytes and returns a string containing the encoded buffer. + */ + public String encode(byte aBuffer[]) { + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + ByteArrayInputStream inStream = new ByteArrayInputStream(aBuffer); + String retVal = null; + try { + encode(inStream, outStream); + // explicit ascii->unicode conversion + retVal = outStream.toString("8859_1"); + } catch (Exception IOException) { + // This should never happen. + throw new Error("CharacterEncoder.encode internal error"); + } + return (retVal); + } + + /** + * Return a byte array from the remaining bytes in this ByteBuffer. + * <P> + * The ByteBuffer's position will be advanced to ByteBuffer's limit. + * <P> + * To avoid an extra copy, the implementation will attempt to return the + * byte array backing the ByteBuffer. If this is not possible, a + * new byte array will be created. + */ + private byte [] getBytes(ByteBuffer bb) { + /* + * This should never return a BufferOverflowException, as we're + * careful to allocate just the right amount. + */ + byte [] buf = null; + + /* + * If it has a usable backing byte buffer, use it. Use only + * if the array exactly represents the current ByteBuffer. + */ + if (bb.hasArray()) { + byte [] tmp = bb.array(); + if ((tmp.length == bb.capacity()) && + (tmp.length == bb.remaining())) { + buf = tmp; + bb.position(bb.limit()); + } + } + + if (buf == null) { + /* + * This class doesn't have a concept of encode(buf, len, off), + * so if we have a partial buffer, we must reallocate + * space. + */ + buf = new byte[bb.remaining()]; + + /* + * position() automatically updated + */ + bb.get(buf); + } + + return buf; + } + + /** + * Encode the <i>aBuffer</i> ByteBuffer and write the encoded + * result to the OutputStream <i>aStream</i>. + * <P> + * The ByteBuffer's position will be advanced to ByteBuffer's limit. + */ + public void encode(ByteBuffer aBuffer, OutputStream aStream) + throws IOException { + byte [] buf = getBytes(aBuffer); + encode(buf, aStream); + } + + /** + * A 'streamless' version of encode that simply takes a ByteBuffer + * and returns a string containing the encoded buffer. + * <P> + * The ByteBuffer's position will be advanced to ByteBuffer's limit. + */ + public String encode(ByteBuffer aBuffer) { + byte [] buf = getBytes(aBuffer); + return encode(buf); + } + + /** + * Encode bytes from the input stream, and write them as text characters + * to the output stream. This method will run until it exhausts the + * input stream. It differs from encode in that it will add the + * line at the end of a final line that is shorter than bytesPerLine(). + */ + public void encodeBuffer(InputStream inStream, OutputStream outStream) + throws IOException { + int j; + int numBytes; + byte tmpbuffer[] = new byte[bytesPerLine()]; + + encodeBufferPrefix(outStream); + + while (true) { + numBytes = readFully(inStream, tmpbuffer); + if (numBytes == 0) { + break; + } + encodeLinePrefix(outStream, numBytes); + for (j = 0; j < numBytes; j += bytesPerAtom()) { + if ((j + bytesPerAtom()) <= numBytes) { + encodeAtom(outStream, tmpbuffer, j, bytesPerAtom()); + } else { + encodeAtom(outStream, tmpbuffer, j, (numBytes)- j); + } + } + encodeLineSuffix(outStream); + if (numBytes < bytesPerLine()) { + break; + } + } + encodeBufferSuffix(outStream); + } + + /** + * Encode the buffer in <i>aBuffer</i> and write the encoded + * result to the OutputStream <i>aStream</i>. + */ + public void encodeBuffer(byte aBuffer[], OutputStream aStream) + throws IOException { + ByteArrayInputStream inStream = new ByteArrayInputStream(aBuffer); + encodeBuffer(inStream, aStream); + } + + /** + * A 'streamless' version of encode that simply takes a buffer of + * bytes and returns a string containing the encoded buffer. + */ + public String encodeBuffer(byte aBuffer[]) { + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + ByteArrayInputStream inStream = new ByteArrayInputStream(aBuffer); + try { + encodeBuffer(inStream, outStream); + } catch (Exception IOException) { + // This should never happen. + throw new Error("CharacterEncoder.encodeBuffer internal error"); + } + return (outStream.toString()); + } + + /** + * Encode the <i>aBuffer</i> ByteBuffer and write the encoded + * result to the OutputStream <i>aStream</i>. + * <P> + * The ByteBuffer's position will be advanced to ByteBuffer's limit. + */ + public void encodeBuffer(ByteBuffer aBuffer, OutputStream aStream) + throws IOException { + byte [] buf = getBytes(aBuffer); + encodeBuffer(buf, aStream); + } + + /** + * A 'streamless' version of encode that simply takes a ByteBuffer + * and returns a string containing the encoded buffer. + * <P> + * The ByteBuffer's position will be advanced to ByteBuffer's limit. + */ + public String encodeBuffer(ByteBuffer aBuffer) { + byte [] buf = getBytes(aBuffer); + return encodeBuffer(buf); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/netx/jnlp/tools/HexDumpEncoder.java Tue Feb 05 15:58:12 2008 -0500 @@ -0,0 +1,120 @@ +/* + * Copyright 1995-1997 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package netx.jnlp.tools; + +import java.io.PrintStream; +import java.io.OutputStream; +import java.io.IOException; + +/** + * This class encodes a buffer into the classic: "Hexadecimal Dump" format of + * the past. It is useful for analyzing the contents of binary buffers. + * The format produced is as follows: + * <pre> + * xxxx: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff ................ + * </pre> + * Where xxxx is the offset into the buffer in 16 byte chunks, followed + * by ascii coded hexadecimal bytes followed by the ASCII representation of + * the bytes or '.' if they are not valid bytes. + * + * @author Chuck McManis + */ + +public class HexDumpEncoder extends CharacterEncoder { + + private int offset; + private int thisLineLength; + private int currentByte; + private byte thisLine[] = new byte[16]; + + static void hexDigit(PrintStream p, byte x) { + char c; + + c = (char) ((x >> 4) & 0xf); + if (c > 9) + c = (char) ((c-10) + 'A'); + else + c = (char)(c + '0'); + p.write(c); + c = (char) (x & 0xf); + if (c > 9) + c = (char)((c-10) + 'A'); + else + c = (char)(c + '0'); + p.write(c); + } + + protected int bytesPerAtom() { + return (1); + } + + protected int bytesPerLine() { + return (16); + } + + protected void encodeBufferPrefix(OutputStream o) throws IOException { + offset = 0; + super.encodeBufferPrefix(o); + } + + protected void encodeLinePrefix(OutputStream o, int len) throws IOException { + hexDigit(pStream, (byte)((offset >>> 8) & 0xff)); + hexDigit(pStream, (byte)(offset & 0xff)); + pStream.print(": "); + currentByte = 0; + thisLineLength = len; + } + + protected void encodeAtom(OutputStream o, byte buf[], int off, int len) throws IOException { + thisLine[currentByte] = buf[off]; + hexDigit(pStream, buf[off]); + pStream.print(" "); + currentByte++; + if (currentByte == 8) + pStream.print(" "); + } + + protected void encodeLineSuffix(OutputStream o) throws IOException { + if (thisLineLength < 16) { + for (int i = thisLineLength; i < 16; i++) { + pStream.print(" "); + if (i == 7) + pStream.print(" "); + } + } + pStream.print(" "); + for (int i = 0; i < thisLineLength; i++) { + if ((thisLine[i] < ' ') || (thisLine[i] > 'z')) { + pStream.print("."); + } else { + pStream.write(thisLine[i]); + } + } + pStream.println(); + offset += thisLineLength; + } + +}
--- a/tools/netx/jnlp/tools/JarSigner.java Tue Feb 05 12:26:45 2008 -0500 +++ b/tools/netx/jnlp/tools/JarSigner.java Tue Feb 05 15:58:12 2008 -0500 @@ -33,10 +33,16 @@ import java.text.MessageFormat; import java.security.cert.Certificate; import java.security.cert.X509Certificate; +import java.security.cert.CertPath; import java.security.*; import sun.security.x509.*; import sun.security.util.*; +import netx.jnlp.cache.*; +import netx.jnlp.*; +import netx.jnlp.security.*; +import netx.jnlp.runtime.JNLPRuntime; + /** * <p>The jarsigner utility. * @@ -46,6 +52,10 @@ public class JarSigner { + private static String R(String key) { + return JNLPRuntime.getMessage(key); + } + private static final Collator collator = Collator.getInstance(); static { // this is for case insensitive string comparisions @@ -108,10 +118,11 @@ // read zip entry raw bytes private ByteArrayOutputStream baos = new ByteArrayOutputStream(2048); private byte[] buffer = new byte[8192]; - // private ContentSigner signingMechanism = null; +// private ContentSigner signingMechanism = null; private String altSignerClass = null; private String altSignerClasspath = null; private ZipFile zipFile = null; + private boolean hasUnsignedEntry = false; private boolean hasExpiredCert = false; private boolean hasExpiringCert = false; private boolean notYetValidCert = false; @@ -120,6 +131,73 @@ private boolean badExtendedKeyUsage = false; private boolean badNetscapeCertType = false; + private boolean allVerified = true; + + private boolean anyJarsSigned = false; + + /** all of the jar files that were verified */ + private ArrayList<String> verifiedJars = null; + + /** all of the jar files that were not verified */ + private ArrayList<String> unverifiedJars = null; + + /** the certificates used for jar verification */ + private ArrayList<CertPath> certs = null; + + /** details of this signing */ + private ArrayList<String> details = new ArrayList<String>(); + + public boolean hasSigningIssues() { + return hasExpiredCert || notYetValidCert || badKeyUsage + || badExtendedKeyUsage || badNetscapeCertType; + } + + public boolean allVerified() { + return allVerified; + } + + public boolean anyJarsSigned() { + return anyJarsSigned; + } + + public ArrayList<String> getDetails() { + return details; + } + + public ArrayList<CertPath> getCerts() { + return certs; + } + + public void verifyJars(List<JARDesc> jars, ResourceTracker tracker) + throws Exception { + + for (int i = 0; i < jars.size(); i++) { + + JARDesc jar = (JARDesc) jars.get(i); + verifiedJars = new ArrayList<String>(); + unverifiedJars = new ArrayList<String>(); + certs = new ArrayList<CertPath>(); + + try { + String localFile = tracker.getCacheFile(jar.getLocation()).getAbsolutePath(); + boolean result = verifyJar(localFile); + + if (!result) { + //allVerified is true until we encounter a problem + //with one or more jars + allVerified = false; + unverifiedJars.add(localFile); + } else { + verifiedJars.add(localFile); + } + } catch (Exception e){ + //We may catch exceptions from using js.verifyJar(localFile). + e.printStackTrace(); + throw e; + } + } + } + public boolean verifyJar(String jarName) throws Exception { boolean anySigned = false; boolean hasUnsignedEntry = false; @@ -167,14 +245,16 @@ && !signatureRelated(name); if (isSigned) { for (int i = 0; i < signers.length; i++) { - Certificate cert = - signers[i].getSignerCertPath() - .getCertificates().get(0); + CertPath certPath = signers[i].getSignerCertPath(); + if (!certs.contains(certPath)) + certs.add(certPath); + Certificate cert = signers[i].getSignerCertPath() + .getCertificates().get(0); if (cert instanceof X509Certificate) { checkCertUsage((X509Certificate)cert, null); if (!showcerts) { long notAfter = ((X509Certificate)cert) - .getNotAfter().getTime(); + .getNotAfter().getTime(); if (notAfter < now) { hasExpiredCert = true; @@ -185,10 +265,39 @@ } } } - } //while e has more elements } //if man not null - + + //Alert the user if any of the following are true. + if (!anySigned) { + + } else { + anyJarsSigned = true; + + //warnings + if (hasUnsignedEntry || hasExpiredCert || hasExpiringCert || + badKeyUsage || badExtendedKeyUsage || badNetscapeCertType || + notYetValidCert) { + + addToDetails(R("SRunWithoutRestrictions")); + + if (badKeyUsage) + addToDetails(R("SBadKeyUsage")); + if (badExtendedKeyUsage) + addToDetails(R("SBadExtendedKeyUsage")); + if (badNetscapeCertType) + addToDetails(R("SBadNetscapeCertType")); + if (hasUnsignedEntry) + addToDetails(R("SHasUnsignedEntry")); + if (hasExpiredCert) + addToDetails(R("SHasExpiredCert")); + if (hasExpiringCert) + addToDetails(R("SHasExpiringCert")); + if (notYetValidCert) + addToDetails(R("SNotYetValidCert")); + } + } + } catch (Exception e) { e.printStackTrace(); throw e; @@ -198,9 +307,17 @@ } } - return anySigned; + //anySigned does not guarantee that all files were signed. + return anySigned && !(hasUnsignedEntry || hasExpiredCert + || badKeyUsage || badExtendedKeyUsage || badNetscapeCertType + || notYetValidCert); } + private void addToDetails(String detail) { + if (!details.contains(detail)) + details.add(detail); + } + private static MessageFormat validityTimeForm = null; private static MessageFormat notYetTimeForm = null; private static MessageFormat expiredTimeForm = null; @@ -210,7 +327,7 @@ Hashtable<Certificate, String> storeHash = - new Hashtable<Certificate, String>(); + new Hashtable<Certificate, String>(); /** * signature-related files include: @@ -219,20 +336,20 @@ * . META-INF/*.SF * . META-INF/*.DSA * . META-INF/*.RSA - * + * * Required for verifyJar() */ private boolean signatureRelated(String name) { String ucName = name.toUpperCase(); if (ucName.equals(JarFile.MANIFEST_NAME) || - ucName.equals(META_INF) || - (ucName.startsWith(SIG_PREFIX) && - ucName.indexOf("/") == ucName.lastIndexOf("/"))) { + ucName.equals(META_INF) || + (ucName.startsWith(SIG_PREFIX) && + ucName.indexOf("/") == ucName.lastIndexOf("/"))) { return true; } if (ucName.startsWith(META_INF) && - SignatureFileVerifier.isBlockOrSF(ucName)) { + SignatureFileVerifier.isBlockOrSF(ucName)) { // .SF/.DSA/.RSA files in META-INF subdirs // are not considered signature-related return (ucName.indexOf("/") == ucName.lastIndexOf("/")); @@ -248,7 +365,7 @@ * NetscapeCertType has codeSigning flag turned on. * If null, the class field badKeyUsage, badExtendedKeyUsage, * badNetscapeCertType will be set. - * + * * Required for verifyJar() */ void checkCertUsage(X509Certificate userCert, boolean[] bad) { @@ -293,18 +410,18 @@ try { // OID_NETSCAPE_CERT_TYPE byte[] netscapeEx = userCert.getExtensionValue - ("2.16.840.1.113730.1.1"); + ("2.16.840.1.113730.1.1"); if (netscapeEx != null) { DerInputStream in = new DerInputStream(netscapeEx); byte[] encoded = in.getOctetString(); encoded = new DerValue(encoded).getUnalignedBitString() - .toByteArray(); + .toByteArray(); NetscapeCertTypeExtension extn = - new NetscapeCertTypeExtension(encoded); + new NetscapeCertTypeExtension(encoded); Boolean val = (Boolean)extn.get( - NetscapeCertTypeExtension.OBJECT_SIGNING); + NetscapeCertTypeExtension.OBJECT_SIGNING); if (!val) { if (bad != null) { bad[2] = true;