Ensure only safe redirects are followed and simplify connection handling
NetBeans did follow redirects even if a protocol change happened. In
this case the user is not aware, that a download potentially drops from
https to http. Instead protocol changes are only allowed, if the
originating URL is insecure (http).
Instead of trying to handle the SSL/TLS trust internally, the default
JDK mechanisms for handling SSL/TLS connections are now used and the
connection configuration code was centralized.
diff --git a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/InstallSupportImpl.java b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/InstallSupportImpl.java
index 5098c28..1644392 100644
--- a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/InstallSupportImpl.java
+++ b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/InstallSupportImpl.java
@@ -924,9 +924,10 @@
String label) throws MalformedURLException, IOException {
OpenConnectionListener listener = new OpenConnectionListener(source);
- final Task task = NetworkAccess.createNetworkAcessTask(source,
+ final Task task = NetworkAccess.createNetworkAccessTask(source,
AutoupdateSettings.getOpenConnectionTimeout(),
- listener);
+ listener,
+ false);
new Thread(new Runnable() {
@SuppressWarnings("SleepWhileInLoop")
@Override
diff --git a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/AutoupdateCatalogCache.java b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/AutoupdateCatalogCache.java
index ab94735..ba81ccd 100644
--- a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/AutoupdateCatalogCache.java
+++ b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/AutoupdateCatalogCache.java
@@ -216,7 +216,7 @@
DownloadListener nwl = new DownloadListener(sourceUrl, temp, allowZeroSize);
- NetworkAccess.Task task = NetworkAccess.createNetworkAcessTask (sourceUrl, AutoupdateSettings.getOpenConnectionTimeout (), nwl);
+ NetworkAccess.Task task = NetworkAccess.createNetworkAccessTask (sourceUrl, AutoupdateSettings.getOpenConnectionTimeout (), nwl, true);
task.waitFinished ();
nwl.notifyException ();
synchronized(getLock(cache)) {
diff --git a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/NetworkAccess.java b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/NetworkAccess.java
index c79154a..1560eda 100644
--- a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/NetworkAccess.java
+++ b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/NetworkAccess.java
@@ -24,10 +24,6 @@
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
-import java.security.KeyManagementException;
-import java.security.NoSuchAlgorithmException;
-import java.security.SecureRandom;
-import java.security.cert.X509Certificate;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
@@ -40,12 +36,6 @@
import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;
-import javax.net.ssl.HostnameVerifier;
-import javax.net.ssl.HttpsURLConnection;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLSession;
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.X509TrustManager;
import org.openide.util.Cancellable;
import org.openide.util.NbBundle;
import org.openide.util.RequestProcessor;
@@ -63,19 +53,20 @@
private NetworkAccess () {}
- public static Task createNetworkAcessTask (URL url, int timeout, NetworkListener networkAcesssListener) {
- return new Task (url, timeout, networkAcesssListener);
+ public static Task createNetworkAccessTask (URL url, int timeout, NetworkListener networkAccessListener, boolean disableInsecureRedirects) {
+ return new Task (url, timeout, networkAccessListener, disableInsecureRedirects);
}
public static class Task implements Cancellable {
+ private final ExecutorService es = Executors.newSingleThreadExecutor();
+ private final boolean disableInsecureRedirects;
private URL url;
private int timeout;
private NetworkListener listener;
- private final ExecutorService es = Executors.newSingleThreadExecutor ();
private Future<InputStream> connect = null;
private RequestProcessor.Task rpTask = null;
- private Task (URL url, int timeout, NetworkListener listener) {
+ private Task (URL url, int timeout, NetworkListener listener, boolean disableInsecureRedirects) {
if (url == null) {
throw new IllegalArgumentException ("URL cannot be null.");
}
@@ -85,6 +76,7 @@
this.url = url;
this.timeout = timeout;
this.listener = listener;
+ this.disableInsecureRedirects = disableInsecureRedirects;
postTask ();
}
@@ -146,11 +138,7 @@
@Override
public InputStream call () throws Exception {
URLConnection conn = url.openConnection ();
- conn.setConnectTimeout (timeout);
- conn.setReadTimeout(timeout);
- if(conn instanceof HttpsURLConnection){
- NetworkAccess.initSSL((HttpsURLConnection) conn);
- }
+ configureConnection(conn, timeout);
// handle redirection here
int redirCount = 0;
@@ -181,13 +169,52 @@
}
};
}
-
+
@Override
public boolean cancel () {
- return connect.cancel (true);
+ return connect.cancel(true);
}
-
+
+ private URLConnection checkRedirect(URLConnection conn, int timeout) throws IOException {
+ if (conn instanceof HttpURLConnection) {
+ conn.connect();
+ int code = ((HttpURLConnection) conn).getResponseCode();
+ boolean isInsecure = "http".equalsIgnoreCase(conn.getURL().getProtocol());
+ if (code == HttpURLConnection.HTTP_MOVED_TEMP
+ || code == HttpURLConnection.HTTP_MOVED_PERM) {
+ // in case of redirection, try to obtain new URL
+ String redirUrl = conn.getHeaderField("Location"); //NOI18N
+ if (null != redirUrl && !redirUrl.isEmpty()) {
+ //create connection to redirected url and substitute original connection
+ URL redirectedUrl = new URL(redirUrl);
+ if (disableInsecureRedirects && (!isInsecure) && (!redirectedUrl.getProtocol().equalsIgnoreCase(conn.getURL().getProtocol()))) {
+ throw new IOException(String.format(
+ "Redirect from secure URL '%s' to '%s' blocked.",
+ conn.getURL().toExternalForm(),
+ redirectedUrl.toExternalForm()
+ ));
+ }
+ URLConnection connRedir = redirectedUrl.openConnection();
+ connRedir.setRequestProperty("User-Agent", "NetBeans"); // NOI18N
+ connRedir.setConnectTimeout(timeout);
+ connRedir.setReadTimeout(timeout);
+ return connRedir;
+ }
+ }
+ }
+ return conn;
+ }
+
+ private static void configureConnection(URLConnection conn, int timeout) {
+ if (conn instanceof HttpURLConnection) {
+ ((HttpURLConnection) conn).setInstanceFollowRedirects(false);
+ }
+ conn.setRequestProperty("User-Agent", "NetBeans");
+ conn.setConnectTimeout(timeout);
+ conn.setReadTimeout(timeout);
+ }
}
+
private interface SizedConnection extends Callable<InputStream> {
public int getContentLength();
}
@@ -197,67 +224,4 @@
public void accessTimeOut ();
public void notifyException (Exception x);
}
-
- public static void initSSL(HttpURLConnection httpCon) throws IOException {
- if (httpCon instanceof HttpsURLConnection) {
- HttpsURLConnection https = (HttpsURLConnection) httpCon;
-
- try {
- TrustManager[] trustAllCerts = new TrustManager[]{
- new X509TrustManager() {
- @Override
- public X509Certificate[] getAcceptedIssuers() {
- return new X509Certificate[0];
- }
-
- @Override
- public void checkClientTrusted(X509Certificate[] certs, String authType) {
- }
-
- @Override
- public void checkServerTrusted(X509Certificate[] certs, String authType) {
- }
- }};
- SSLContext sslContext = SSLContext.getInstance("SSL"); // NOI18N
- sslContext.init(null, trustAllCerts, new SecureRandom());
- https.setHostnameVerifier(new HostnameVerifier() {
- @Override
- public boolean verify(String hostname, SSLSession session) {
- return true;
- }
- });
- https.setSSLSocketFactory(sslContext.getSocketFactory());
- } catch (KeyManagementException ex) {
- throw new IOException(ex);
- } catch (NoSuchAlgorithmException ex) {
- throw new IOException(ex);
- }
- }
- }
-
- private static URLConnection checkRedirect(URLConnection conn, int timeout) throws IOException {
- if (conn instanceof HttpURLConnection) {
- conn.connect();
- int code = ((HttpURLConnection) conn).getResponseCode();
- if (code == HttpURLConnection.HTTP_MOVED_TEMP
- || code == HttpURLConnection.HTTP_MOVED_PERM) {
- // in case of redirection, try to obtain new URL
- String redirUrl = conn.getHeaderField("Location"); //NOI18N
- if (null != redirUrl && !redirUrl.isEmpty()) {
- //create connection to redirected url and substitute original conn
- URL redirectedUrl = new URL(redirUrl);
- URLConnection connRedir = redirectedUrl.openConnection();
- // XXX is this neede
- connRedir.setRequestProperty("User-Agent", "NetBeans"); // NOI18N
- connRedir.setConnectTimeout(timeout);
- connRedir.setReadTimeout(timeout);
- if (connRedir instanceof HttpsURLConnection) {
- NetworkAccess.initSSL((HttpsURLConnection) connRedir);
- }
- return connRedir;
- }
- }
- }
- return conn;
- }
}