aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog32
-rw-r--r--netx/net/sourceforge/jnlp/PluginBridge.java4
-rw-r--r--netx/net/sourceforge/jnlp/resources/Messages.properties9
-rw-r--r--netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java17
-rw-r--r--netx/net/sourceforge/jnlp/security/SecurityDialog.java3
-rw-r--r--netx/net/sourceforge/jnlp/security/SecurityDialogs.java24
-rw-r--r--netx/net/sourceforge/jnlp/security/UnsignedAppletTrustWarningDialog.java63
-rw-r--r--netx/net/sourceforge/jnlp/security/UnsignedAppletTrustWarningPanel.java228
-rw-r--r--netx/net/sourceforge/jnlp/security/appletextendedsecurity/UnsignedAppletActionStorage.java17
-rw-r--r--netx/net/sourceforge/jnlp/security/appletextendedsecurity/UnsignedAppletTrustConfirmation.java209
-rw-r--r--plugin/icedteanp/java/sun/applet/PluginAppletViewer.java13
-rw-r--r--tests/netx/unit/net/sourceforge/jnlp/security/appletextendedsecurity/UnsignedAppletTrustConfirmationTest.java59
12 files changed, 668 insertions, 10 deletions
diff --git a/ChangeLog b/ChangeLog
index 84a7737..f342cef 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,35 @@
+2013-03-26 Adam Domurad <[email protected]>
+
+ Integration of unsigned applet confirmation dialogue.
+ * netx/net/sourceforge/jnlp/PluginBridge.java
+ (getArchiveJars): New, returns archive jars as list
+ * netx/net/sourceforge/jnlp/resources/Messages.properties:
+ Confirmation messages added to properties file
+ * netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java
+ (getInstance): Initialization refactored into createInstance
+ (createInstance): New, checks if unsigned applet is allowed,
+ initializes classloader.
+ (initializeResources): Don't consider no-jar applets signed.
+ * netx/net/sourceforge/jnlp/security/SecurityDialogs.java
+ (showUnsignedWarningDialog): Creates message with
+ DialogType.UNSIGNED_WARNING
+ * netx/net/sourceforge/jnlp/security/SecurityDialog.java
+ (installPanel): Add case for DialogType.UNSIGNED_WARNING
+ * netx/net/sourceforge/jnlp/security/appletextendedsecurity/UnsignedAppletActionStorage.java:
+ Expose locking members from interface
+ * plugin/icedteanp/java/sun/applet/PluginAppletViewer.java
+ (handleInitializationMessage): Do nothing if applets have been
+ disabled.
+ * netx/net/sourceforge/jnlp/security/UnsignedAppletTrustWarningDialog.java:
+ New, security dialog that asks for unsigned applet confirmation.
+ * netx/net/sourceforge/jnlp/security/UnsignedAppletTrustWarningPanel.java:
+ Implements the dialog contents for unsigned applet confirmation.
+ * netx/net/sourceforge/jnlp/security/appletextendedsecurity/UnsignedAppletTrustConfirmation.java:
+ Updates and checks applet confirmation storage, creates warning dialog
+ if required.
+ * tests/netx/unit/net/sourceforge/jnlp/security/appletextendedsecurity/UnsignedAppletTrustConfirmationTest.java:
+ New, tests relative & normalized path creation helpers.
+
2013-03-26 Jiri Vanek <[email protected]>
Path validator fixed to be correctly multiplatform
diff --git a/netx/net/sourceforge/jnlp/PluginBridge.java b/netx/net/sourceforge/jnlp/PluginBridge.java
index 1e34c49..98dee8e 100644
--- a/netx/net/sourceforge/jnlp/PluginBridge.java
+++ b/netx/net/sourceforge/jnlp/PluginBridge.java
@@ -206,6 +206,10 @@ public class PluginBridge extends JNLPFile {
}
}
+ public List<String> getArchiveJars() {
+ return new ArrayList<String>(jars);
+ }
+
public boolean codeBaseLookup() {
return params.useCodebaseLookup();
}
diff --git a/netx/net/sourceforge/jnlp/resources/Messages.properties b/netx/net/sourceforge/jnlp/resources/Messages.properties
index 105b09e..7d66087 100644
--- a/netx/net/sourceforge/jnlp/resources/Messages.properties
+++ b/netx/net/sourceforge/jnlp/resources/Messages.properties
@@ -80,6 +80,9 @@ LUnsignedJarWithSecurity=Cannot grant permissions to unsigned jars.
LUnsignedJarWithSecurityInfo=Application requested security permissions, but jars are not signed.
LSignedJNLPAppDifferentCerts=The JNLP application is not fully signed by a single cert.
LSignedJNLPAppDifferentCertsInfo=The JNLP application has its components individually signed, however there must be a common signer to all entries.
+LUnsignedApplet=The applet was unsigned.
+LUnsignedAppletPolicyDenied=The applet was unsigned, and the security policy prevented it from running.
+LUnsignedAppletUserDenied=The applet was unsigned, and was not trusted.
LSignedAppJarUsingUnsignedJar=Signed application using unsigned jars.
LSignedAppJarUsingUnsignedJarInfo=The main application jar is signed, but some of the jars it is using aren't.
LSignedJNLPFileDidNotMatch=The signed JNLP file did not match the launching JNLP file.
@@ -220,6 +223,12 @@ SNoAssociatedCertificate=<no associated certificate>
SUnverified=(unverified)
SAlwaysTrustPublisher=Always trust content from this publisher
SHttpsUnverified=The website's HTTPS certificate cannot be verified.
+SRememberOption=<b>Remember this option?</b>
+SUnsignedSummary=An unsigned Java application wants to run
+SUnsignedDetail=An unsigned application from the following location wants to run:<br><u>{0}</u><br><br><b>It is recommended you only run applications from sites you trust.</b>
+SUnsignedAllowedBefore=<font color="green">You have accepted this applet previously.</font>
+SUnsignedRejectedBefore=<font color="red">You have rejected this applet previously.</font>
+SUnsignedQuestion=Allow the applet to run?
SNotAllSignedSummary=Only parts of this application code are signed.
SNotAllSignedDetail=This application contains both signed and unsigned code. While signed code is safe if you trust the provider, unsigned code may imply code outside of the trusted provider's control.
SNotAllSignedQuestion=Do you wish to proceed and run this application anyway?
diff --git a/netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java b/netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java
index 484d4f4..983979e 100644
--- a/netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java
+++ b/netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java
@@ -63,6 +63,7 @@ import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
+import net.sourceforge.jnlp.security.appletextendedsecurity.UnsignedAppletTrustConfirmation;
import net.sourceforge.jnlp.AppletDesc;
import net.sourceforge.jnlp.ApplicationDesc;
import net.sourceforge.jnlp.DownloadOptions;
@@ -380,6 +381,13 @@ public class JNLPClassLoader extends URLClassLoader {
JNLPClassLoader baseLoader = uniqueKeyToLoader.get(uniqueKey);
JNLPClassLoader loader = new JNLPClassLoader(file, policy, mainName);
+ // If security level is 'high' or greater, we must check if the user allows unsigned applets
+ // when the JNLPClassLoader is created. We do so here, because doing so in the constructor
+ // causes unwanted side-effects for some applets
+ if (!loader.getSigning() && file instanceof PluginBridge) {
+ UnsignedAppletTrustConfirmation.checkUnsignedWithUserIfRequired((PluginBridge)file);
+ }
+
// New loader init may have caused extentions to create a
// loader for this unique key. Check.
JNLPClassLoader extLoader = uniqueKeyToLoader.get(uniqueKey);
@@ -596,9 +604,9 @@ public class JNLPClassLoader extends URLClassLoader {
JARDesc jars[] = resources.getJARs();
- if (jars == null || jars.length == 0) {
+ if (jars.length == 0) {
- boolean allSigned = true;
+ boolean allSigned = (loaders.length > 1) /* has extensions */;
for (int i = 1; i < loaders.length; i++) {
if (!loaders[i].getSigning()) {
allSigned = false;
@@ -681,7 +689,6 @@ public class JNLPClassLoader extends URLClassLoader {
!SecurityDialogs.showNotAllSignedWarningDialog(file))
throw new LaunchException(file, null, R("LSFatal"), R("LCClient"), R("LSignedAppJarUsingUnsignedJar"), R("LSignedAppJarUsingUnsignedJarInfo"));
-
// Check for main class in the downloaded jars, and check/verify signed JNLP fill
checkForMain(initialJars);
@@ -718,9 +725,9 @@ public class JNLPClassLoader extends URLClassLoader {
}
} else {
+ // Otherwise this jar is simply unsigned -- make sure to ask
+ // for permission on certain actions
signing = false;
- //otherwise this jar is simply unsigned -- make sure to ask
- //for permission on certain actions
}
}
diff --git a/netx/net/sourceforge/jnlp/security/SecurityDialog.java b/netx/net/sourceforge/jnlp/security/SecurityDialog.java
index 6420c29..e204f0e 100644
--- a/netx/net/sourceforge/jnlp/security/SecurityDialog.java
+++ b/netx/net/sourceforge/jnlp/security/SecurityDialog.java
@@ -38,6 +38,7 @@ exception statement from your version.
package net.sourceforge.jnlp.security;
import net.sourceforge.jnlp.JNLPFile;
+import net.sourceforge.jnlp.PluginBridge;
import net.sourceforge.jnlp.runtime.JNLPRuntime;
import net.sourceforge.jnlp.security.SecurityDialogs.AccessType;
import net.sourceforge.jnlp.security.SecurityDialogs.DialogType;
@@ -306,6 +307,8 @@ public class SecurityDialog extends JDialog {
panel = new AppletWarningPane(this, this.certVerifier);
else if (dialogType == DialogType.NOTALLSIGNED_WARNING)
panel = new NotAllSignedWarningPane(this);
+ else if (dialogType == DialogType.UNSIGNED_WARNING) // Only necessary for applets on 'high security' or above
+ panel = new UnsignedAppletTrustWarningDialog(this, (PluginBridge)file);
else if (dialogType == DialogType.AUTHENTICATION)
panel = new PasswordAuthenticationPane(this, extras);
diff --git a/netx/net/sourceforge/jnlp/security/SecurityDialogs.java b/netx/net/sourceforge/jnlp/security/SecurityDialogs.java
index c5bf824..a1af82e 100644
--- a/netx/net/sourceforge/jnlp/security/SecurityDialogs.java
+++ b/netx/net/sourceforge/jnlp/security/SecurityDialogs.java
@@ -37,6 +37,8 @@ exception statement from your version.
package net.sourceforge.jnlp.security;
+import net.sourceforge.jnlp.security.appletextendedsecurity.ExecuteUnsignedApplet;
+
import java.awt.Dialog.ModalityType;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
@@ -70,6 +72,7 @@ public class SecurityDialogs {
SINGLE_CERT_INFO,
ACCESS_WARNING,
NOTALLSIGNED_WARNING,
+ UNSIGNED_WARNING, /* requires confirmation with 'high-security' setting */
APPLET_WARNING,
AUTHENTICATION,
}
@@ -86,6 +89,7 @@ public class SecurityDialogs {
VERIFIED,
UNVERIFIED,
NOTALLSIGNED,
+ UNSIGNED, /* requires confirmation with 'high-security' setting */
SIGNING_ERROR
}
@@ -173,6 +177,26 @@ public class SecurityDialogs {
}
/**
+ * Shows a warning dialog for when a plugin applet is unsigned.
+ * This is used with 'high-security' setting.
+ *
+ * @return true if permission was granted by the user, false otherwise.
+ */
+ public static ExecuteUnsignedApplet showUnsignedWarningDialog(JNLPFile file) {
+
+ if (!shouldPromptUser()) {
+ return ExecuteUnsignedApplet.NO;
+ }
+
+ final SecurityDialogMessage message = new SecurityDialogMessage();
+ message.dialogType = DialogType.UNSIGNED_WARNING;
+ message.accessType = AccessType.UNSIGNED;
+ message.file = file;
+
+ return (ExecuteUnsignedApplet)getUserResponse(message);
+ }
+
+ /**
* 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
diff --git a/netx/net/sourceforge/jnlp/security/UnsignedAppletTrustWarningDialog.java b/netx/net/sourceforge/jnlp/security/UnsignedAppletTrustWarningDialog.java
new file mode 100644
index 0000000..4860e52
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/security/UnsignedAppletTrustWarningDialog.java
@@ -0,0 +1,63 @@
+/* Copyright (C) 2013 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 net.sourceforge.jnlp.security;
+
+import net.sourceforge.jnlp.PluginBridge;
+import net.sourceforge.jnlp.security.UnsignedAppletTrustWarningPanel.ActionChoiceListener;
+import net.sourceforge.jnlp.security.appletextendedsecurity.ExecuteUnsignedApplet;
+
+/**
+ * A panel that confirms that the user is OK with unsigned code running.
+ *
+ */
+public class UnsignedAppletTrustWarningDialog extends SecurityDialogPanel {
+
+ public UnsignedAppletTrustWarningDialog(SecurityDialog x, PluginBridge file) {
+ super(x);
+
+ add(new UnsignedAppletTrustWarningPanel(file,
+ new ActionChoiceListener() {
+ @Override
+ public void actionChosen(ExecuteUnsignedApplet action) {
+ parent.setValue(action);
+ parent.dispose();
+ }
+ })
+ );
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/security/UnsignedAppletTrustWarningPanel.java b/netx/net/sourceforge/jnlp/security/UnsignedAppletTrustWarningPanel.java
new file mode 100644
index 0000000..e20967c
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/security/UnsignedAppletTrustWarningPanel.java
@@ -0,0 +1,228 @@
+/* Copyright (C) 2013 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 net.sourceforge.jnlp.security;
+
+import static net.sourceforge.jnlp.runtime.Translator.R;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.Font;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.SwingConstants;
+
+import net.sourceforge.jnlp.JNLPFile;
+import net.sourceforge.jnlp.PluginBridge;
+import net.sourceforge.jnlp.security.appletextendedsecurity.ExecuteUnsignedApplet;
+import net.sourceforge.jnlp.security.appletextendedsecurity.UnsignedAppletTrustConfirmation;
+
+public class UnsignedAppletTrustWarningPanel extends JPanel {
+
+ /*
+ * Callback for when action is decided.
+ */
+ public static interface ActionChoiceListener {
+ void actionChosen(ExecuteUnsignedApplet action);
+ }
+
+ private final int PANE_WIDTH = 500;
+
+ private final int TOP_PANEL_HEIGHT = 60;
+ private final int INFO_PANEL_HEIGHT = 100;
+ private final int INFO_PANEL_HINT_HEIGHT = 25;
+ private final int QUESTION_PANEL_HEIGHT = 35;
+
+ private JButton allowButton;
+ private JButton rejectButton;
+ private JCheckBox permanencyCheckBox;
+
+ private PluginBridge file;
+
+ private ActionChoiceListener actionChoiceListener;
+
+ public UnsignedAppletTrustWarningPanel(PluginBridge file, ActionChoiceListener actionChoiceListener) {
+
+ this.file = file;
+ this.actionChoiceListener = actionChoiceListener;
+
+ addComponents();
+ }
+
+ public JButton getAllowButton() {
+ return allowButton;
+ }
+
+ public JButton getRejectButton() {
+ return rejectButton;
+ }
+
+ private String htmlWrap(String text) {
+ return "<html>" + text + "</html>";
+ }
+
+ private ImageIcon infoImage() {
+ final String location = "net/sourceforge/jnlp/resources/info-small.png";
+ final ClassLoader appLoader = new sun.misc.Launcher().getClassLoader();
+ return new ImageIcon(appLoader.getResource(location));
+ }
+
+ private void setupTopPanel() {
+ final String topLabelText = R("SUnsignedSummary");
+
+ JLabel topLabel = new JLabel(htmlWrap(topLabelText), infoImage(),
+ SwingConstants.LEFT);
+ 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(PANE_WIDTH, TOP_PANEL_HEIGHT));
+ topPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+
+ add(topPanel);
+ }
+
+ private void setupInfoPanel() {
+ String infoLabelText = R("SUnsignedDetail", file.getCodeBase());
+ ExecuteUnsignedApplet rememberedAction = UnsignedAppletTrustConfirmation.getStoredAction((PluginBridge)file);
+ int panelHeight = INFO_PANEL_HEIGHT;
+ if (rememberedAction == ExecuteUnsignedApplet.YES) {
+ infoLabelText += "<br>" + R("SUnsignedAllowedBefore");
+ panelHeight += INFO_PANEL_HINT_HEIGHT;
+ } else if (rememberedAction == ExecuteUnsignedApplet.NO) {
+ infoLabelText += "<br>" + R("SUnsignedRejectedBefore");
+ panelHeight += INFO_PANEL_HINT_HEIGHT;
+ }
+
+ JLabel infoLabel = new JLabel(htmlWrap(infoLabelText));
+ JPanel infoPanel = new JPanel(new BorderLayout());
+ infoPanel.add(infoLabel, BorderLayout.CENTER);
+ infoPanel.setPreferredSize(new Dimension(PANE_WIDTH, panelHeight));
+ infoPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+
+ add(infoPanel);
+ }
+
+ private void setupQuestionsPanel() {
+ JPanel questionPanel = new JPanel(new BorderLayout());
+
+ questionPanel.add(new JLabel(htmlWrap(R("SUnsignedQuestion"))), BorderLayout.EAST);
+
+ questionPanel.setPreferredSize(new Dimension(PANE_WIDTH, QUESTION_PANEL_HEIGHT));
+ questionPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+
+ add(questionPanel);
+ }
+
+ private JPanel createCheckBoxPanel() {
+ JPanel checkBoxPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
+
+ permanencyCheckBox = new JCheckBox(htmlWrap(R("SRememberOption")));
+ checkBoxPanel.add(permanencyCheckBox);
+
+ checkBoxPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+
+ return checkBoxPanel;
+ }
+
+ private JPanel createButtonPanel() {
+ JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
+
+ allowButton = new JButton(R("ButProceed"));
+ rejectButton = new JButton(R("ButCancel"));
+
+ allowButton.addActionListener(chosenActionSetter(true));
+ rejectButton.addActionListener(chosenActionSetter(false));
+
+ buttonPanel.add(allowButton);
+ buttonPanel.add(rejectButton);
+
+ buttonPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+
+ return buttonPanel;
+ }
+
+ // Set up 'Remember Option' checkbox & Proceed/Cancel buttons
+ private void setupButtonAndCheckBoxPanel() {
+ JPanel outerPanel = new JPanel(new BorderLayout());
+
+ outerPanel.add(createCheckBoxPanel(), BorderLayout.WEST);
+ outerPanel.add(createButtonPanel(), BorderLayout.EAST);
+
+ add(outerPanel);
+ }
+
+ /**
+ * Creates the actual GUI components, and adds it to this panel
+ */
+ private void addComponents() {
+ setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+
+ setupTopPanel();
+ setupInfoPanel();
+ setupQuestionsPanel();
+ setupButtonAndCheckBoxPanel();
+ }
+
+ // Sets action depending on allowApplet + checkbox state
+ private ActionListener chosenActionSetter(final boolean allowApplet) {
+ return new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ ExecuteUnsignedApplet action;
+
+ if (allowApplet) {
+ action = permanencyCheckBox.isSelected() ? ExecuteUnsignedApplet.ALWAYS : ExecuteUnsignedApplet.YES;
+ } else {
+ action = permanencyCheckBox.isSelected() ? ExecuteUnsignedApplet.NEVER : ExecuteUnsignedApplet.NO;
+ }
+
+ actionChoiceListener.actionChosen(action);
+ }
+ };
+ }
+} \ No newline at end of file
diff --git a/netx/net/sourceforge/jnlp/security/appletextendedsecurity/UnsignedAppletActionStorage.java b/netx/net/sourceforge/jnlp/security/appletextendedsecurity/UnsignedAppletActionStorage.java
index 8ce6500..511b5fa 100644
--- a/netx/net/sourceforge/jnlp/security/appletextendedsecurity/UnsignedAppletActionStorage.java
+++ b/netx/net/sourceforge/jnlp/security/appletextendedsecurity/UnsignedAppletActionStorage.java
@@ -37,7 +37,6 @@ package net.sourceforge.jnlp.security.appletextendedsecurity;
import java.util.List;
-
/**
* This is abstract access to white/blacklist created from some permanent storage.
*
@@ -53,9 +52,9 @@ public interface UnsignedAppletActionStorage {
/**
* This methods iterates through records in
- * DeploymentConfiguration.getAppletTrustSettingsPath(), and is mathing
- * regexes saved here against params. so parameters here are NOR tegexes,
- * but are matched against saved regexes
+ * DeploymentConfiguration.getAppletTrustSettingsPath(), and is matching
+ * regexes saved here against params. So parameters here are NOT regexes,
+ * but are matched against saved regexes.
*
* Null or empty values are dangerously ignored, user, be aware of it. eg:
* match only codeBase will be null someCodeBase null null match only
@@ -122,4 +121,14 @@ public interface UnsignedAppletActionStorage {
* @param item
*/
public void update(final UnsignedAppletActionEntry item);
+
+ /**
+ * Lock the storage, if necessary. If no ownership issues arise, can be a no-op.
+ */
+ public void lock();
+
+ /**
+ * Unlock the storage, if necessary. If no ownership issues arise, can be a no-op.
+ */
+ public void unlock();
}
diff --git a/netx/net/sourceforge/jnlp/security/appletextendedsecurity/UnsignedAppletTrustConfirmation.java b/netx/net/sourceforge/jnlp/security/appletextendedsecurity/UnsignedAppletTrustConfirmation.java
new file mode 100644
index 0000000..26bc1d4
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/security/appletextendedsecurity/UnsignedAppletTrustConfirmation.java
@@ -0,0 +1,209 @@
+/* Copyright (C) 2013 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 net.sourceforge.jnlp.security.appletextendedsecurity;
+
+import static net.sourceforge.jnlp.runtime.Translator.R;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import net.sourceforge.jnlp.LaunchException;
+import net.sourceforge.jnlp.PluginBridge;
+import net.sourceforge.jnlp.cache.ResourceTracker;
+import net.sourceforge.jnlp.security.SecurityDialogs;
+
+public class UnsignedAppletTrustConfirmation {
+ static private final boolean DEBUG = System.getenv().containsKey("ICEDTEAPLUGIN_DEBUG");
+
+ private static final AppletStartupSecuritySettings securitySettings = AppletStartupSecuritySettings.getInstance();
+
+ private static boolean unsignedConfirmationIsRequired() {
+ // If we are using the 'high' security setting or higher, we must confirm
+ // if the user wishes to run unsigned applets (not applicable to JNLP-launched apps)
+ return !(AppletSecurityLevel.ALLOW_UNSIGNED == securitySettings.getSecurityLevel());
+ }
+
+ private static boolean unsignedAppletsAreForbidden() {
+ // If we are using the 'very high' security setting or higher, we do not
+ // run unsigned applets
+ return AppletSecurityLevel.DENY_UNSIGNED == securitySettings.getSecurityLevel()
+ || AppletSecurityLevel.DENY_ALL == securitySettings.getSecurityLevel();
+ }
+
+ /**
+ * Gets the remembered decision, first checking the user policy for an ALWAYS/NEVER,
+ * and then the global policy.
+ *
+ * @param file the plugin file
+ * @return the remembered decision
+ */
+ public static ExecuteUnsignedApplet getStoredAction(PluginBridge file) {
+ UnsignedAppletActionStorage userActionStorage = securitySettings.getUnsignedAppletActionCustomStorage();
+ UnsignedAppletActionStorage globalActionStorage = securitySettings.getUnsignedAppletActionGlobalStorage();
+
+ UnsignedAppletActionEntry globalEntry = getMatchingItem(globalActionStorage, file);
+ UnsignedAppletActionEntry userEntry = getMatchingItem(userActionStorage, file);
+
+ ExecuteUnsignedApplet globalAction = globalEntry == null ? null : globalEntry.getUnsignedAppletAction();
+ ExecuteUnsignedApplet userAction = userEntry == null ? null : userEntry.getUnsignedAppletAction();
+
+ if (userAction == ExecuteUnsignedApplet.ALWAYS || userAction == ExecuteUnsignedApplet.NEVER) {
+ return userAction;
+ } else if (globalAction == ExecuteUnsignedApplet.ALWAYS || globalAction == ExecuteUnsignedApplet.NEVER) {
+ return globalAction;
+ } else {
+ return userAction;
+ }
+ }
+
+ private static UnsignedAppletActionEntry getMatchingItem(UnsignedAppletActionStorage actionStorage, PluginBridge file) {
+ return actionStorage.getMatchingItem(
+ normalizeUrlAndStripParams(file.getSourceLocation()).toString(),
+ normalizeUrlAndStripParams(file.getCodeBase()).toString(),
+ toRelativePaths(file.getArchiveJars(), file.getCodeBase().toString()));
+ }
+
+ static URL normalizeUrlAndStripParams(URL url) {
+ try {
+ String[] urlParts = url.toString().split("\\?");
+ URL strippedUrl = new URL(urlParts[0]);
+ return ResourceTracker.normalizeUrl(strippedUrl, false);
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (URISyntaxException e) {
+ e.printStackTrace();
+ }
+ return url;
+ }
+
+ /* Extract the archives as relative paths */
+ static List<String> toRelativePaths(List<String> paths, String rootPath) {
+ List<String> fileNames = new ArrayList<String>();
+ for (String path : paths) {
+ if (path.startsWith(rootPath)) {
+ fileNames.add(path.substring(rootPath.length()));
+ } else {
+ fileNames.add(path);
+ }
+ }
+ return fileNames;
+ }
+
+ private static void updateAppletAction(PluginBridge file, ExecuteUnsignedApplet behaviour) {
+
+ UnsignedAppletActionStorage userActionStorage = securitySettings.getUnsignedAppletActionCustomStorage();
+ userActionStorage.lock(); // We should ensure this operation is atomic
+ try {
+ UnsignedAppletActionEntry oldEntry = getMatchingItem(userActionStorage, file);
+
+ /* Update, if entry exists */
+ if (oldEntry != null) {
+ oldEntry.setUnsignedAppletAction(behaviour);
+ oldEntry.setTimeStamp(new Date());
+ userActionStorage.update(oldEntry);
+ return;
+ }
+
+ URL codebase = normalizeUrlAndStripParams(file.getCodeBase());
+ URL documentbase = normalizeUrlAndStripParams(file.getSourceLocation());
+
+ /* Else, create a new entry */
+ UrlRegEx codebaseRegex = new UrlRegEx("\\Q" + codebase + "\\E");
+ UrlRegEx documentbaseRegex = new UrlRegEx("\\Q" + documentbase + "\\E");
+
+ UnsignedAppletActionEntry entry = new UnsignedAppletActionEntry(
+ behaviour,
+ new Date(),
+ documentbaseRegex,
+ codebaseRegex,
+ toRelativePaths(file.getArchiveJars(), file.getCodeBase().toString()));
+ userActionStorage.add(entry);
+ } finally {
+ userActionStorage.unlock();
+ }
+ }
+ static private void debug(String logMessage) {
+ if (DEBUG) {
+ System.err.println(logMessage);
+ }
+
+ }
+
+ public static void checkUnsignedWithUserIfRequired(PluginBridge file) throws LaunchException {
+
+ if (unsignedAppletsAreForbidden()) {
+ debug("Not running unsigned applet at " + file.getCodeBase() +" because unsigned applets are disallowed by security policy.");
+ throw new LaunchException(file, null, R("LSFatal"), R("LCClient"), R("LUnsignedApplet"), R("LUnsignedAppletPolicyDenied"));
+ }
+
+ if (!unsignedConfirmationIsRequired()) {
+ debug("Running unsigned applet at " + file.getCodeBase() +" does not require confirmation according to security policy.");
+ return;
+ }
+
+ ExecuteUnsignedApplet storedAction = getStoredAction(file);
+ debug("Stored action for unsigned applet at " + file.getCodeBase() +" was " + storedAction);
+
+ boolean appletOK;
+
+ if (storedAction == ExecuteUnsignedApplet.ALWAYS) {
+ appletOK = true;
+ } else if (storedAction == ExecuteUnsignedApplet.NEVER) {
+ appletOK = false;
+ } else {
+ // No remembered decision, prompt the user
+ ExecuteUnsignedApplet decidedAction = SecurityDialogs.showUnsignedWarningDialog(file);
+
+ appletOK = (decidedAction == ExecuteUnsignedApplet.YES || decidedAction == ExecuteUnsignedApplet.ALWAYS);
+
+ if (decidedAction != null) {
+ updateAppletAction(file, decidedAction);
+ }
+
+ debug("Decided action for unsigned applet at " + file.getCodeBase() +" was " + decidedAction);
+ }
+
+ if (!appletOK) {
+ throw new LaunchException(file, null, R("LSFatal"), R("LCClient"), R("LUnsignedApplet"), R("LUnsignedAppletUserDenied"));
+ }
+
+ }
+} \ No newline at end of file
diff --git a/plugin/icedteanp/java/sun/applet/PluginAppletViewer.java b/plugin/icedteanp/java/sun/applet/PluginAppletViewer.java
index 8a4f078..9151d5e 100644
--- a/plugin/icedteanp/java/sun/applet/PluginAppletViewer.java
+++ b/plugin/icedteanp/java/sun/applet/PluginAppletViewer.java
@@ -62,6 +62,8 @@ exception statement from your version. */
package sun.applet;
+import static net.sourceforge.jnlp.runtime.Translator.R;
+
import java.applet.Applet;
import java.applet.AppletContext;
import java.applet.AudioClip;
@@ -103,9 +105,13 @@ import java.util.concurrent.locks.ReentrantLock;
import javax.swing.SwingUtilities;
+import net.sourceforge.jnlp.LaunchException;
import net.sourceforge.jnlp.NetxPanel;
import net.sourceforge.jnlp.PluginParameters;
import net.sourceforge.jnlp.runtime.JNLPClassLoader;
+import net.sourceforge.jnlp.security.appletextendedsecurity.AppletSecurityLevel;
+import net.sourceforge.jnlp.security.appletextendedsecurity.AppletStartupSecuritySettings;
+import net.sourceforge.jnlp.security.appletextendedsecurity.ExecuteUnsignedApplet;
import net.sourceforge.jnlp.splashscreen.SplashController;
import net.sourceforge.jnlp.splashscreen.SplashPanel;
import net.sourceforge.jnlp.splashscreen.SplashUtils;
@@ -406,7 +412,12 @@ public class PluginAppletViewer extends XEmbeddedFrame
requestFactory = rf;
}
- private static void handleInitializationMessage(int identifier, String message) throws IOException {
+ private static void handleInitializationMessage(int identifier, String message) throws IOException, LaunchException {
+
+ /* The user has specified via a global setting that applets should not be run.*/
+ if (AppletStartupSecuritySettings.getInstance().getSecurityLevel() == AppletSecurityLevel.DENY_ALL) {
+ throw new LaunchException(null, null, R("LSFatal"), R("LCClient"), R("LUnsignedApplet"), R("LUnsignedAppletPolicyDenied"));
+ }
// If there is a key for this status, it means it
// was either initialized before, or destroy has been
diff --git a/tests/netx/unit/net/sourceforge/jnlp/security/appletextendedsecurity/UnsignedAppletTrustConfirmationTest.java b/tests/netx/unit/net/sourceforge/jnlp/security/appletextendedsecurity/UnsignedAppletTrustConfirmationTest.java
new file mode 100644
index 0000000..be33f20
--- /dev/null
+++ b/tests/netx/unit/net/sourceforge/jnlp/security/appletextendedsecurity/UnsignedAppletTrustConfirmationTest.java
@@ -0,0 +1,59 @@
+package net.sourceforge.jnlp.security.appletextendedsecurity;
+
+import static org.junit.Assert.assertEquals;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+public class UnsignedAppletTrustConfirmationTest {
+
+ private List<String> toList(String ... parts) {
+ List<String> list = new ArrayList<String>();
+ for (String part : parts) {
+ list.add(part);
+ }
+ return list;
+ }
+
+ @Test
+ public void testToRelativePaths() throws Exception {
+ /* Absolute -> Relative */
+ assertEquals(toList("test.jar"),
+ UnsignedAppletTrustConfirmation.toRelativePaths(toList("http://example.com/test.jar"), "http://example.com/"));
+
+ /* Relative is unchanged */
+ assertEquals(toList("test.jar"),
+ UnsignedAppletTrustConfirmation.toRelativePaths(toList("test.jar"), "http://example.com/"));
+
+ /* Different root URL is unchanged */
+ assertEquals(toList("http://example2.com/test.jar"),
+ UnsignedAppletTrustConfirmation.toRelativePaths(toList("http://example2.com/test.jar"), "http://example.com/"));
+
+ /* Path with invalid URL characters is handled */
+ assertEquals(toList("test .jar"),
+ UnsignedAppletTrustConfirmation.toRelativePaths(toList("http://example.com/test .jar"), "http://example.com/"));
+ }
+
+ @Test
+ public void testNormalizeUrlAndStripParams() throws Exception {
+ /* Test that URL is normalized (encoded if not already encoded, leading whitespace trimmed, etc) */
+ assertEquals("http://example.com/%20test%20test",
+ UnsignedAppletTrustConfirmation.normalizeUrlAndStripParams(new URL("http://example.com/ test%20test ")).toString());
+ /* Test that a URL without '?' is left unchanged */
+ assertEquals("http://example.com/test",
+ UnsignedAppletTrustConfirmation.normalizeUrlAndStripParams(new URL("http://example.com/test")).toString());
+ /* Test that parts of a URL that come after '?' are stripped */
+ assertEquals("http://example.com/test",
+ UnsignedAppletTrustConfirmation.normalizeUrlAndStripParams(new URL("http://example.com/test?test=test")).toString());
+ /* Test that everything after the first '?' is stripped */
+ assertEquals("http://example.com/test",
+ UnsignedAppletTrustConfirmation.normalizeUrlAndStripParams(new URL("http://example.com/test?http://example.com/?test")).toString());
+
+ /* Test normalization + stripping */
+ assertEquals("http://example.com/%20test%20test",
+ UnsignedAppletTrustConfirmation.normalizeUrlAndStripParams(new URL("http://www.example.com/ test%20test ?test=test")).toString());
+ }
+} \ No newline at end of file