Return-Path: Delivered-To: apmail-jakarta-tomcat-dev-archive@apache.org Received: (qmail 20996 invoked from network); 5 Nov 2002 21:28:44 -0000 Received: from unknown (HELO nagoya.betaversion.org) (192.18.49.131) by daedalus.apache.org with SMTP; 5 Nov 2002 21:28:44 -0000 Received: (qmail 11798 invoked by uid 97); 5 Nov 2002 21:29:32 -0000 Delivered-To: qmlist-jakarta-archive-tomcat-dev@jakarta.apache.org Received: (qmail 11782 invoked by uid 97); 5 Nov 2002 21:29:32 -0000 Mailing-List: contact tomcat-dev-help@jakarta.apache.org; run by ezmlm Precedence: bulk List-Unsubscribe: List-Subscribe: List-Help: List-Post: List-Id: "Tomcat Developers List" Reply-To: "Tomcat Developers List" Delivered-To: mailing list tomcat-dev@jakarta.apache.org Received: (qmail 11745 invoked by uid 98); 5 Nov 2002 21:29:31 -0000 X-Antivirus: nagoya (v4218 created Aug 14 2002) Subject: Client-cert and POST From: Bob Herrmann To: Tomcat Developers List Content-Type: multipart/mixed; boundary="=-gKOWgTtOCLd4hdqHbVYK" Organization: Message-Id: <1036531710.2238.27.camel@dhcp-ubur02-70-212.East.Sun.COM> Mime-Version: 1.0 X-Mailer: Ximian Evolution 1.1.90 (Preview Release) Date: 05 Nov 2002 16:28:31 -0500 X-Spam-Rating: daedalus.apache.org 1.6.2 0/1000/N X-Spam-Rating: daedalus.apache.org 1.6.2 0/1000/N --=-gKOWgTtOCLd4hdqHbVYK Content-Type: text/plain Content-Transfer-Encoding: 7bit I have been snooping around this problem with client-cert and POSTs for quite a while now. I have a patch that fixes it, but I really don't like my own work. This topic has raised it's ugly head a few times already here on Tomcat-dev, but allow me to restate it (yet again... hopefully with more clarity) Clients can POST data to Tomcat. They can do this on a secure socket. Tomcat can be configured to protect resources from the client based on the clients Certificates. So, a client can be POSTing data to a secure resource and tomcat may not yet have gotten the Certificates it needs. HERE IS WHERE STUFF GETS TRICKY (could I be wrong?) The certificate exchange can only happen once the POST data is removed from the socket. I talked with someone who implemented an SSL stack and he said that this is the way the protocol is. Boo. So in order to support work correctly, Tomcat must hold onto this POST until it is done, then certify the client, then proceed or bail. Messy messy. I tweaked Tomcat5 Coyote to do this. It works by changing CoyoteRequest to change the InputStream to a Buffered InputStream when a POST and an SSL Certrequest happen. What do you think? Cheers, -bob --=-gKOWgTtOCLd4hdqHbVYK Content-Disposition: attachment; filename=coyote.patch Content-Type: text/x-patch; name=coyote.patch; charset=ISO-8859-15 Content-Transfer-Encoding: 7bit Index: CoyoteRequest.java =================================================================== RCS file: /home/cvs/jakarta-tomcat-connectors/coyote/src/java/org/apache/coyote/tomcat5/CoyoteRequest.java,v retrieving revision 1.6 diff -u -r1.6 CoyoteRequest.java --- CoyoteRequest.java 18 Oct 2002 21:37:08 -0000 1.6 +++ CoyoteRequest.java 5 Nov 2002 21:25:45 -0000 @@ -377,7 +377,10 @@ pathInfo = null; servletPath = null; reader = null; - inputStream.recycle(); + if (inputStream instanceof CoyoteBufferedInputStream ) + inputStream = new CoyoteInputStream(); + else + inputStream.recycle(); userPrincipal = null; sessionParsed = false; authorization = null; @@ -783,6 +786,22 @@ return attr; // XXX Should move to Globals if(Constants.SSL_CERTIFICATE_ATTR.equals(name)) { + if ( "POST".equalsIgnoreCase(getMethod()) ){ + String contentType = getContentType(); + if (contentType == null) + contentType = ""; + int semicolon = contentType.indexOf(';'); + if (semicolon >= 0) { + contentType = contentType.substring(0, semicolon).trim(); + } else { + contentType = contentType.trim(); + } + + if (("application/x-www-form-urlencoded".equals(contentType))) + parseRequestParameters(); + else + inputStream = new CoyoteBufferedInputStream( this ); + } coyoteRequest.action(ActionCode.ACTION_REQ_SSL_CERTIFICATE, null); attr = getAttribute(Globals.CERTIFICATES_ATTR); if(attr != null) --=-gKOWgTtOCLd4hdqHbVYK Content-Disposition: attachment; filename=CoyoteBufferedInputStream.java Content-Type: text/x-java; name=CoyoteBufferedInputStream.java; charset=ISO-8859-15 Content-Transfer-Encoding: 7bit /* * ==================================================================== * * The Apache Software License, Version 1.1 * * Copyright (c) 1999 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact apache@apache.org. * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * * [Additional notices, if required by prior licensing conditions] * */ package org.apache.coyote.tomcat5; import java.io.InputStream; import java.io.IOException; import java.io.ByteArrayInputStream; import javax.servlet.ServletInputStream; import org.apache.coyote.Request; /** * This class handles buffering the entire Post body. Primarily for * use when an SSL certificate Exchange requires the entire body be * read. * * Since this class is so rarely used, it mimics CoyoteInputStream so * Coyotes request processing doesnt need to be adjusted for this * special case. */ class CoyoteBufferedInputStream extends CoyoteInputStream { CoyoteBufferedInputStream(CoyoteRequest coyoteRequest ){ int len = coyoteRequest.getContentLength(); if (len < 0 ){ // ISSUE: So someone is posting with no content-length on // an SSL Socket that needs to do a certificate exchange, // is this really likely? (does it every happen?) throw new RuntimeException("Need Content-Length to do SSL key exchange"); } if (len > 10*1000*1000 ){ // ISSUE: How much memory can we use to buffer the POST? // Should we start buffering to the filesystem? Then how // much of that can we use? Should we expose this as a // setting in server.xml? throw new RuntimeException("10 Meg is too much data in POST buffer and perform SSL key exchange"); } byte[] postData = new byte[ len ]; int offset = 0; InputStream is = coyoteRequest.getStream(); try { do { int inputLen = is.read(postData, offset, len - offset); if (inputLen <= 0) { break; } offset += inputLen; } while ((len - offset) > 0); } catch(IOException ioe){ throw new RuntimeException("Exception occured reading stream (used to buffer POST.): "+ioe); } if ( offset != len ) throw new RuntimeException( "Socket closed while buffering POST (to perform SSL key exchange)"); postStream = new ByteArrayInputStream( postData ); } // ----------------------------------------------------- Instance Variables private ByteArrayInputStream postStream; // --------------------------------------------------------- Public Methods void setRequest(Request coyoteRequest) { } void recycle() { // This should never ever be called. throw new RuntimeException("Dont recycle me."); } // --------------------------------------------- ServletInputStream Methods public int read() throws IOException { return postStream.read(); } public int available() throws IOException { return postStream.available(); } public int read(byte[] b) throws IOException { return postStream.read(b, 0, b.length); } public int read(byte[] b, int off, int len) throws IOException { return postStream.read(b,off,len); } // lifted from ServletInputStream public int readLine(byte[] b, int off, int len) throws IOException { if (len <= 0) { return 0; } int count = 0, c; while ((c = read()) != -1) { b[off++] = (byte)c; count++; if (c == '\n' || count == len) { break; } } return count > 0 ? count : -1; } public void close() { } } --=-gKOWgTtOCLd4hdqHbVYK Content-Type: text/plain; charset=us-ascii -- To unsubscribe, e-mail: For additional commands, e-mail: --=-gKOWgTtOCLd4hdqHbVYK--