package com.intuit.tank.proxy; /* * #%L * proxy-extension * %% * Copyright (C) 2011 - 2015 Intuit Inc. * %% * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * #L% */ /* * This file is part of the OWASP Proxy, a free intercepting proxy library. * Copyright (C) 2008-2010 Rogan Dawes <rogan@dawes.za.net> * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.ProxySelector; import java.net.SocketAddress; import java.net.URI; import java.security.GeneralSecurityException; import java.util.Arrays; import java.util.List; import java.util.concurrent.Executors; import java.util.logging.ConsoleHandler; import java.util.logging.Handler; import java.util.logging.Logger; import javax.security.auth.x500.X500Principal; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.commons.cli.PosixParser; import org.owasp.proxy.daemon.LoopAvoidingTargetedConnectionHandler; import org.owasp.proxy.daemon.Proxy; import org.owasp.proxy.daemon.ServerGroup; import org.owasp.proxy.daemon.TargetedConnectionHandler; import org.owasp.proxy.http.MutableRequestHeader; import org.owasp.proxy.http.MutableResponseHeader; import org.owasp.proxy.http.RequestHeader; import org.owasp.proxy.http.client.HttpClient; import org.owasp.proxy.http.server.BufferedMessageInterceptor; import org.owasp.proxy.http.server.DefaultHttpRequestHandler; import org.owasp.proxy.http.server.HttpProxyConnectionHandler; import org.owasp.proxy.http.server.HttpRequestHandler; import org.owasp.proxy.http.server.LoggingHttpRequestHandler; import org.owasp.proxy.socks.SocksConnectionHandler; import org.owasp.proxy.ssl.AutoGeneratingContextSelector; import org.owasp.proxy.ssl.SSLConnectionHandler; import org.owasp.proxy.ssl.SSLContextSelector; import org.owasp.proxy.util.TextFormatter; import com.intuit.tank.conversation.Transaction; import com.intuit.tank.entity.Application; import com.intuit.tank.handler.BufferingHttpRequestHandler; import com.intuit.tank.proxy.config.CommonsProxyConfiguration; import com.intuit.tank.proxy.config.ProxyConfiguration; import com.intuit.tank.proxy.table.TransactionRecordedListener; import com.sun.net.httpserver.Headers; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; public class Main implements TransactionRecordedListener { private static Logger logger = Logger.getLogger("org.owasp.proxy"); private Proxy p; private Application application; private int port; private int apiPort; private String dumpFileName; static { System.setProperty("https.protocols", "TLSv1.1"); } /** * */ public Main(ProxyConfiguration config, int apiPort) { this.apiPort = apiPort; this.application = new Application(config); port = config.getPort(); dumpFileName = config.getOutputFile(); try { startHttpServer(); } catch (IOException e) { e.printStackTrace(); quit(); } } public void start() { try { application.startSession(this); logger.setUseParentHandlers(false); Handler ch = new ConsoleHandler(); ch.setFormatter(new TextFormatter()); logger.addHandler(ch); InetSocketAddress listen; try { listen = new InetSocketAddress("localhost", port); } catch (NumberFormatException nfe) { usage(); return; } String proxy = "DIRECT"; final ProxySelector ps = getProxySelector(proxy); DefaultHttpRequestHandler drh = new DefaultHttpRequestHandler() { @Override protected HttpClient createClient() { HttpClient client = super.createClient(); client.setProxySelector(ps); client.setSoTimeout(20000); return client; } }; ServerGroup sg = new ServerGroup(); sg.addServer(listen); drh.setServerGroup(sg); HttpRequestHandler rh = drh; rh = new LoggingHttpRequestHandler(rh); BufferedMessageInterceptor bmi = new BufferedMessageInterceptor() { @Override public Action directResponse(RequestHeader request, MutableResponseHeader response) { // System.err.println(new String(request.getHeader()) // + "+++++++++++\n" + new String(response.getHeader()) // + "==========="); return Action.BUFFER; } @Override public Action directRequest(MutableRequestHeader request) { // System.err.println(new String(request.getHeader()) // + "==========="); return Action.BUFFER; } }; rh = new BufferingHttpRequestHandler(rh, bmi, 1024 * 1024, application); HttpProxyConnectionHandler hpch = new HttpProxyConnectionHandler(rh); SSLContextSelector cp = getSSLContextSelector(); TargetedConnectionHandler tch = new SSLConnectionHandler(cp, true, hpch); tch = new LoopAvoidingTargetedConnectionHandler(sg, tch); hpch.setConnectHandler(tch); TargetedConnectionHandler socks = new SocksConnectionHandler(tch, true); p = new Proxy(listen, socks, null); p.setSocketTimeout(90000); p.start(); } catch (Exception e) { throw new RuntimeException(e); } } private void stop() { if (p != null) { try { p.stop(); application.endSession(); System.out.println("Terminated"); p = null; } catch (Exception e) { throw new RuntimeException(e); } } } public void quit() { try { stop(); } catch (Exception e) { e.printStackTrace(); } System.exit(0); } @SuppressWarnings("restriction") public void startHttpServer() throws IOException { InetSocketAddress addr = new InetSocketAddress(apiPort); HttpServer server = HttpServer.create(addr, 0); HttpHandler handler = new HttpHandler() { public void handle(HttpExchange exchange) throws IOException { String requestMethod = exchange.getRequestMethod(); URI requestURI = exchange.getRequestURI(); String msg = "unknown path"; if (requestMethod.equalsIgnoreCase("GET")) { // Thread t = null; if (requestURI.getPath().startsWith("/start")) { msg = "starting proxy on port " + port + " outputting to " + dumpFileName; // t = new Thread(new Runnable() { // @Override // public void run() { start(); // } // }); } else if (requestURI.getPath().startsWith("/stop")) { msg = "stopping proxy and finalizing output in file " + dumpFileName; // t = new Thread(new Runnable() { // @Override // public void run() { stop(); // } // }); } else if (requestURI.getPath().startsWith("/quit")) { msg = "quitting proxy and finalizing output in file " + dumpFileName; // t = new Thread(new Runnable() { // @Override // public void run() { quit(); // } // }); } byte[] bytes = msg.getBytes(); Headers responseHeaders = exchange.getResponseHeaders(); responseHeaders.set("Content-Type", "text/plain"); exchange.sendResponseHeaders(200, bytes.length); OutputStream responseBody = exchange.getResponseBody(); responseBody.write(bytes); responseBody.close(); // if (t != null) { // t.start(); // } } } }; server.createContext("/", handler); server.setExecutor(Executors.newCachedThreadPool()); server.start(); System.out.println("Server is listening on port " + apiPort); } /** * Prints the usage of the program. */ private static void usage() { System.err .println("Usage: java -jar proxy.jar port [\"proxy instruction\"] [ <JDBC Driver> <JDBC URL> <username> <password> ]"); System.err.println("Where \'proxy instruction\' might look like:"); System.err .println("'DIRECT' or 'PROXY server:port' or 'SOCKS server:port'"); System.err.println("and the JDBC connection details might look like:"); System.err .println("org.h2.Driver jdbc:h2:mem:webscarab3;DB_CLOSE_DELAY=-1 sa \"\""); } /** * Returns a proxy selector * * @param proxy * @return */ static ProxySelector getProxySelector(String proxy) { final java.net.Proxy upstream; if ("DIRECT".equals(proxy)) { upstream = java.net.Proxy.NO_PROXY; } else { java.net.Proxy.Type type = null; if (proxy.startsWith("PROXY ")) { type = java.net.Proxy.Type.HTTP; } else if (proxy.startsWith("SOCKS ")) { type = java.net.Proxy.Type.SOCKS; } else throw new IllegalArgumentException("Unknown Proxy type: " + proxy); proxy = proxy.substring(6); // "SOCKS " or "PROXY " int c = proxy.indexOf(':'); if (c == -1) throw new IllegalArgumentException("Illegal proxy address: " + proxy); InetSocketAddress addr = new InetSocketAddress(proxy .substring(0, c), Integer.parseInt(proxy.substring(c + 1))); upstream = new java.net.Proxy(type, addr); } ProxySelector ps = new ProxySelector() { @Override public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { logger.info("Proxy connection to " + uri + " via " + sa + " failed! " + ioe.getLocalizedMessage()); } @Override public List<java.net.Proxy> select(URI uri) { return Arrays.asList(upstream); } }; return ps; } static SSLContextSelector getSSLContextSelector() throws GeneralSecurityException, IOException { File ks = new File("auto_generated_ca.p12"); String type = "PKCS12"; char[] password = "password".toCharArray(); String alias = "CA"; if (ks.exists()) { try { return new AutoGeneratingContextSelector(ks, type, password, password, alias); } catch (GeneralSecurityException e) { System.err.println("Error loading CA keys from keystore: " + e.getLocalizedMessage()); } catch (IOException e) { System.err.println("Error loading CA keys from keystore: " + e.getLocalizedMessage()); } } System.err.println("Generating a new CA"); X500Principal ca = new X500Principal("cn=OWASP Custom CA for Tank,ou=Tank Custom CA,o=Tank,l=Tank,st=Tank,c=Tank"); AutoGeneratingContextSelector ssl = new AutoGeneratingContextSelector( ca); try { ssl.save(ks, type, password, password, alias); } catch (GeneralSecurityException e) { System.err.println("Error saving CA keys to keystore: " + e.getLocalizedMessage()); } catch (IOException e) { System.err.println("Error saving CA keys to keystore: " + e.getLocalizedMessage()); } FileWriter pem = null; try { pem = new FileWriter("auto_generated_ca.pem"); pem.write(ssl.getCACert()); } catch (IOException e) { System.err.println("Error writing CA cert : " + e.getLocalizedMessage()); } finally { if (pem != null) pem.close(); } return ssl; } public static void main(String[] args) throws Exception { Options options = new Options(); options.addOption(new Option("config", true, "The name of the recording configuration file.")); options.addOption(new Option("apiPort", true, "The port on which the api server should listen to.")); options.addOption(new Option("help", false, "Print usage.")); String configFile = null; int apiPort = 8008; CommandLineParser clp = new PosixParser(); try { CommandLine cl = clp.parse(options, args); if (cl.hasOption("help")) { HelpFormatter hf = new HelpFormatter(); hf.printHelp("CLITest <arguments>", options); System.exit(0); } if (cl.hasOption("config")) { configFile = cl.getOptionValue("config"); } else { logger.info("Using default Configuration"); } if (cl.hasOption("apiPort")) { apiPort = Integer.parseInt(cl.getOptionValue("apiPort")); } else { logger.info("Using " + apiPort + " as the default apiPort."); } } catch (ParseException e) { HelpFormatter hf = new HelpFormatter(); hf.printHelp("CLITest <arguments>", options); System.exit(0); } ProxyConfiguration config = new CommonsProxyConfiguration(configFile); new Main(config, apiPort); } /** * @{inheritDoc */ public void transactionProcessed(Transaction t, boolean filtered) { // don't know if we need to do something here } }