001: /*
002: * $Id: HttpsClient.java,v 1.5 2005/11/30 11:27:27 ss150821 Exp $
003: * $Source: /m/portal/ps/srap/src/com/sun/portal/rproxy/https/HttpsClient.java,v $
004: * $Log: HttpsClient.java,v $
005: * Revision 1.5 2005/11/30 11:27:27 ss150821
006: * 6356996 - Srap Code base needs to save files in the unix file format and not windows
007: *
008: * Revision 1.4 2005/02/23 09:02:01 ss150821
009: * RFE 6223490 - SRA Should use JDK based logging
010: *
011: * Revision 1.3 2005/02/23 08:59:22 ss150821
012: * RFE 6223490 - SRA Should use JDK based logging
013: *
014: * Revision 1.2 2004/07/27 12:58:28 vt126379
015: * RFE#5075809, CRT#99
016: *
017: * Revision 1.1 2002/06/14 09:53:56 rt130506
018: * SRAP rebranding
019: *
020: * Revision 1.2 2002/06/11 16:02:08 bv131302
021: * new branded
022: *
023: * Revision 1.1 2002/05/28 09:38:19 mm132998
024: * Bug id - 4692062 , CRT - 1215 , Desc - Support for iDSAME in https mode.
025: *
026: *
027: */
028: /*
029: * @(#)HttpsClient.java 1.24 99/05/27
030: *
031: * Copyright (c) 1995-1997 Sun Microsystems, Inc. All Rights Reserved.
032: *
033: * This software is the confidential and proprietary information of Sun
034: * Microsystems, Inc. ("Confidential Information"). You shall not
035: * disclose such Confidential Information and shall use it only in
036: * accordance with the terms of the license agreement you entered into
037: * with Sun.
038: *
039: * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
040: * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
041: * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
042: * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
043: * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
044: * THIS SOFTWARE OR ITS DERIVATIVES.
045: *
046: * CopyrightVersion 1.0
047: */
048:
049: package com.sun.portal.rproxy.https;
050:
051: import java.io.IOException;
052: import java.net.InetAddress;
053: import java.net.ServerSocket;
054: import java.net.Socket;
055: import java.net.URL;
056: import java.net.UnknownHostException;
057: import java.util.logging.Level;
058: import java.util.logging.Logger;
059:
060: import org.mozilla.jss.crypto.X509Certificate;
061: import org.mozilla.jss.ssl.SSLSocket;
062:
063: import sun.misc.RegexpPool;
064: import sun.net.www.http.HttpClient;
065:
066: import com.sun.portal.log.common.PortalLogger;
067:
068: /**
069: * This class provides HTTPS client URL support, building on the standard
070: * "sun.net.www" HTTP protocol handler. HTTPS is the same protocol as HTTP, but
071: * differs in the transport layer which it uses:
072: * <UL>
073: *
074: * <LI>There's a <em>Secure Sockets Layer</em> between TCP and the HTTP
075: * protocol code.
076: *
077: * <LI>It uses a different default TCP port.
078: *
079: * <LI>It doesn't use application level proxies, which can see and manipulate
080: * HTTP user level data, compromising privacy. It uses low level tunneling
081: * instead, which hides HTTP protocol and data from all third parties. (Traffic
082: * analysis is still possible).
083: *
084: * <LI>It does basic server authentication, to protect against "URL spoofing"
085: * attacks. This involves deciding whether the X.509 certificate chain
086: * identifying the server is trusted, and verifying that the name of the server
087: * is found in the certificate. (The application may enable an anonymous SSL
088: * cipher suite, and such checks are not done for anonymous ciphers.)
089: *
090: * <LI>It exposes key SSL session attributes, specifically the cipher suite in
091: * use and the server's X509 certificates, to application software which knows
092: * about this protocol handler.
093: *
094: * </UL>
095: *
096: * <P>
097: * System properties used include:
098: * <UL>
099: *
100: * <LI><em>https.proxyHost</em> ... the host supporting SSL tunneling using
101: * the conventional CONNECT syntax
102: *
103: * <LI><em>https.proxyPort</em> ... port to use on proxyHost
104: *
105: * <LI><em>https.cipherSuites</em> ... comma separated list of SSL cipher
106: * suite names to enable.
107: *
108: * <LI><em>http.nonProxyHosts</em> ...
109: *
110: * </UL>
111: *
112: * @version 1.24
113: * @author David Brownell
114: * @author Bill Foote
115: */
116:
117: // final for export control reasons (access to APIs); remove with care
118: public final class HttpsClient extends HttpClient
119: // implements HandshakeCompletedListener
120: {
121: // STATIC STATE and ACCESSORS THERETO
122:
123: // Host and port we use for proxying, or null if none are declared.
124: private static String secureTunnelHost = null;
125:
126: private static int secureTunnelPort = 80;
127:
128: // regexp pool of hosts for which we should connect directly, not Tunnel
129: // these are intialized from a property at class init time
130: private static RegexpPool dontProxy = null;
131:
132: // HTTPS uses a different default port number than HTTP.
133: private static final int httpsPortNumber = 443;
134:
135: /** Returns the default HTTPS port (443) */
136: protected int getDefaultPort() {
137: return httpsPortNumber;
138: }
139:
140: public static int jssProxyPort = -1;
141:
142: // private static Logger logger =
143: // Logger.getLogger("com.sun.portal.sra.rproxy");
144: private static Logger logger = PortalLogger
145: .getLogger(HttpsClient.class);
146:
147: /* Set properties on startup */
148: static {
149: resetSecureProperties();
150: }
151:
152: // HttpClient.proxyDisabled will always be false, because we don't
153: // use an application-level HTTP proxy. We might tunnel through
154: // our http proxy, though.
155:
156: // INSTANCE DATA
157:
158: // tunnel host/port for this instance. instTunnelHost will be null
159: // if tunneling is turned off for this client for any reason.
160: private String instTunnelHost;
161:
162: private int instTunnelPort;
163:
164: private SSLSocket sslSocket = null;
165:
166: // STATIC FUNCTIONS
167:
168: /**
169: * Re-initialize the dontProxy list, and other properties we depend on.
170: */
171: public static synchronized void resetSecureProperties() {
172: // REMIND: Don't change the name of resetSecureProperties -
173: // sun.hotjava.applets.SecureHttpProxyApplet depends on it.
174:
175: String ourHost = getProperty("https.proxyHost");
176: // logger.info("HttpsClient: proxyHost = " + ourHost);
177: Object[] params0 = { ourHost };
178: logger.log(Level.INFO, "PSSRRPROXY_CSPRH003", params0);
179: if (ourHost == null || "".equals(ourHost)) {
180: secureTunnelHost = null;
181: secureTunnelPort = 80;
182: } else {
183: secureTunnelHost = ourHost;
184: secureTunnelPort = Integer
185: .getInteger("https.proxyPort", 80).intValue();
186: }
187:
188: // REMIND: The following is cut-and-paste from HttpClient.
189: // The dontProxy stuff needs to be factored out into a protected
190: // method that we can use, but we're after code freeze right now
191: // (this has to work with both 1.1.1 and 1.1).
192:
193: dontProxy = new RegexpPool();
194: String rawList = getProperty("http.nonProxyHosts");
195: // logger.info("HttpsClient: nonProxyHosts = " + rawList);
196: Object[] params1 = { rawList };
197: logger.log(Level.INFO, "PSSRRPROXY_CSPRH004", params1);
198:
199: if (rawList != null) {
200: java.util.StringTokenizer st = new java.util.StringTokenizer(
201: rawList, "|", false);
202: try {
203: while (st.hasMoreTokens()) {
204: dontProxy.add(st.nextToken().toLowerCase(),
205: new Boolean(true));
206: }
207: } catch (Exception e) {
208: e.printStackTrace();
209: }
210: }
211:
212: ServerSocket ss = null;
213:
214: try {
215: ss = new ServerSocket(0, 50);
216: } catch (IOException e) {
217: ss = null;
218: }
219:
220: if (ss != null) {
221: jssProxyPort = ss.getLocalPort();
222: JSSProxyRunnable jssProxy = new JSSProxyRunnable(ss);
223: Thread t = new Thread(jssProxy);
224: t.start();
225: }
226:
227: }
228:
229: // CONSTRUCTOR, FACTORY
230:
231: /**
232: * Create an HTTPS client URL. Traffic will be tunneled through any
233: * intermediate nodes rather than proxied, so that confidentiality of data
234: * exchanged can be preserved. However, note that all the anonymous SSL
235: * flavors are subject to "person-in-the-middle" attacks against
236: * confidentiality. If you enable use of those flavors, you may be giving up
237: * the protection you get through SSL tunneling.
238: *
239: * @param URL
240: * https URL with which a connection must be established
241: */
242: public HttpsClient(URL url) throws IOException {
243: // HttpClient-level proxying is always disabled,
244: // because we override doConnect to do tunneling instead.
245: super (url, true);
246: // logger.info("HttpsClient: HttpsClient " + url);
247: Object[] params2 = { url };
248: logger.log(Level.INFO, "PSSRRPROXY_CSPRH005", params2);
249: }
250:
251: // This code largely ripped off from HttpClient.New, and
252: // it uses the same keepalive cache.
253:
254: /** See HttpClient for the model for this method. */
255: public static HttpClient New(URL url) throws IOException {
256: // logger.info("HttpsClient: New " + url);
257: Object[] params3 = { url };
258: logger.log(Level.INFO, "PSSRRPROXY_CSPRH006", params3);
259: /* see if one's already around */
260: HttpsClient ret = (HttpsClient) kac.get(url);
261:
262: if (ret == null) {
263: ret = new HttpsClient(url);
264: } else {
265: ret.url = url;
266: }
267: return ret;
268: }
269:
270: // METHODS
271:
272: /**
273: * Returns true if host is on the "don't proxy" list.
274: */
275: // REMIND As mentioned above, this should be moved to HttpClient
276: protected boolean isNonProxyHost() {
277: if (dontProxy.match(url.getHost().toLowerCase()) != null) {
278: return true;
279: }
280: try {
281: InetAddress addr = InetAddress.getByName(url.getHost());
282: String host = addr.getHostAddress();
283: if (dontProxy.match(host) != null) {
284: return true;
285: }
286: } catch (UnknownHostException ignored) {
287: }
288: return false;
289: }
290:
291: /**
292: * Overrides HTTP protocol handler method so that we return an SSL socket,
293: * not a TCP socket. This establishes a secure tunnel if appropriate.
294: *
295: * @param host
296: * the host to connect to
297: * @param port
298: * the port on that host.
299: * @exception IOException
300: * on errors including a host doesn't authenicate corectly.
301: * @exception UnknownHostException
302: * if "host" is unknown
303: */
304: protected Socket doConnect(String host, int port)
305: throws IOException, UnknownHostException {
306: // logger.info("HttpsClient: doConnect(" + host + ", " + port + ")");
307: Object[] params4 = { host, ", ", port + "", ")" };
308: logger.log(Level.INFO, "PSSRRPROXY_CSPRH007", params4);
309: instTunnelHost = secureTunnelHost;
310: instTunnelPort = (secureTunnelPort < 0) ? super
311: .getDefaultPort() : secureTunnelPort;
312:
313: if (instTunnelHost == null || isNonProxyHost()) {
314: sslSocket = new SSLSocket(InetAddress.getByName(host),
315: port, null, 0, ApprovalCallback.getInstance(), null);
316: } else {
317: // logger.info("HttpsClient: doConnect through proxy " +
318: // secureTunnelHost + ":" + secureTunnelPort);
319: Object[] params5 = { secureTunnelHost, ":",
320: new Integer(secureTunnelPort) };
321: logger.log(Level.INFO, "PSSRRPROXY_CSPRH008", params5);
322: sslSocket = new SSLSocket(InetAddress
323: .getByName("localhost"), jssProxyPort, null, 0,
324: new ApprovalCallback(host), null);
325:
326: Integer localport = new Integer(sslSocket.getLocalPort());
327: String info = secureTunnelHost + " " + secureTunnelPort
328: + " " + host + " " + port;
329:
330: JSSProxyRunnable.connectHashMap.put(localport, info);
331:
332: }
333:
334: return sslSocket;
335: }
336:
337: /**
338: * Returns the cipher suite in use on this connection.
339: */
340: public String getCipherSuite() {
341: /*
342: * JSS3.1.1 change
343: */
344: // return sslSocket.getStatus().getCipher();
345: String cipher = null;
346: try {
347: cipher = sslSocket.getStatus().getCipher();
348: } catch (java.net.SocketException se) {
349: // logger.info("HttpsClient: getCipher failed");
350: logger.info("PSSRRPROXY_CSPRH009");
351: }
352: return cipher;
353: // End of code change for JSS3.1.1
354:
355: }
356:
357: /**
358: * Returns the X.509 certificate chain with which the server authenticated
359: * itself, or null if the server did not authenticate.
360: */
361: public X509Certificate[] getServerCertificateChain() {
362: return null;
363: }
364:
365: private static String getProperty(String prop) {
366: SecurityException ex = null;
367:
368: try {
369: return System.getProperty(prop);
370: } catch (SecurityException e) {
371: ex = e;
372: }
373:
374: /*
375: * If we hit an exception then we're probably running with an access
376: * controller. Turn on privileges and try again.
377: */
378: try {
379: return (String) java.security.AccessController
380: .doPrivileged(new sun.security.action.GetPropertyAction(
381: "prop"));
382: } catch (LinkageError e) {
383: // System.err.println("getProperty caught " + e);
384: }
385:
386: throw ex;
387: }
388: }
|