View Javadoc
1   /*
2    * This file is part of dependency-check-core.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   *
16   * Copyright (c) 2014 Jeremy Long. All Rights Reserved.
17   */
18  package org.owasp.dependencycheck.utils;
19  
20  import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
21  import org.apache.commons.lang3.StringUtils;
22  
23  import java.io.IOException;
24  import java.net.Authenticator;
25  import java.net.HttpURLConnection;
26  import java.net.InetSocketAddress;
27  import java.net.PasswordAuthentication;
28  import java.net.Proxy;
29  import java.net.SocketAddress;
30  import java.net.URL;
31  import java.security.KeyManagementException;
32  import java.security.NoSuchAlgorithmException;
33  import javax.net.ssl.HttpsURLConnection;
34  import org.slf4j.Logger;
35  import org.slf4j.LoggerFactory;
36  
37  /**
38   * A URLConnection Factory to create new connections. This encapsulates several
39   * configuration checks to ensure that the connection uses the correct proxy
40   * settings.
41   *
42   * @author Jeremy Long
43   */
44  public final class URLConnectionFactory {
45  
46      /**
47       * The logger.
48       */
49      private static final Logger LOGGER = LoggerFactory.getLogger(URLConnectionFactory.class);
50  
51      /**
52       * Private constructor for this factory.
53       */
54      private URLConnectionFactory() {
55      }
56  
57      /**
58       * Utility method to create an HttpURLConnection. If the application is
59       * configured to use a proxy this method will retrieve the proxy settings
60       * and use them when setting up the connection.
61       *
62       * @param url the url to connect to
63       * @return an HttpURLConnection
64       * @throws URLConnectionFailureException thrown if there is an exception
65       */
66      @SuppressFBWarnings(value = "RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE", justification = "Just being extra safe")
67      public static HttpURLConnection createHttpURLConnection(URL url) throws URLConnectionFailureException {
68          HttpURLConnection conn = null;
69          final String proxyUrl = Settings.getString(Settings.KEYS.PROXY_SERVER);
70  
71          try {
72              if (proxyUrl != null && !matchNonProxy(url)) {
73                  final int proxyPort = Settings.getInt(Settings.KEYS.PROXY_PORT);
74                  final SocketAddress address = new InetSocketAddress(proxyUrl, proxyPort);
75  
76                  final String username = Settings.getString(Settings.KEYS.PROXY_USERNAME);
77                  final String password = Settings.getString(Settings.KEYS.PROXY_PASSWORD);
78  
79                  if (username != null && password != null) {
80                      final Authenticator auth = new Authenticator() {
81                          @Override
82                          public PasswordAuthentication getPasswordAuthentication() {
83                              if (getRequestorType().equals(Authenticator.RequestorType.PROXY)) {
84                                  return new PasswordAuthentication(username, password.toCharArray());
85                              }
86                              return super.getPasswordAuthentication();
87                          }
88                      };
89                      Authenticator.setDefault(auth);
90                  }
91  
92                  final Proxy proxy = new Proxy(Proxy.Type.HTTP, address);
93                  conn = (HttpURLConnection) url.openConnection(proxy);
94              } else {
95                  conn = (HttpURLConnection) url.openConnection();
96              }
97              final int timeout = Settings.getInt(Settings.KEYS.CONNECTION_TIMEOUT, 10000);
98              conn.setConnectTimeout(timeout);
99              conn.setInstanceFollowRedirects(true);
100         } catch (IOException ex) {
101             if (conn != null) {
102                 try {
103                     conn.disconnect();
104                 } finally {
105                     conn = null;
106                 }
107             }
108             throw new URLConnectionFailureException("Error getting connection.", ex);
109         }
110         configureTLS(url, conn);
111         return conn;
112     }
113 
114     /**
115      * Check if hostname matches nonProxy settings
116      *
117      * @param url the url to connect to
118      * @return matching result. true: match nonProxy
119      */
120     private static boolean matchNonProxy(final URL url) {
121         final String host = url.getHost();
122 
123         // code partially from org.apache.maven.plugins.site.AbstractDeployMojo#getProxyInfo
124         final String nonProxyHosts = Settings.getString(Settings.KEYS.PROXY_NON_PROXY_HOSTS);
125         if (null != nonProxyHosts) {
126             final String[] nonProxies = nonProxyHosts.split("(,)|(;)|(\\|)");
127             for (final String nonProxyHost : nonProxies) {
128                 //if ( StringUtils.contains( nonProxyHost, "*" ) )
129                 if (null != nonProxyHost && nonProxyHost.contains("*")) {
130                     // Handle wildcard at the end, beginning or middle of the nonProxyHost
131                     final int pos = nonProxyHost.indexOf('*');
132                     final String nonProxyHostPrefix = nonProxyHost.substring(0, pos);
133                     final String nonProxyHostSuffix = nonProxyHost.substring(pos + 1);
134                     // prefix*
135                     if (!StringUtils.isEmpty(nonProxyHostPrefix) && host.startsWith(nonProxyHostPrefix) && StringUtils.isEmpty(nonProxyHostSuffix)) {
136                         return true;
137                     }
138                     // *suffix
139                     if (StringUtils.isEmpty(nonProxyHostPrefix) && !StringUtils.isEmpty(nonProxyHostSuffix) && host.endsWith(nonProxyHostSuffix)) {
140                         return true;
141                     }
142                     // prefix*suffix
143                     if (!StringUtils.isEmpty(nonProxyHostPrefix) && host.startsWith(nonProxyHostPrefix) && !StringUtils.isEmpty(nonProxyHostSuffix)
144                             && host.endsWith(nonProxyHostSuffix)) {
145                         return true;
146                     }
147                 } else if (host.equals(nonProxyHost)) {
148                     return true;
149                 }
150             }
151         }
152         return false;
153     }
154 
155     /**
156      * Utility method to create an HttpURLConnection. The use of a proxy here is
157      * optional as there may be cases where a proxy is configured but we don't
158      * want to use it (for example, if there's an internal repository
159      * configured)
160      *
161      * @param url the URL to connect to
162      * @param proxy whether to use the proxy (if configured)
163      * @return a newly constructed HttpURLConnection
164      * @throws URLConnectionFailureException thrown if there is an exception
165      */
166     public static HttpURLConnection createHttpURLConnection(URL url, boolean proxy) throws URLConnectionFailureException {
167         if (proxy) {
168             return createHttpURLConnection(url);
169         }
170         HttpURLConnection conn = null;
171         try {
172             conn = (HttpURLConnection) url.openConnection();
173             final int timeout = Settings.getInt(Settings.KEYS.CONNECTION_TIMEOUT, 10000);
174             conn.setConnectTimeout(timeout);
175             conn.setInstanceFollowRedirects(true);
176         } catch (IOException ioe) {
177             throw new URLConnectionFailureException("Error getting connection.", ioe);
178         }
179         configureTLS(url, conn);
180         return conn;
181     }
182 
183     /**
184      * If the protocol is HTTPS, this will configure the cipher suites so that
185      * connections can be made to the NVD, and others, using older versions of
186      * Java.
187      *
188      * @param url the URL
189      * @param conn the connection
190      */
191     private static void configureTLS(URL url, HttpURLConnection conn) {
192         if ("https".equals(url.getProtocol())) {
193             try {
194                 final HttpsURLConnection secCon = (HttpsURLConnection) conn;
195                 final SSLSocketFactoryEx factory = new SSLSocketFactoryEx();
196                 secCon.setSSLSocketFactory(factory);
197             } catch (NoSuchAlgorithmException ex) {
198                 LOGGER.debug("Unsupported algorithm in SSLSocketFactoryEx", ex);
199             } catch (KeyManagementException ex) {
200                 LOGGER.debug("Key mnagement eception in SSLSocketFactoryEx", ex);
201             }
202         }
203     }
204 }