Merge lp:~james-page/ubuntu/oneiric/tomcat6/CVE-2011-3190 into lp:ubuntu/oneiric/tomcat6

Proposed by James Page
Status: Merged
Merge reported by: Dave Walker
Merged at revision: not available
Proposed branch: lp:~james-page/ubuntu/oneiric/tomcat6/CVE-2011-3190
Merge into: lp:ubuntu/oneiric/tomcat6
Diff against target: 2811 lines (+2710/-9)
9 files modified
.pc/0013-CVE-2011-3190.patch/java/org/apache/coyote/ajp/AjpAprProcessor.java (+1337/-0)
.pc/0013-CVE-2011-3190.patch/java/org/apache/coyote/ajp/AjpProcessor.java (+1269/-0)
.pc/applied-patches (+1/-0)
debian/changelog (+6/-0)
debian/control (+2/-1)
debian/patches/0013-CVE-2011-3190.patch (+72/-0)
debian/patches/series (+1/-0)
java/org/apache/coyote/ajp/AjpAprProcessor.java (+11/-4)
java/org/apache/coyote/ajp/AjpProcessor.java (+11/-4)
To merge this branch: bzr merge lp:~james-page/ubuntu/oneiric/tomcat6/CVE-2011-3190
Reviewer Review Type Date Requested Status
Dave Walker (community) Approve
Ubuntu branches Pending
Review via email: mp+75495@code.launchpad.net

Description of the change

I've also committed this to the Debian tomcat6 SVN repository and will resync pre-release if it lands soon enough.

To post a comment you must log in.
Revision history for this message
Dave Walker (davewalker) wrote :

Uploaded!

Thanks.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory '.pc/0013-CVE-2011-3190.patch'
2=== added directory '.pc/0013-CVE-2011-3190.patch/java'
3=== added directory '.pc/0013-CVE-2011-3190.patch/java/org'
4=== added directory '.pc/0013-CVE-2011-3190.patch/java/org/apache'
5=== added directory '.pc/0013-CVE-2011-3190.patch/java/org/apache/coyote'
6=== added directory '.pc/0013-CVE-2011-3190.patch/java/org/apache/coyote/ajp'
7=== added file '.pc/0013-CVE-2011-3190.patch/java/org/apache/coyote/ajp/AjpAprProcessor.java'
8--- .pc/0013-CVE-2011-3190.patch/java/org/apache/coyote/ajp/AjpAprProcessor.java 1970-01-01 00:00:00 +0000
9+++ .pc/0013-CVE-2011-3190.patch/java/org/apache/coyote/ajp/AjpAprProcessor.java 2011-09-15 08:37:39 +0000
10@@ -0,0 +1,1337 @@
11+/*
12+ * Licensed to the Apache Software Foundation (ASF) under one or more
13+ * contributor license agreements. See the NOTICE file distributed with
14+ * this work for additional information regarding copyright ownership.
15+ * The ASF licenses this file to You under the Apache License, Version 2.0
16+ * (the "License"); you may not use this file except in compliance with
17+ * the License. You may obtain a copy of the License at
18+ *
19+ * http://www.apache.org/licenses/LICENSE-2.0
20+ *
21+ * Unless required by applicable law or agreed to in writing, software
22+ * distributed under the License is distributed on an "AS IS" BASIS,
23+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24+ * See the License for the specific language governing permissions and
25+ * limitations under the License.
26+ */
27+
28+package org.apache.coyote.ajp;
29+
30+import java.io.ByteArrayInputStream;
31+import java.io.IOException;
32+import java.io.InterruptedIOException;
33+import java.net.InetAddress;
34+import java.nio.ByteBuffer;
35+import java.security.cert.CertificateFactory;
36+import java.security.cert.X509Certificate;
37+
38+import org.apache.coyote.ActionCode;
39+import org.apache.coyote.ActionHook;
40+import org.apache.coyote.Adapter;
41+import org.apache.coyote.InputBuffer;
42+import org.apache.coyote.OutputBuffer;
43+import org.apache.coyote.Request;
44+import org.apache.coyote.RequestInfo;
45+import org.apache.coyote.Response;
46+import org.apache.tomcat.jni.Socket;
47+import org.apache.tomcat.jni.Status;
48+import org.apache.tomcat.util.buf.ByteChunk;
49+import org.apache.tomcat.util.buf.HexUtils;
50+import org.apache.tomcat.util.buf.MessageBytes;
51+import org.apache.tomcat.util.http.HttpMessages;
52+import org.apache.tomcat.util.http.MimeHeaders;
53+import org.apache.tomcat.util.net.AprEndpoint;
54+import org.apache.tomcat.util.res.StringManager;
55+
56+
57+/**
58+ * Processes HTTP requests.
59+ *
60+ * @author Remy Maucherat
61+ * @author Henri Gomez
62+ * @author Dan Milstein
63+ * @author Keith Wannamaker
64+ * @author Kevin Seguin
65+ * @author Costin Manolache
66+ * @author Bill Barker
67+ */
68+public class AjpAprProcessor implements ActionHook {
69+
70+
71+ /**
72+ * Logger.
73+ */
74+ protected static org.apache.juli.logging.Log log
75+ = org.apache.juli.logging.LogFactory.getLog(AjpAprProcessor.class);
76+
77+ /**
78+ * The string manager for this package.
79+ */
80+ protected static StringManager sm =
81+ StringManager.getManager(Constants.Package);
82+
83+
84+ // ----------------------------------------------------------- Constructors
85+
86+
87+ public AjpAprProcessor(int packetSize, AprEndpoint endpoint) {
88+
89+ this.endpoint = endpoint;
90+
91+ request = new Request();
92+ request.setInputBuffer(new SocketInputBuffer());
93+
94+ response = new Response();
95+ response.setHook(this);
96+ response.setOutputBuffer(new SocketOutputBuffer());
97+ request.setResponse(response);
98+
99+ this.packetSize = packetSize;
100+ requestHeaderMessage = new AjpMessage(packetSize);
101+ responseHeaderMessage = new AjpMessage(packetSize);
102+ bodyMessage = new AjpMessage(packetSize);
103+
104+ // Set the get body message buffer
105+ AjpMessage getBodyMessage = new AjpMessage(16);
106+ getBodyMessage.reset();
107+ getBodyMessage.appendByte(Constants.JK_AJP13_GET_BODY_CHUNK);
108+ // Adjust allowed size if packetSize != default (Constants.MAX_PACKET_SIZE)
109+ getBodyMessage.appendInt(Constants.MAX_READ_SIZE + packetSize - Constants.MAX_PACKET_SIZE);
110+ getBodyMessage.end();
111+ getBodyMessageBuffer =
112+ ByteBuffer.allocateDirect(getBodyMessage.getLen());
113+ getBodyMessageBuffer.put(getBodyMessage.getBuffer(), 0,
114+ getBodyMessage.getLen());
115+
116+ // Allocate input and output buffers
117+ inputBuffer = ByteBuffer.allocateDirect(packetSize * 2);
118+ inputBuffer.limit(0);
119+ outputBuffer = ByteBuffer.allocateDirect(packetSize * 2);
120+
121+ // Cause loading of HexUtils
122+ int foo = HexUtils.DEC[0];
123+
124+ // Cause loading of HttpMessages
125+ HttpMessages.getMessage(200);
126+
127+ }
128+
129+
130+ // ----------------------------------------------------- Instance Variables
131+
132+
133+ /**
134+ * Associated adapter.
135+ */
136+ protected Adapter adapter = null;
137+
138+
139+ /**
140+ * Request object.
141+ */
142+ protected Request request = null;
143+
144+
145+ /**
146+ * Response object.
147+ */
148+ protected Response response = null;
149+
150+
151+ /**
152+ * The socket timeout used when reading the first block of the request
153+ * header.
154+ */
155+ protected int packetSize;
156+
157+ /**
158+ * Header message. Note that this header is merely the one used during the
159+ * processing of the first message of a "request", so it might not be a request
160+ * header. It will stay unchanged during the processing of the whole request.
161+ */
162+ protected AjpMessage requestHeaderMessage = null;
163+
164+
165+ /**
166+ * Message used for response header composition.
167+ */
168+ protected AjpMessage responseHeaderMessage = null;
169+
170+
171+ /**
172+ * Body message.
173+ */
174+ protected AjpMessage bodyMessage = null;
175+
176+
177+ /**
178+ * Body message.
179+ */
180+ protected MessageBytes bodyBytes = MessageBytes.newInstance();
181+
182+
183+ /**
184+ * State flag.
185+ */
186+ protected boolean started = false;
187+
188+
189+ /**
190+ * Error flag.
191+ */
192+ protected boolean error = false;
193+
194+
195+ /**
196+ * Socket associated with the current connection.
197+ */
198+ protected long socket;
199+
200+
201+ /**
202+ * Host name (used to avoid useless B2C conversion on the host name).
203+ */
204+ protected char[] hostNameC = new char[0];
205+
206+
207+ /**
208+ * Associated endpoint.
209+ */
210+ protected AprEndpoint endpoint;
211+
212+
213+ /**
214+ * Temp message bytes used for processing.
215+ */
216+ protected MessageBytes tmpMB = MessageBytes.newInstance();
217+
218+
219+ /**
220+ * Byte chunk for certs.
221+ */
222+ protected MessageBytes certificates = MessageBytes.newInstance();
223+
224+
225+ /**
226+ * End of stream flag.
227+ */
228+ protected boolean endOfStream = false;
229+
230+
231+ /**
232+ * Body empty flag.
233+ */
234+ protected boolean empty = true;
235+
236+
237+ /**
238+ * First read.
239+ */
240+ protected boolean first = true;
241+
242+
243+ /**
244+ * Replay read.
245+ */
246+ protected boolean replay = false;
247+
248+
249+ /**
250+ * Finished response.
251+ */
252+ protected boolean finished = false;
253+
254+
255+ /**
256+ * Direct buffer used for output.
257+ */
258+ protected ByteBuffer outputBuffer = null;
259+
260+
261+ /**
262+ * Direct buffer used for input.
263+ */
264+ protected ByteBuffer inputBuffer = null;
265+
266+
267+ /**
268+ * Direct buffer used for sending right away a get body message.
269+ */
270+ protected final ByteBuffer getBodyMessageBuffer;
271+
272+
273+ /**
274+ * Direct buffer used for sending right away a pong message.
275+ */
276+ protected static final ByteBuffer pongMessageBuffer;
277+
278+
279+ /**
280+ * End message array.
281+ */
282+ protected static final byte[] endMessageArray;
283+
284+ /**
285+ * Direct buffer used for sending explicit flush message.
286+ */
287+ protected static final ByteBuffer flushMessageBuffer;
288+
289+
290+ // ----------------------------------------------------- Static Initializer
291+
292+
293+ static {
294+
295+ // Set the read body message buffer
296+ AjpMessage pongMessage = new AjpMessage(16);
297+ pongMessage.reset();
298+ pongMessage.appendByte(Constants.JK_AJP13_CPONG_REPLY);
299+ pongMessage.end();
300+ pongMessageBuffer = ByteBuffer.allocateDirect(pongMessage.getLen());
301+ pongMessageBuffer.put(pongMessage.getBuffer(), 0,
302+ pongMessage.getLen());
303+
304+ // Allocate the end message array
305+ AjpMessage endMessage = new AjpMessage(16);
306+ endMessage.reset();
307+ endMessage.appendByte(Constants.JK_AJP13_END_RESPONSE);
308+ endMessage.appendByte(1);
309+ endMessage.end();
310+ endMessageArray = new byte[endMessage.getLen()];
311+ System.arraycopy(endMessage.getBuffer(), 0, endMessageArray, 0,
312+ endMessage.getLen());
313+
314+ // Set the flush message buffer
315+ AjpMessage flushMessage = new AjpMessage(16);
316+ flushMessage.reset();
317+ flushMessage.appendByte(Constants.JK_AJP13_SEND_BODY_CHUNK);
318+ flushMessage.appendInt(0);
319+ flushMessage.appendByte(0);
320+ flushMessage.end();
321+ flushMessageBuffer =
322+ ByteBuffer.allocateDirect(flushMessage.getLen());
323+ flushMessageBuffer.put(flushMessage.getBuffer(), 0,
324+ flushMessage.getLen());
325+
326+ }
327+
328+
329+ // ------------------------------------------------------------- Properties
330+
331+
332+ /**
333+ * Use Tomcat authentication ?
334+ */
335+ protected boolean tomcatAuthentication = true;
336+ public boolean getTomcatAuthentication() { return tomcatAuthentication; }
337+ public void setTomcatAuthentication(boolean tomcatAuthentication) { this.tomcatAuthentication = tomcatAuthentication; }
338+
339+
340+ /**
341+ * Required secret.
342+ */
343+ protected String requiredSecret = null;
344+ public void setRequiredSecret(String requiredSecret) { this.requiredSecret = requiredSecret; }
345+
346+
347+ // --------------------------------------------------------- Public Methods
348+
349+
350+ /** Get the request associated with this processor.
351+ *
352+ * @return The request
353+ */
354+ public Request getRequest() {
355+ return request;
356+ }
357+
358+
359+ /**
360+ * Process pipelined HTTP requests using the specified input and output
361+ * streams.
362+ *
363+ * @throws IOException error during an I/O operation
364+ */
365+ public boolean process(long socket)
366+ throws IOException {
367+ RequestInfo rp = request.getRequestProcessor();
368+ rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
369+
370+ // Setting up the socket
371+ this.socket = socket;
372+ Socket.setrbb(this.socket, inputBuffer);
373+ Socket.setsbb(this.socket, outputBuffer);
374+
375+ // Error flag
376+ error = false;
377+
378+ boolean openSocket = true;
379+ boolean keptAlive = false;
380+
381+ while (started && !error) {
382+
383+ // Parsing the request header
384+ try {
385+ // Get first message of the request
386+ if (!readMessage(requestHeaderMessage, true, keptAlive)) {
387+ // This means that no data is available right now
388+ // (long keepalive), so that the processor should be recycled
389+ // and the method should return true
390+ rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
391+ break;
392+ }
393+ // Check message type, process right away and break if
394+ // not regular request processing
395+ int type = requestHeaderMessage.getByte();
396+ if (type == Constants.JK_AJP13_CPING_REQUEST) {
397+ if (Socket.sendb(socket, pongMessageBuffer, 0,
398+ pongMessageBuffer.position()) < 0) {
399+ error = true;
400+ }
401+ continue;
402+ } else if(type != Constants.JK_AJP13_FORWARD_REQUEST) {
403+ // Usually the servlet didn't read the previous request body
404+ if(log.isDebugEnabled()) {
405+ log.debug("Unexpected message: "+type);
406+ }
407+ continue;
408+ }
409+
410+ keptAlive = true;
411+ request.setStartTime(System.currentTimeMillis());
412+ } catch (IOException e) {
413+ error = true;
414+ break;
415+ } catch (Throwable t) {
416+ log.debug(sm.getString("ajpprocessor.header.error"), t);
417+ // 400 - Bad Request
418+ response.setStatus(400);
419+ adapter.log(request, response, 0);
420+ error = true;
421+ }
422+
423+ // Setting up filters, and parse some request headers
424+ rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
425+ try {
426+ prepareRequest();
427+ } catch (Throwable t) {
428+ log.debug(sm.getString("ajpprocessor.request.prepare"), t);
429+ // 400 - Internal Server Error
430+ response.setStatus(400);
431+ adapter.log(request, response, 0);
432+ error = true;
433+ }
434+
435+ // Process the request in the adapter
436+ if (!error) {
437+ try {
438+ rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
439+ adapter.service(request, response);
440+ } catch (InterruptedIOException e) {
441+ error = true;
442+ } catch (Throwable t) {
443+ log.error(sm.getString("ajpprocessor.request.process"), t);
444+ // 500 - Internal Server Error
445+ response.setStatus(500);
446+ adapter.log(request, response, 0);
447+ error = true;
448+ }
449+ }
450+
451+ // Finish the response if not done yet
452+ if (!finished) {
453+ try {
454+ finish();
455+ } catch (Throwable t) {
456+ error = true;
457+ }
458+ }
459+
460+ // If there was an error, make sure the request is counted as
461+ // and error, and update the statistics counter
462+ if (error) {
463+ response.setStatus(500);
464+ }
465+ request.updateCounters();
466+
467+ rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
468+ recycle();
469+
470+ }
471+
472+ // Add the socket to the poller
473+ if (!error) {
474+ endpoint.getPoller().add(socket);
475+ } else {
476+ openSocket = false;
477+ }
478+
479+ rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
480+ recycle();
481+
482+ return openSocket;
483+
484+ }
485+
486+
487+ // ----------------------------------------------------- ActionHook Methods
488+
489+
490+ /**
491+ * Send an action to the connector.
492+ *
493+ * @param actionCode Type of the action
494+ * @param param Action parameter
495+ */
496+ public void action(ActionCode actionCode, Object param) {
497+
498+ if (actionCode == ActionCode.ACTION_COMMIT) {
499+
500+ if (response.isCommitted())
501+ return;
502+
503+ // Validate and write response headers
504+ try {
505+ prepareResponse();
506+ } catch (IOException e) {
507+ // Set error flag
508+ error = true;
509+ }
510+
511+ } else if (actionCode == ActionCode.ACTION_CLIENT_FLUSH) {
512+
513+ if (!response.isCommitted()) {
514+ // Validate and write response headers
515+ try {
516+ prepareResponse();
517+ } catch (IOException e) {
518+ // Set error flag
519+ error = true;
520+ return;
521+ }
522+ }
523+
524+ try {
525+ flush();
526+ // Send explicit flush message
527+ if (Socket.sendb(socket, flushMessageBuffer, 0,
528+ flushMessageBuffer.position()) < 0) {
529+ error = true;
530+ }
531+ } catch (IOException e) {
532+ // Set error flag
533+ error = true;
534+ }
535+
536+ } else if (actionCode == ActionCode.ACTION_CLOSE) {
537+ // Close
538+
539+ // End the processing of the current request, and stop any further
540+ // transactions with the client
541+
542+ try {
543+ finish();
544+ } catch (IOException e) {
545+ // Set error flag
546+ error = true;
547+ }
548+
549+ } else if (actionCode == ActionCode.ACTION_START) {
550+
551+ started = true;
552+
553+ } else if (actionCode == ActionCode.ACTION_STOP) {
554+
555+ started = false;
556+
557+ } else if (actionCode == ActionCode.ACTION_REQ_SSL_ATTRIBUTE ) {
558+
559+ if (!certificates.isNull()) {
560+ ByteChunk certData = certificates.getByteChunk();
561+ X509Certificate jsseCerts[] = null;
562+ ByteArrayInputStream bais =
563+ new ByteArrayInputStream(certData.getBytes(),
564+ certData.getStart(),
565+ certData.getLength());
566+ // Fill the elements.
567+ try {
568+ CertificateFactory cf =
569+ CertificateFactory.getInstance("X.509");
570+ while(bais.available() > 0) {
571+ X509Certificate cert = (X509Certificate)
572+ cf.generateCertificate(bais);
573+ if(jsseCerts == null) {
574+ jsseCerts = new X509Certificate[1];
575+ jsseCerts[0] = cert;
576+ } else {
577+ X509Certificate [] temp = new X509Certificate[jsseCerts.length+1];
578+ System.arraycopy(jsseCerts,0,temp,0,jsseCerts.length);
579+ temp[jsseCerts.length] = cert;
580+ jsseCerts = temp;
581+ }
582+ }
583+ } catch (java.security.cert.CertificateException e) {
584+ log.error(sm.getString("ajpprocessor.certs.fail"), e);
585+ return;
586+ }
587+ request.setAttribute(AprEndpoint.CERTIFICATE_KEY, jsseCerts);
588+ }
589+
590+ } else if (actionCode == ActionCode.ACTION_REQ_HOST_ATTRIBUTE) {
591+
592+ // Get remote host name using a DNS resolution
593+ if (request.remoteHost().isNull()) {
594+ try {
595+ request.remoteHost().setString(InetAddress.getByName
596+ (request.remoteAddr().toString()).getHostName());
597+ } catch (IOException iex) {
598+ // Ignore
599+ }
600+ }
601+
602+ } else if (actionCode == ActionCode.ACTION_REQ_LOCAL_ADDR_ATTRIBUTE) {
603+
604+ // Copy from local name for now, which should simply be an address
605+ request.localAddr().setString(request.localName().toString());
606+
607+ } else if (actionCode == ActionCode.ACTION_REQ_SET_BODY_REPLAY) {
608+
609+ // Set the given bytes as the content
610+ ByteChunk bc = (ByteChunk) param;
611+ int length = bc.getLength();
612+ bodyBytes.setBytes(bc.getBytes(), bc.getStart(), length);
613+ request.setContentLength(length);
614+ first = false;
615+ empty = false;
616+ replay = true;
617+
618+ }
619+
620+
621+ }
622+
623+
624+ // ------------------------------------------------------ Connector Methods
625+
626+
627+ /**
628+ * Set the associated adapter.
629+ *
630+ * @param adapter the new adapter
631+ */
632+ public void setAdapter(Adapter adapter) {
633+ this.adapter = adapter;
634+ }
635+
636+
637+ /**
638+ * Get the associated adapter.
639+ *
640+ * @return the associated adapter
641+ */
642+ public Adapter getAdapter() {
643+ return adapter;
644+ }
645+
646+
647+ // ------------------------------------------------------ Protected Methods
648+
649+
650+ /**
651+ * After reading the request headers, we have to setup the request filters.
652+ */
653+ protected void prepareRequest() {
654+
655+ // Translate the HTTP method code to a String.
656+ byte methodCode = requestHeaderMessage.getByte();
657+ if (methodCode != Constants.SC_M_JK_STORED) {
658+ String methodName = Constants.methodTransArray[(int)methodCode - 1];
659+ request.method().setString(methodName);
660+ }
661+
662+ requestHeaderMessage.getBytes(request.protocol());
663+ requestHeaderMessage.getBytes(request.requestURI());
664+
665+ requestHeaderMessage.getBytes(request.remoteAddr());
666+ requestHeaderMessage.getBytes(request.remoteHost());
667+ requestHeaderMessage.getBytes(request.localName());
668+ request.setLocalPort(requestHeaderMessage.getInt());
669+
670+ boolean isSSL = requestHeaderMessage.getByte() != 0;
671+ if (isSSL) {
672+ request.scheme().setString("https");
673+ }
674+
675+ // Decode headers
676+ MimeHeaders headers = request.getMimeHeaders();
677+
678+ int hCount = requestHeaderMessage.getInt();
679+ for(int i = 0 ; i < hCount ; i++) {
680+ String hName = null;
681+
682+ // Header names are encoded as either an integer code starting
683+ // with 0xA0, or as a normal string (in which case the first
684+ // two bytes are the length).
685+ int isc = requestHeaderMessage.peekInt();
686+ int hId = isc & 0xFF;
687+
688+ MessageBytes vMB = null;
689+ isc &= 0xFF00;
690+ if(0xA000 == isc) {
691+ requestHeaderMessage.getInt(); // To advance the read position
692+ hName = Constants.headerTransArray[hId - 1];
693+ vMB = headers.addValue(hName);
694+ } else {
695+ // reset hId -- if the header currently being read
696+ // happens to be 7 or 8 bytes long, the code below
697+ // will think it's the content-type header or the
698+ // content-length header - SC_REQ_CONTENT_TYPE=7,
699+ // SC_REQ_CONTENT_LENGTH=8 - leading to unexpected
700+ // behaviour. see bug 5861 for more information.
701+ hId = -1;
702+ requestHeaderMessage.getBytes(tmpMB);
703+ ByteChunk bc = tmpMB.getByteChunk();
704+ vMB = headers.addValue(bc.getBuffer(),
705+ bc.getStart(), bc.getLength());
706+ }
707+
708+ requestHeaderMessage.getBytes(vMB);
709+
710+ if (hId == Constants.SC_REQ_CONTENT_LENGTH ||
711+ (hId == -1 && tmpMB.equalsIgnoreCase("Content-Length"))) {
712+ // just read the content-length header, so set it
713+ long cl = vMB.getLong();
714+ if(cl < Integer.MAX_VALUE)
715+ request.setContentLength( (int)cl );
716+ } else if (hId == Constants.SC_REQ_CONTENT_TYPE ||
717+ (hId == -1 && tmpMB.equalsIgnoreCase("Content-Type"))) {
718+ // just read the content-type header, so set it
719+ ByteChunk bchunk = vMB.getByteChunk();
720+ request.contentType().setBytes(bchunk.getBytes(),
721+ bchunk.getOffset(),
722+ bchunk.getLength());
723+ }
724+ }
725+
726+ // Decode extra attributes
727+ boolean secret = false;
728+ byte attributeCode;
729+ while ((attributeCode = requestHeaderMessage.getByte())
730+ != Constants.SC_A_ARE_DONE) {
731+
732+ switch (attributeCode) {
733+
734+ case Constants.SC_A_REQ_ATTRIBUTE :
735+ requestHeaderMessage.getBytes(tmpMB);
736+ String n = tmpMB.toString();
737+ requestHeaderMessage.getBytes(tmpMB);
738+ String v = tmpMB.toString();
739+ /*
740+ * AJP13 misses to forward the remotePort.
741+ * Allow the AJP connector to add this info via
742+ * a private request attribute.
743+ * We will accept the forwarded data as the remote port,
744+ * and remove it from the public list of request attributes.
745+ */
746+ if(n.equals(Constants.SC_A_REQ_REMOTE_PORT)) {
747+ try {
748+ request.setRemotePort(Integer.parseInt(v));
749+ } catch (NumberFormatException nfe) {
750+ }
751+ } else {
752+ request.setAttribute(n, v );
753+ }
754+ break;
755+
756+ case Constants.SC_A_CONTEXT :
757+ requestHeaderMessage.getBytes(tmpMB);
758+ // nothing
759+ break;
760+
761+ case Constants.SC_A_SERVLET_PATH :
762+ requestHeaderMessage.getBytes(tmpMB);
763+ // nothing
764+ break;
765+
766+ case Constants.SC_A_REMOTE_USER :
767+ if (tomcatAuthentication) {
768+ // ignore server
769+ requestHeaderMessage.getBytes(tmpMB);
770+ } else {
771+ requestHeaderMessage.getBytes(request.getRemoteUser());
772+ }
773+ break;
774+
775+ case Constants.SC_A_AUTH_TYPE :
776+ if (tomcatAuthentication) {
777+ // ignore server
778+ requestHeaderMessage.getBytes(tmpMB);
779+ } else {
780+ requestHeaderMessage.getBytes(request.getAuthType());
781+ }
782+ break;
783+
784+ case Constants.SC_A_QUERY_STRING :
785+ requestHeaderMessage.getBytes(request.queryString());
786+ break;
787+
788+ case Constants.SC_A_JVM_ROUTE :
789+ requestHeaderMessage.getBytes(request.instanceId());
790+ break;
791+
792+ case Constants.SC_A_SSL_CERT :
793+ request.scheme().setString("https");
794+ // SSL certificate extraction is lazy, moved to JkCoyoteHandler
795+ requestHeaderMessage.getBytes(certificates);
796+ break;
797+
798+ case Constants.SC_A_SSL_CIPHER :
799+ request.scheme().setString("https");
800+ requestHeaderMessage.getBytes(tmpMB);
801+ request.setAttribute(AprEndpoint.CIPHER_SUITE_KEY,
802+ tmpMB.toString());
803+ break;
804+
805+ case Constants.SC_A_SSL_SESSION :
806+ request.scheme().setString("https");
807+ requestHeaderMessage.getBytes(tmpMB);
808+ request.setAttribute(AprEndpoint.SESSION_ID_KEY,
809+ tmpMB.toString());
810+ break;
811+
812+ case Constants.SC_A_SSL_KEY_SIZE :
813+ request.setAttribute(AprEndpoint.KEY_SIZE_KEY,
814+ new Integer(requestHeaderMessage.getInt()));
815+ break;
816+
817+ case Constants.SC_A_STORED_METHOD:
818+ requestHeaderMessage.getBytes(request.method());
819+ break;
820+
821+ case Constants.SC_A_SECRET:
822+ requestHeaderMessage.getBytes(tmpMB);
823+ if (requiredSecret != null) {
824+ secret = true;
825+ if (!tmpMB.equals(requiredSecret)) {
826+ response.setStatus(403);
827+ adapter.log(request, response, 0);
828+ error = true;
829+ }
830+ }
831+ break;
832+
833+ default:
834+ // Ignore unknown attribute for backward compatibility
835+ break;
836+
837+ }
838+
839+ }
840+
841+ // Check if secret was submitted if required
842+ if ((requiredSecret != null) && !secret) {
843+ response.setStatus(403);
844+ adapter.log(request, response, 0);
845+ error = true;
846+ }
847+
848+ // Check for a full URI (including protocol://host:port/)
849+ ByteChunk uriBC = request.requestURI().getByteChunk();
850+ if (uriBC.startsWithIgnoreCase("http", 0)) {
851+
852+ int pos = uriBC.indexOf("://", 0, 3, 4);
853+ int uriBCStart = uriBC.getStart();
854+ int slashPos = -1;
855+ if (pos != -1) {
856+ byte[] uriB = uriBC.getBytes();
857+ slashPos = uriBC.indexOf('/', pos + 3);
858+ if (slashPos == -1) {
859+ slashPos = uriBC.getLength();
860+ // Set URI as "/"
861+ request.requestURI().setBytes
862+ (uriB, uriBCStart + pos + 1, 1);
863+ } else {
864+ request.requestURI().setBytes
865+ (uriB, uriBCStart + slashPos,
866+ uriBC.getLength() - slashPos);
867+ }
868+ MessageBytes hostMB = headers.setValue("host");
869+ hostMB.setBytes(uriB, uriBCStart + pos + 3,
870+ slashPos - pos - 3);
871+ }
872+
873+ }
874+
875+ MessageBytes valueMB = request.getMimeHeaders().getValue("host");
876+ parseHost(valueMB);
877+
878+ }
879+
880+
881+ /**
882+ * Parse host.
883+ */
884+ public void parseHost(MessageBytes valueMB) {
885+
886+ if (valueMB == null || (valueMB != null && valueMB.isNull()) ) {
887+ // HTTP/1.0
888+ request.setServerPort(request.getLocalPort());
889+ try {
890+ request.serverName().duplicate(request.localName());
891+ } catch (IOException e) {
892+ response.setStatus(400);
893+ adapter.log(request, response, 0);
894+ error = true;
895+ }
896+ return;
897+ }
898+
899+ ByteChunk valueBC = valueMB.getByteChunk();
900+ byte[] valueB = valueBC.getBytes();
901+ int valueL = valueBC.getLength();
902+ int valueS = valueBC.getStart();
903+ int colonPos = -1;
904+ if (hostNameC.length < valueL) {
905+ hostNameC = new char[valueL];
906+ }
907+
908+ boolean ipv6 = (valueB[valueS] == '[');
909+ boolean bracketClosed = false;
910+ for (int i = 0; i < valueL; i++) {
911+ char b = (char) valueB[i + valueS];
912+ hostNameC[i] = b;
913+ if (b == ']') {
914+ bracketClosed = true;
915+ } else if (b == ':') {
916+ if (!ipv6 || bracketClosed) {
917+ colonPos = i;
918+ break;
919+ }
920+ }
921+ }
922+
923+ if (colonPos < 0) {
924+ if (request.scheme().equalsIgnoreCase("https")) {
925+ // 443 - Default HTTPS port
926+ request.setServerPort(443);
927+ } else {
928+ // 80 - Default HTTTP port
929+ request.setServerPort(80);
930+ }
931+ request.serverName().setChars(hostNameC, 0, valueL);
932+ } else {
933+
934+ request.serverName().setChars(hostNameC, 0, colonPos);
935+
936+ int port = 0;
937+ int mult = 1;
938+ for (int i = valueL - 1; i > colonPos; i--) {
939+ int charValue = HexUtils.DEC[(int) valueB[i + valueS]];
940+ if (charValue == -1) {
941+ // Invalid character
942+ error = true;
943+ // 400 - Bad request
944+ response.setStatus(400);
945+ adapter.log(request, response, 0);
946+ break;
947+ }
948+ port = port + (charValue * mult);
949+ mult = 10 * mult;
950+ }
951+ request.setServerPort(port);
952+
953+ }
954+
955+ }
956+
957+
958+ /**
959+ * When committing the response, we have to validate the set of headers, as
960+ * well as setup the response filters.
961+ */
962+ protected void prepareResponse()
963+ throws IOException {
964+
965+ response.setCommitted(true);
966+
967+ responseHeaderMessage.reset();
968+ responseHeaderMessage.appendByte(Constants.JK_AJP13_SEND_HEADERS);
969+
970+ // HTTP header contents
971+ responseHeaderMessage.appendInt(response.getStatus());
972+ String message = null;
973+ if (org.apache.coyote.Constants.USE_CUSTOM_STATUS_MSG_IN_HEADER &&
974+ HttpMessages.isSafeInHttpHeader(response.getMessage())) {
975+ message = response.getMessage();
976+ }
977+ if (message == null){
978+ message = HttpMessages.getMessage(response.getStatus());
979+ }
980+ if (message == null) {
981+ // mod_jk + httpd 2.x fails with a null status message - bug 45026
982+ message = Integer.toString(response.getStatus());
983+ }
984+ tmpMB.setString(message);
985+ responseHeaderMessage.appendBytes(tmpMB);
986+
987+ // Special headers
988+ MimeHeaders headers = response.getMimeHeaders();
989+ String contentType = response.getContentType();
990+ if (contentType != null) {
991+ headers.setValue("Content-Type").setString(contentType);
992+ }
993+ String contentLanguage = response.getContentLanguage();
994+ if (contentLanguage != null) {
995+ headers.setValue("Content-Language").setString(contentLanguage);
996+ }
997+ long contentLength = response.getContentLengthLong();
998+ if (contentLength >= 0) {
999+ headers.setValue("Content-Length").setLong(contentLength);
1000+ }
1001+
1002+ // Other headers
1003+ int numHeaders = headers.size();
1004+ responseHeaderMessage.appendInt(numHeaders);
1005+ for (int i = 0; i < numHeaders; i++) {
1006+ MessageBytes hN = headers.getName(i);
1007+ int hC = Constants.getResponseAjpIndex(hN.toString());
1008+ if (hC > 0) {
1009+ responseHeaderMessage.appendInt(hC);
1010+ }
1011+ else {
1012+ responseHeaderMessage.appendBytes(hN);
1013+ }
1014+ MessageBytes hV=headers.getValue(i);
1015+ responseHeaderMessage.appendBytes(hV);
1016+ }
1017+
1018+ // Write to buffer
1019+ responseHeaderMessage.end();
1020+ outputBuffer.put(responseHeaderMessage.getBuffer(), 0, responseHeaderMessage.getLen());
1021+
1022+ }
1023+
1024+
1025+ /**
1026+ * Finish AJP response.
1027+ */
1028+ protected void finish()
1029+ throws IOException {
1030+
1031+ if (!response.isCommitted()) {
1032+ // Validate and write response headers
1033+ try {
1034+ prepareResponse();
1035+ } catch (IOException e) {
1036+ // Set error flag
1037+ error = true;
1038+ }
1039+ }
1040+
1041+ if (finished)
1042+ return;
1043+
1044+ finished = true;
1045+
1046+ // Add the end message
1047+ if (outputBuffer.position() + endMessageArray.length > outputBuffer.capacity()) {
1048+ flush();
1049+ }
1050+ outputBuffer.put(endMessageArray);
1051+ flush();
1052+
1053+ }
1054+
1055+
1056+ /**
1057+ * Read at least the specified amount of bytes, and place them
1058+ * in the input buffer.
1059+ */
1060+ protected boolean read(int n)
1061+ throws IOException {
1062+
1063+ if (inputBuffer.capacity() - inputBuffer.limit() <=
1064+ n - inputBuffer.remaining()) {
1065+ inputBuffer.compact();
1066+ inputBuffer.limit(inputBuffer.position());
1067+ inputBuffer.position(0);
1068+ }
1069+ int nRead;
1070+ while (inputBuffer.remaining() < n) {
1071+ nRead = Socket.recvbb
1072+ (socket, inputBuffer.limit(),
1073+ inputBuffer.capacity() - inputBuffer.limit());
1074+ if (nRead > 0) {
1075+ inputBuffer.limit(inputBuffer.limit() + nRead);
1076+ } else {
1077+ throw new IOException(sm.getString("ajpprotocol.failedread"));
1078+ }
1079+ }
1080+
1081+ return true;
1082+
1083+ }
1084+
1085+
1086+ /**
1087+ * Read at least the specified amount of bytes, and place them
1088+ * in the input buffer.
1089+ */
1090+ protected boolean readt(int n, boolean useAvailableData)
1091+ throws IOException {
1092+
1093+ if (useAvailableData && inputBuffer.remaining() == 0) {
1094+ return false;
1095+ }
1096+ if (inputBuffer.capacity() - inputBuffer.limit() <=
1097+ n - inputBuffer.remaining()) {
1098+ inputBuffer.compact();
1099+ inputBuffer.limit(inputBuffer.position());
1100+ inputBuffer.position(0);
1101+ }
1102+ int nRead;
1103+ while (inputBuffer.remaining() < n) {
1104+ nRead = Socket.recvbb
1105+ (socket, inputBuffer.limit(),
1106+ inputBuffer.capacity() - inputBuffer.limit());
1107+ if (nRead > 0) {
1108+ inputBuffer.limit(inputBuffer.limit() + nRead);
1109+ } else {
1110+ if ((-nRead) == Status.ETIMEDOUT || (-nRead) == Status.TIMEUP) {
1111+ return false;
1112+ } else {
1113+ throw new IOException(sm.getString("ajpprotocol.failedread"));
1114+ }
1115+ }
1116+ }
1117+
1118+ return true;
1119+
1120+ }
1121+
1122+
1123+ /** Receive a chunk of data. Called to implement the
1124+ * 'special' packet in ajp13 and to receive the data
1125+ * after we send a GET_BODY packet
1126+ */
1127+ public boolean receive() throws IOException {
1128+
1129+ first = false;
1130+ bodyMessage.reset();
1131+ readMessage(bodyMessage, false, false);
1132+
1133+ // No data received.
1134+ if (bodyMessage.getLen() == 0) {
1135+ // just the header
1136+ // Don't mark 'end of stream' for the first chunk.
1137+ return false;
1138+ }
1139+ int blen = bodyMessage.peekInt();
1140+ if (blen == 0) {
1141+ return false;
1142+ }
1143+
1144+ bodyMessage.getBytes(bodyBytes);
1145+ empty = false;
1146+ return true;
1147+ }
1148+
1149+ /**
1150+ * Get more request body data from the web server and store it in the
1151+ * internal buffer.
1152+ *
1153+ * @return true if there is more data, false if not.
1154+ */
1155+ private boolean refillReadBuffer() throws IOException {
1156+ // If the server returns an empty packet, assume that that end of
1157+ // the stream has been reached (yuck -- fix protocol??).
1158+ // FORM support
1159+ if (replay) {
1160+ endOfStream = true; // we've read everything there is
1161+ }
1162+ if (endOfStream) {
1163+ return false;
1164+ }
1165+
1166+ // Request more data immediately
1167+ Socket.sendb(socket, getBodyMessageBuffer, 0,
1168+ getBodyMessageBuffer.position());
1169+
1170+ boolean moreData = receive();
1171+ if( !moreData ) {
1172+ endOfStream = true;
1173+ }
1174+ return moreData;
1175+ }
1176+
1177+
1178+ /**
1179+ * Read an AJP message.
1180+ *
1181+ * @param first is true if the message is the first in the request, which
1182+ * will cause a short duration blocking read
1183+ * @return true if the message has been read, false if the short read
1184+ * didn't return anything
1185+ * @throws IOException any other failure, including incomplete reads
1186+ */
1187+ protected boolean readMessage(AjpMessage message, boolean first,
1188+ boolean useAvailableData)
1189+ throws IOException {
1190+
1191+ byte[] buf = message.getBuffer();
1192+ int headerLength = message.getHeaderLength();
1193+
1194+ if (first) {
1195+ if (!readt(headerLength, useAvailableData)) {
1196+ return false;
1197+ }
1198+ } else {
1199+ read(headerLength);
1200+ }
1201+ inputBuffer.get(message.getBuffer(), 0, headerLength);
1202+ message.processHeader();
1203+ read(message.getLen());
1204+ inputBuffer.get(message.getBuffer(), headerLength, message.getLen());
1205+
1206+ return true;
1207+
1208+ }
1209+
1210+
1211+ /**
1212+ * Recycle the processor.
1213+ */
1214+ public void recycle() {
1215+
1216+ // Recycle Request object
1217+ first = true;
1218+ endOfStream = false;
1219+ empty = true;
1220+ replay = false;
1221+ finished = false;
1222+ request.recycle();
1223+ response.recycle();
1224+ certificates.recycle();
1225+
1226+ inputBuffer.clear();
1227+ inputBuffer.limit(0);
1228+ outputBuffer.clear();
1229+
1230+ }
1231+
1232+
1233+ /**
1234+ * Callback to write data from the buffer.
1235+ */
1236+ protected void flush()
1237+ throws IOException {
1238+ if (outputBuffer.position() > 0) {
1239+ if (Socket.sendbb(socket, 0, outputBuffer.position()) < 0) {
1240+ throw new IOException(sm.getString("ajpprocessor.failedsend"));
1241+ }
1242+ outputBuffer.clear();
1243+ }
1244+ }
1245+
1246+
1247+ // ------------------------------------- InputStreamInputBuffer Inner Class
1248+
1249+
1250+ /**
1251+ * This class is an input buffer which will read its data from an input
1252+ * stream.
1253+ */
1254+ protected class SocketInputBuffer
1255+ implements InputBuffer {
1256+
1257+
1258+ /**
1259+ * Read bytes into the specified chunk.
1260+ */
1261+ public int doRead(ByteChunk chunk, Request req )
1262+ throws IOException {
1263+
1264+ if (endOfStream) {
1265+ return -1;
1266+ }
1267+ if (first && req.getContentLengthLong() > 0) {
1268+ // Handle special first-body-chunk
1269+ if (!receive()) {
1270+ return 0;
1271+ }
1272+ } else if (empty) {
1273+ if (!refillReadBuffer()) {
1274+ return -1;
1275+ }
1276+ }
1277+ ByteChunk bc = bodyBytes.getByteChunk();
1278+ chunk.setBytes(bc.getBuffer(), bc.getStart(), bc.getLength());
1279+ empty = true;
1280+ return chunk.getLength();
1281+
1282+ }
1283+
1284+ }
1285+
1286+
1287+ // ----------------------------------- OutputStreamOutputBuffer Inner Class
1288+
1289+
1290+ /**
1291+ * This class is an output buffer which will write data to an output
1292+ * stream.
1293+ */
1294+ protected class SocketOutputBuffer
1295+ implements OutputBuffer {
1296+
1297+
1298+ /**
1299+ * Write chunk.
1300+ */
1301+ public int doWrite(ByteChunk chunk, Response res)
1302+ throws IOException {
1303+
1304+ if (!response.isCommitted()) {
1305+ // Validate and write response headers
1306+ try {
1307+ prepareResponse();
1308+ } catch (IOException e) {
1309+ // Set error flag
1310+ error = true;
1311+ }
1312+ }
1313+
1314+ int len = chunk.getLength();
1315+ // 4 - hardcoded, byte[] marshalling overhead
1316+ // Adjust allowed size if packetSize != default (Constants.MAX_PACKET_SIZE)
1317+ int chunkSize = Constants.MAX_SEND_SIZE + packetSize - Constants.MAX_PACKET_SIZE;
1318+ int off = 0;
1319+ while (len > 0) {
1320+ int thisTime = len;
1321+ if (thisTime > chunkSize) {
1322+ thisTime = chunkSize;
1323+ }
1324+ len -= thisTime;
1325+ if (outputBuffer.position() + thisTime +
1326+ Constants.H_SIZE + 4 > outputBuffer.capacity()) {
1327+ flush();
1328+ }
1329+ outputBuffer.put((byte) 0x41);
1330+ outputBuffer.put((byte) 0x42);
1331+ outputBuffer.putShort((short) (thisTime + 4));
1332+ outputBuffer.put(Constants.JK_AJP13_SEND_BODY_CHUNK);
1333+ outputBuffer.putShort((short) thisTime);
1334+ outputBuffer.put(chunk.getBytes(), chunk.getOffset() + off, thisTime);
1335+ outputBuffer.put((byte) 0x00);
1336+ off += thisTime;
1337+ }
1338+
1339+ return chunk.getLength();
1340+
1341+ }
1342+
1343+
1344+ }
1345+
1346+
1347+}
1348
1349=== added file '.pc/0013-CVE-2011-3190.patch/java/org/apache/coyote/ajp/AjpProcessor.java'
1350--- .pc/0013-CVE-2011-3190.patch/java/org/apache/coyote/ajp/AjpProcessor.java 1970-01-01 00:00:00 +0000
1351+++ .pc/0013-CVE-2011-3190.patch/java/org/apache/coyote/ajp/AjpProcessor.java 2011-09-15 08:37:39 +0000
1352@@ -0,0 +1,1269 @@
1353+/*
1354+ * Licensed to the Apache Software Foundation (ASF) under one or more
1355+ * contributor license agreements. See the NOTICE file distributed with
1356+ * this work for additional information regarding copyright ownership.
1357+ * The ASF licenses this file to You under the Apache License, Version 2.0
1358+ * (the "License"); you may not use this file except in compliance with
1359+ * the License. You may obtain a copy of the License at
1360+ *
1361+ * http://www.apache.org/licenses/LICENSE-2.0
1362+ *
1363+ * Unless required by applicable law or agreed to in writing, software
1364+ * distributed under the License is distributed on an "AS IS" BASIS,
1365+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1366+ * See the License for the specific language governing permissions and
1367+ * limitations under the License.
1368+ */
1369+
1370+package org.apache.coyote.ajp;
1371+
1372+import java.io.ByteArrayInputStream;
1373+import java.io.IOException;
1374+import java.io.InputStream;
1375+import java.io.InterruptedIOException;
1376+import java.io.OutputStream;
1377+import java.net.InetAddress;
1378+import java.net.Socket;
1379+import java.security.cert.CertificateFactory;
1380+import java.security.cert.X509Certificate;
1381+
1382+import org.apache.coyote.ActionCode;
1383+import org.apache.coyote.ActionHook;
1384+import org.apache.coyote.Adapter;
1385+import org.apache.coyote.InputBuffer;
1386+import org.apache.coyote.OutputBuffer;
1387+import org.apache.coyote.Request;
1388+import org.apache.coyote.RequestInfo;
1389+import org.apache.coyote.Response;
1390+import org.apache.tomcat.util.buf.ByteChunk;
1391+import org.apache.tomcat.util.buf.HexUtils;
1392+import org.apache.tomcat.util.buf.MessageBytes;
1393+import org.apache.tomcat.util.http.HttpMessages;
1394+import org.apache.tomcat.util.http.MimeHeaders;
1395+import org.apache.tomcat.util.net.JIoEndpoint;
1396+import org.apache.tomcat.util.res.StringManager;
1397+
1398+
1399+/**
1400+ * Processes HTTP requests.
1401+ *
1402+ * @author Remy Maucherat
1403+ * @author Henri Gomez
1404+ * @author Dan Milstein
1405+ * @author Keith Wannamaker
1406+ * @author Kevin Seguin
1407+ * @author Costin Manolache
1408+ * @author Bill Barker
1409+ */
1410+public class AjpProcessor implements ActionHook {
1411+
1412+
1413+ /**
1414+ * Logger.
1415+ */
1416+ protected static org.apache.juli.logging.Log log
1417+ = org.apache.juli.logging.LogFactory.getLog(AjpProcessor.class);
1418+
1419+ /**
1420+ * The string manager for this package.
1421+ */
1422+ protected static StringManager sm =
1423+ StringManager.getManager(Constants.Package);
1424+
1425+
1426+ // ----------------------------------------------------------- Constructors
1427+
1428+
1429+ public AjpProcessor(int packetSize, JIoEndpoint endpoint) {
1430+
1431+ this.endpoint = endpoint;
1432+
1433+ request = new Request();
1434+ request.setInputBuffer(new SocketInputBuffer());
1435+
1436+ response = new Response();
1437+ response.setHook(this);
1438+ response.setOutputBuffer(new SocketOutputBuffer());
1439+ request.setResponse(response);
1440+
1441+ this.packetSize = packetSize;
1442+ requestHeaderMessage = new AjpMessage(packetSize);
1443+ responseHeaderMessage = new AjpMessage(packetSize);
1444+ bodyMessage = new AjpMessage(packetSize);
1445+
1446+ // Set the get body message buffer
1447+ AjpMessage getBodyMessage = new AjpMessage(16);
1448+ getBodyMessage.reset();
1449+ getBodyMessage.appendByte(Constants.JK_AJP13_GET_BODY_CHUNK);
1450+ // Adjust allowed size if packetSize != default (Constants.MAX_PACKET_SIZE)
1451+ getBodyMessage.appendInt(Constants.MAX_READ_SIZE + packetSize - Constants.MAX_PACKET_SIZE);
1452+ getBodyMessage.end();
1453+ getBodyMessageArray = new byte[getBodyMessage.getLen()];
1454+ System.arraycopy(getBodyMessage.getBuffer(), 0, getBodyMessageArray,
1455+ 0, getBodyMessage.getLen());
1456+
1457+ // Cause loading of HexUtils
1458+ int foo = HexUtils.DEC[0];
1459+
1460+ // Cause loading of HttpMessages
1461+ HttpMessages.getMessage(200);
1462+
1463+ }
1464+
1465+
1466+ // ----------------------------------------------------- Instance Variables
1467+
1468+
1469+ /**
1470+ * Associated adapter.
1471+ */
1472+ protected Adapter adapter = null;
1473+
1474+
1475+ /**
1476+ * Request object.
1477+ */
1478+ protected Request request = null;
1479+
1480+
1481+ /**
1482+ * Response object.
1483+ */
1484+ protected Response response = null;
1485+
1486+
1487+ /**
1488+ * The socket timeout used when reading the first block of the request
1489+ * header.
1490+ */
1491+ protected int packetSize;
1492+
1493+ /**
1494+ * Header message. Note that this header is merely the one used during the
1495+ * processing of the first message of a "request", so it might not be a request
1496+ * header. It will stay unchanged during the processing of the whole request.
1497+ */
1498+ protected AjpMessage requestHeaderMessage = null;
1499+
1500+
1501+ /**
1502+ * Message used for response header composition.
1503+ */
1504+ protected AjpMessage responseHeaderMessage = null;
1505+
1506+
1507+ /**
1508+ * Body message.
1509+ */
1510+ protected AjpMessage bodyMessage = null;
1511+
1512+
1513+ /**
1514+ * Body message.
1515+ */
1516+ protected MessageBytes bodyBytes = MessageBytes.newInstance();
1517+
1518+
1519+ /**
1520+ * State flag.
1521+ */
1522+ protected boolean started = false;
1523+
1524+
1525+ /**
1526+ * Error flag.
1527+ */
1528+ protected boolean error = false;
1529+
1530+
1531+ /**
1532+ * Socket associated with the current connection.
1533+ */
1534+ protected Socket socket;
1535+
1536+
1537+ /**
1538+ * Input stream.
1539+ */
1540+ protected InputStream input;
1541+
1542+
1543+ /**
1544+ * Output stream.
1545+ */
1546+ protected OutputStream output;
1547+
1548+
1549+ /**
1550+ * Host name (used to avoid useless B2C conversion on the host name).
1551+ */
1552+ protected char[] hostNameC = new char[0];
1553+
1554+
1555+ /**
1556+ * Associated endpoint.
1557+ */
1558+ protected JIoEndpoint endpoint;
1559+
1560+
1561+ /**
1562+ * The socket timeout used when reading the first block of the request
1563+ * header.
1564+ */
1565+ protected long readTimeout;
1566+
1567+
1568+ /**
1569+ * Temp message bytes used for processing.
1570+ */
1571+ protected MessageBytes tmpMB = MessageBytes.newInstance();
1572+
1573+
1574+ /**
1575+ * Byte chunk for certs.
1576+ */
1577+ protected MessageBytes certificates = MessageBytes.newInstance();
1578+
1579+
1580+ /**
1581+ * End of stream flag.
1582+ */
1583+ protected boolean endOfStream = false;
1584+
1585+
1586+ /**
1587+ * Body empty flag.
1588+ */
1589+ protected boolean empty = true;
1590+
1591+
1592+ /**
1593+ * First read.
1594+ */
1595+ protected boolean first = true;
1596+
1597+
1598+ /**
1599+ * Replay read.
1600+ */
1601+ protected boolean replay = false;
1602+
1603+
1604+ /**
1605+ * Finished response.
1606+ */
1607+ protected boolean finished = false;
1608+
1609+
1610+ /**
1611+ * Direct buffer used for sending right away a get body message.
1612+ */
1613+ protected final byte[] getBodyMessageArray;
1614+
1615+
1616+ /**
1617+ * Direct buffer used for sending right away a pong message.
1618+ */
1619+ protected static final byte[] pongMessageArray;
1620+
1621+
1622+ /**
1623+ * End message array.
1624+ */
1625+ protected static final byte[] endMessageArray;
1626+
1627+ /**
1628+ * Flush message array.
1629+ */
1630+ protected static final byte[] flushMessageArray;
1631+
1632+
1633+ // ----------------------------------------------------- Static Initializer
1634+
1635+
1636+ static {
1637+
1638+ // Set the read body message buffer
1639+ AjpMessage pongMessage = new AjpMessage(16);
1640+ pongMessage.reset();
1641+ pongMessage.appendByte(Constants.JK_AJP13_CPONG_REPLY);
1642+ pongMessage.end();
1643+ pongMessageArray = new byte[pongMessage.getLen()];
1644+ System.arraycopy(pongMessage.getBuffer(), 0, pongMessageArray,
1645+ 0, pongMessage.getLen());
1646+
1647+ // Allocate the end message array
1648+ AjpMessage endMessage = new AjpMessage(16);
1649+ endMessage.reset();
1650+ endMessage.appendByte(Constants.JK_AJP13_END_RESPONSE);
1651+ endMessage.appendByte(1);
1652+ endMessage.end();
1653+ endMessageArray = new byte[endMessage.getLen()];
1654+ System.arraycopy(endMessage.getBuffer(), 0, endMessageArray, 0,
1655+ endMessage.getLen());
1656+
1657+ // Allocate the flush message array
1658+ AjpMessage flushMessage = new AjpMessage(16);
1659+ flushMessage.reset();
1660+ flushMessage.appendByte(Constants.JK_AJP13_SEND_BODY_CHUNK);
1661+ flushMessage.appendInt(0);
1662+ flushMessage.appendByte(0);
1663+ flushMessage.end();
1664+ flushMessageArray = new byte[flushMessage.getLen()];
1665+ System.arraycopy(flushMessage.getBuffer(), 0, flushMessageArray, 0,
1666+ flushMessage.getLen());
1667+
1668+ }
1669+
1670+
1671+ // ------------------------------------------------------------- Properties
1672+
1673+
1674+ /**
1675+ * Use Tomcat authentication ?
1676+ */
1677+ protected boolean tomcatAuthentication = true;
1678+ public boolean getTomcatAuthentication() { return tomcatAuthentication; }
1679+ public void setTomcatAuthentication(boolean tomcatAuthentication) { this.tomcatAuthentication = tomcatAuthentication; }
1680+
1681+
1682+ /**
1683+ * Required secret.
1684+ */
1685+ protected String requiredSecret = null;
1686+ public void setRequiredSecret(String requiredSecret) { this.requiredSecret = requiredSecret; }
1687+
1688+
1689+ /**
1690+ * The number of milliseconds Tomcat will wait for a subsequent request
1691+ * before closing the connection. The default is the same as for
1692+ * Apache HTTP Server (15 000 milliseconds).
1693+ */
1694+ protected int keepAliveTimeout = -1;
1695+ public int getKeepAliveTimeout() { return keepAliveTimeout; }
1696+ public void setKeepAliveTimeout(int timeout) { keepAliveTimeout = timeout; }
1697+
1698+
1699+ // --------------------------------------------------------- Public Methods
1700+
1701+
1702+ /** Get the request associated with this processor.
1703+ *
1704+ * @return The request
1705+ */
1706+ public Request getRequest() {
1707+ return request;
1708+ }
1709+
1710+
1711+ /**
1712+ * Process pipelined HTTP requests using the specified input and output
1713+ * streams.
1714+ *
1715+ * @throws IOException error during an I/O operation
1716+ */
1717+ public void process(Socket socket)
1718+ throws IOException {
1719+ RequestInfo rp = request.getRequestProcessor();
1720+ rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
1721+
1722+ // Setting up the socket
1723+ this.socket = socket;
1724+ input = socket.getInputStream();
1725+ output = socket.getOutputStream();
1726+ int soTimeout = -1;
1727+ if (keepAliveTimeout > 0) {
1728+ soTimeout = socket.getSoTimeout();
1729+ }
1730+
1731+ // Error flag
1732+ error = false;
1733+
1734+ while (started && !error) {
1735+
1736+ // Parsing the request header
1737+ try {
1738+ // Set keep alive timeout if enabled
1739+ if (keepAliveTimeout > 0) {
1740+ socket.setSoTimeout(keepAliveTimeout);
1741+ }
1742+ // Get first message of the request
1743+ if (!readMessage(requestHeaderMessage)) {
1744+ // This means a connection timeout
1745+ rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
1746+ break;
1747+ }
1748+ // Set back timeout if keep alive timeout is enabled
1749+ if (keepAliveTimeout > 0) {
1750+ socket.setSoTimeout(soTimeout);
1751+ }
1752+ // Check message type, process right away and break if
1753+ // not regular request processing
1754+ int type = requestHeaderMessage.getByte();
1755+ if (type == Constants.JK_AJP13_CPING_REQUEST) {
1756+ try {
1757+ output.write(pongMessageArray);
1758+ } catch (IOException e) {
1759+ error = true;
1760+ }
1761+ continue;
1762+ } else if(type != Constants.JK_AJP13_FORWARD_REQUEST) {
1763+ // Usually the servlet didn't read the previous request body
1764+ if(log.isDebugEnabled()) {
1765+ log.debug("Unexpected message: "+type);
1766+ }
1767+ continue;
1768+ }
1769+
1770+ request.setStartTime(System.currentTimeMillis());
1771+ } catch (IOException e) {
1772+ error = true;
1773+ break;
1774+ } catch (Throwable t) {
1775+ log.debug(sm.getString("ajpprocessor.header.error"), t);
1776+ // 400 - Bad Request
1777+ response.setStatus(400);
1778+ adapter.log(request, response, 0);
1779+ error = true;
1780+ }
1781+
1782+ // Setting up filters, and parse some request headers
1783+ rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
1784+ try {
1785+ prepareRequest();
1786+ } catch (Throwable t) {
1787+ log.debug(sm.getString("ajpprocessor.request.prepare"), t);
1788+ // 400 - Internal Server Error
1789+ response.setStatus(400);
1790+ adapter.log(request, response, 0);
1791+ error = true;
1792+ }
1793+
1794+ // Process the request in the adapter
1795+ if (!error) {
1796+ try {
1797+ rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
1798+ adapter.service(request, response);
1799+ } catch (InterruptedIOException e) {
1800+ error = true;
1801+ } catch (Throwable t) {
1802+ log.error(sm.getString("ajpprocessor.request.process"), t);
1803+ // 500 - Internal Server Error
1804+ response.setStatus(500);
1805+ adapter.log(request, response, 0);
1806+ error = true;
1807+ }
1808+ }
1809+
1810+ // Finish the response if not done yet
1811+ if (!finished) {
1812+ try {
1813+ finish();
1814+ } catch (Throwable t) {
1815+ error = true;
1816+ }
1817+ }
1818+
1819+ // If there was an error, make sure the request is counted as
1820+ // and error, and update the statistics counter
1821+ if (error) {
1822+ response.setStatus(500);
1823+ }
1824+ request.updateCounters();
1825+
1826+ rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
1827+ recycle();
1828+
1829+ }
1830+
1831+ rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
1832+ recycle();
1833+ input = null;
1834+ output = null;
1835+
1836+ }
1837+
1838+
1839+ // ----------------------------------------------------- ActionHook Methods
1840+
1841+
1842+ /**
1843+ * Send an action to the connector.
1844+ *
1845+ * @param actionCode Type of the action
1846+ * @param param Action parameter
1847+ */
1848+ public void action(ActionCode actionCode, Object param) {
1849+
1850+ if (actionCode == ActionCode.ACTION_COMMIT) {
1851+
1852+ if (response.isCommitted())
1853+ return;
1854+
1855+ // Validate and write response headers
1856+ try {
1857+ prepareResponse();
1858+ } catch (IOException e) {
1859+ // Set error flag
1860+ error = true;
1861+ }
1862+
1863+ } else if (actionCode == ActionCode.ACTION_CLIENT_FLUSH) {
1864+
1865+ if (!response.isCommitted()) {
1866+ // Validate and write response headers
1867+ try {
1868+ prepareResponse();
1869+ } catch (IOException e) {
1870+ // Set error flag
1871+ error = true;
1872+ return;
1873+ }
1874+ }
1875+
1876+ try {
1877+ flush();
1878+ } catch (IOException e) {
1879+ // Set error flag
1880+ error = true;
1881+ }
1882+
1883+ } else if (actionCode == ActionCode.ACTION_CLOSE) {
1884+ // Close
1885+
1886+ // End the processing of the current request, and stop any further
1887+ // transactions with the client
1888+
1889+ try {
1890+ finish();
1891+ } catch (IOException e) {
1892+ // Set error flag
1893+ error = true;
1894+ }
1895+
1896+ } else if (actionCode == ActionCode.ACTION_START) {
1897+
1898+ started = true;
1899+
1900+ } else if (actionCode == ActionCode.ACTION_STOP) {
1901+
1902+ started = false;
1903+
1904+ } else if (actionCode == ActionCode.ACTION_REQ_SSL_ATTRIBUTE ) {
1905+
1906+ if (!certificates.isNull()) {
1907+ ByteChunk certData = certificates.getByteChunk();
1908+ X509Certificate jsseCerts[] = null;
1909+ ByteArrayInputStream bais =
1910+ new ByteArrayInputStream(certData.getBytes(),
1911+ certData.getStart(),
1912+ certData.getLength());
1913+ // Fill the elements.
1914+ try {
1915+ CertificateFactory cf =
1916+ CertificateFactory.getInstance("X.509");
1917+ while(bais.available() > 0) {
1918+ X509Certificate cert = (X509Certificate)
1919+ cf.generateCertificate(bais);
1920+ if(jsseCerts == null) {
1921+ jsseCerts = new X509Certificate[1];
1922+ jsseCerts[0] = cert;
1923+ } else {
1924+ X509Certificate [] temp = new X509Certificate[jsseCerts.length+1];
1925+ System.arraycopy(jsseCerts,0,temp,0,jsseCerts.length);
1926+ temp[jsseCerts.length] = cert;
1927+ jsseCerts = temp;
1928+ }
1929+ }
1930+ } catch (java.security.cert.CertificateException e) {
1931+ log.error(sm.getString("ajpprocessor.certs.fail"), e);
1932+ return;
1933+ }
1934+ request.setAttribute(JIoEndpoint.CERTIFICATE_KEY, jsseCerts);
1935+ }
1936+
1937+ } else if (actionCode == ActionCode.ACTION_REQ_HOST_ATTRIBUTE) {
1938+
1939+ // Get remote host name using a DNS resolution
1940+ if (request.remoteHost().isNull()) {
1941+ try {
1942+ request.remoteHost().setString(InetAddress.getByName
1943+ (request.remoteAddr().toString()).getHostName());
1944+ } catch (IOException iex) {
1945+ // Ignore
1946+ }
1947+ }
1948+
1949+ } else if (actionCode == ActionCode.ACTION_REQ_LOCAL_ADDR_ATTRIBUTE) {
1950+
1951+ // Copy from local name for now, which should simply be an address
1952+ request.localAddr().setString(request.localName().toString());
1953+
1954+ } else if (actionCode == ActionCode.ACTION_REQ_SET_BODY_REPLAY) {
1955+
1956+ // Set the given bytes as the content
1957+ ByteChunk bc = (ByteChunk) param;
1958+ int length = bc.getLength();
1959+ bodyBytes.setBytes(bc.getBytes(), bc.getStart(), length);
1960+ request.setContentLength(length);
1961+ first = false;
1962+ empty = false;
1963+ replay = true;
1964+
1965+ }
1966+
1967+
1968+ }
1969+
1970+
1971+ // ------------------------------------------------------ Connector Methods
1972+
1973+
1974+ /**
1975+ * Set the associated adapter.
1976+ *
1977+ * @param adapter the new adapter
1978+ */
1979+ public void setAdapter(Adapter adapter) {
1980+ this.adapter = adapter;
1981+ }
1982+
1983+
1984+ /**
1985+ * Get the associated adapter.
1986+ *
1987+ * @return the associated adapter
1988+ */
1989+ public Adapter getAdapter() {
1990+ return adapter;
1991+ }
1992+
1993+
1994+ // ------------------------------------------------------ Protected Methods
1995+
1996+
1997+ /**
1998+ * After reading the request headers, we have to setup the request filters.
1999+ */
2000+ protected void prepareRequest() {
2001+
2002+ // Translate the HTTP method code to a String.
2003+ byte methodCode = requestHeaderMessage.getByte();
2004+ if (methodCode != Constants.SC_M_JK_STORED) {
2005+ String methodName = Constants.methodTransArray[(int)methodCode - 1];
2006+ request.method().setString(methodName);
2007+ }
2008+
2009+ requestHeaderMessage.getBytes(request.protocol());
2010+ requestHeaderMessage.getBytes(request.requestURI());
2011+
2012+ requestHeaderMessage.getBytes(request.remoteAddr());
2013+ requestHeaderMessage.getBytes(request.remoteHost());
2014+ requestHeaderMessage.getBytes(request.localName());
2015+ request.setLocalPort(requestHeaderMessage.getInt());
2016+
2017+ boolean isSSL = requestHeaderMessage.getByte() != 0;
2018+ if (isSSL) {
2019+ request.scheme().setString("https");
2020+ }
2021+
2022+ // Decode headers
2023+ MimeHeaders headers = request.getMimeHeaders();
2024+
2025+ int hCount = requestHeaderMessage.getInt();
2026+ for(int i = 0 ; i < hCount ; i++) {
2027+ String hName = null;
2028+
2029+ // Header names are encoded as either an integer code starting
2030+ // with 0xA0, or as a normal string (in which case the first
2031+ // two bytes are the length).
2032+ int isc = requestHeaderMessage.peekInt();
2033+ int hId = isc & 0xFF;
2034+
2035+ MessageBytes vMB = null;
2036+ isc &= 0xFF00;
2037+ if(0xA000 == isc) {
2038+ requestHeaderMessage.getInt(); // To advance the read position
2039+ hName = Constants.headerTransArray[hId - 1];
2040+ vMB = headers.addValue(hName);
2041+ } else {
2042+ // reset hId -- if the header currently being read
2043+ // happens to be 7 or 8 bytes long, the code below
2044+ // will think it's the content-type header or the
2045+ // content-length header - SC_REQ_CONTENT_TYPE=7,
2046+ // SC_REQ_CONTENT_LENGTH=8 - leading to unexpected
2047+ // behaviour. see bug 5861 for more information.
2048+ hId = -1;
2049+ requestHeaderMessage.getBytes(tmpMB);
2050+ ByteChunk bc = tmpMB.getByteChunk();
2051+ vMB = headers.addValue(bc.getBuffer(),
2052+ bc.getStart(), bc.getLength());
2053+ }
2054+
2055+ requestHeaderMessage.getBytes(vMB);
2056+
2057+ if (hId == Constants.SC_REQ_CONTENT_LENGTH ||
2058+ (hId == -1 && tmpMB.equalsIgnoreCase("Content-Length"))) {
2059+ // just read the content-length header, so set it
2060+ long cl = vMB.getLong();
2061+ if(cl < Integer.MAX_VALUE)
2062+ request.setContentLength( (int)cl );
2063+ } else if (hId == Constants.SC_REQ_CONTENT_TYPE ||
2064+ (hId == -1 && tmpMB.equalsIgnoreCase("Content-Type"))) {
2065+ // just read the content-type header, so set it
2066+ ByteChunk bchunk = vMB.getByteChunk();
2067+ request.contentType().setBytes(bchunk.getBytes(),
2068+ bchunk.getOffset(),
2069+ bchunk.getLength());
2070+ }
2071+ }
2072+
2073+ // Decode extra attributes
2074+ boolean secret = false;
2075+ byte attributeCode;
2076+ while ((attributeCode = requestHeaderMessage.getByte())
2077+ != Constants.SC_A_ARE_DONE) {
2078+
2079+ switch (attributeCode) {
2080+
2081+ case Constants.SC_A_REQ_ATTRIBUTE :
2082+ requestHeaderMessage.getBytes(tmpMB);
2083+ String n = tmpMB.toString();
2084+ requestHeaderMessage.getBytes(tmpMB);
2085+ String v = tmpMB.toString();
2086+ /*
2087+ * AJP13 misses to forward the remotePort.
2088+ * Allow the AJP connector to add this info via
2089+ * a private request attribute.
2090+ * We will accept the forwarded data as the remote port,
2091+ * and remove it from the public list of request attributes.
2092+ */
2093+ if(n.equals(Constants.SC_A_REQ_REMOTE_PORT)) {
2094+ try {
2095+ request.setRemotePort(Integer.parseInt(v));
2096+ } catch (NumberFormatException nfe) {
2097+ }
2098+ } else {
2099+ request.setAttribute(n, v );
2100+ }
2101+ break;
2102+
2103+ case Constants.SC_A_CONTEXT :
2104+ requestHeaderMessage.getBytes(tmpMB);
2105+ // nothing
2106+ break;
2107+
2108+ case Constants.SC_A_SERVLET_PATH :
2109+ requestHeaderMessage.getBytes(tmpMB);
2110+ // nothing
2111+ break;
2112+
2113+ case Constants.SC_A_REMOTE_USER :
2114+ if (tomcatAuthentication) {
2115+ // ignore server
2116+ requestHeaderMessage.getBytes(tmpMB);
2117+ } else {
2118+ requestHeaderMessage.getBytes(request.getRemoteUser());
2119+ }
2120+ break;
2121+
2122+ case Constants.SC_A_AUTH_TYPE :
2123+ if (tomcatAuthentication) {
2124+ // ignore server
2125+ requestHeaderMessage.getBytes(tmpMB);
2126+ } else {
2127+ requestHeaderMessage.getBytes(request.getAuthType());
2128+ }
2129+ break;
2130+
2131+ case Constants.SC_A_QUERY_STRING :
2132+ requestHeaderMessage.getBytes(request.queryString());
2133+ break;
2134+
2135+ case Constants.SC_A_JVM_ROUTE :
2136+ requestHeaderMessage.getBytes(request.instanceId());
2137+ break;
2138+
2139+ case Constants.SC_A_SSL_CERT :
2140+ request.scheme().setString("https");
2141+ // SSL certificate extraction is lazy, moved to JkCoyoteHandler
2142+ requestHeaderMessage.getBytes(certificates);
2143+ break;
2144+
2145+ case Constants.SC_A_SSL_CIPHER :
2146+ request.scheme().setString("https");
2147+ requestHeaderMessage.getBytes(tmpMB);
2148+ request.setAttribute(JIoEndpoint.CIPHER_SUITE_KEY,
2149+ tmpMB.toString());
2150+ break;
2151+
2152+ case Constants.SC_A_SSL_SESSION :
2153+ request.scheme().setString("https");
2154+ requestHeaderMessage.getBytes(tmpMB);
2155+ request.setAttribute(JIoEndpoint.SESSION_ID_KEY,
2156+ tmpMB.toString());
2157+ break;
2158+
2159+ case Constants.SC_A_SSL_KEY_SIZE :
2160+ request.setAttribute(JIoEndpoint.KEY_SIZE_KEY,
2161+ new Integer(requestHeaderMessage.getInt()));
2162+ break;
2163+
2164+ case Constants.SC_A_STORED_METHOD:
2165+ requestHeaderMessage.getBytes(request.method());
2166+ break;
2167+
2168+ case Constants.SC_A_SECRET:
2169+ requestHeaderMessage.getBytes(tmpMB);
2170+ if (requiredSecret != null) {
2171+ secret = true;
2172+ if (!tmpMB.equals(requiredSecret)) {
2173+ response.setStatus(403);
2174+ adapter.log(request, response, 0);
2175+ error = true;
2176+ }
2177+ }
2178+ break;
2179+
2180+ default:
2181+ // Ignore unknown attribute for backward compatibility
2182+ break;
2183+
2184+ }
2185+
2186+ }
2187+
2188+ // Check if secret was submitted if required
2189+ if ((requiredSecret != null) && !secret) {
2190+ response.setStatus(403);
2191+ adapter.log(request, response, 0);
2192+ error = true;
2193+ }
2194+
2195+ // Check for a full URI (including protocol://host:port/)
2196+ ByteChunk uriBC = request.requestURI().getByteChunk();
2197+ if (uriBC.startsWithIgnoreCase("http", 0)) {
2198+
2199+ int pos = uriBC.indexOf("://", 0, 3, 4);
2200+ int uriBCStart = uriBC.getStart();
2201+ int slashPos = -1;
2202+ if (pos != -1) {
2203+ byte[] uriB = uriBC.getBytes();
2204+ slashPos = uriBC.indexOf('/', pos + 3);
2205+ if (slashPos == -1) {
2206+ slashPos = uriBC.getLength();
2207+ // Set URI as "/"
2208+ request.requestURI().setBytes
2209+ (uriB, uriBCStart + pos + 1, 1);
2210+ } else {
2211+ request.requestURI().setBytes
2212+ (uriB, uriBCStart + slashPos,
2213+ uriBC.getLength() - slashPos);
2214+ }
2215+ MessageBytes hostMB = headers.setValue("host");
2216+ hostMB.setBytes(uriB, uriBCStart + pos + 3,
2217+ slashPos - pos - 3);
2218+ }
2219+
2220+ }
2221+
2222+ MessageBytes valueMB = request.getMimeHeaders().getValue("host");
2223+ parseHost(valueMB);
2224+
2225+ }
2226+
2227+
2228+ /**
2229+ * Parse host.
2230+ */
2231+ public void parseHost(MessageBytes valueMB) {
2232+
2233+ if (valueMB == null || (valueMB != null && valueMB.isNull()) ) {
2234+ // HTTP/1.0
2235+ request.setServerPort(request.getLocalPort());
2236+ try {
2237+ request.serverName().duplicate(request.localName());
2238+ } catch (IOException e) {
2239+ response.setStatus(400);
2240+ adapter.log(request, response, 0);
2241+ error = true;
2242+ }
2243+ return;
2244+ }
2245+
2246+ ByteChunk valueBC = valueMB.getByteChunk();
2247+ byte[] valueB = valueBC.getBytes();
2248+ int valueL = valueBC.getLength();
2249+ int valueS = valueBC.getStart();
2250+ int colonPos = -1;
2251+ if (hostNameC.length < valueL) {
2252+ hostNameC = new char[valueL];
2253+ }
2254+
2255+ boolean ipv6 = (valueB[valueS] == '[');
2256+ boolean bracketClosed = false;
2257+ for (int i = 0; i < valueL; i++) {
2258+ char b = (char) valueB[i + valueS];
2259+ hostNameC[i] = b;
2260+ if (b == ']') {
2261+ bracketClosed = true;
2262+ } else if (b == ':') {
2263+ if (!ipv6 || bracketClosed) {
2264+ colonPos = i;
2265+ break;
2266+ }
2267+ }
2268+ }
2269+
2270+ if (colonPos < 0) {
2271+ if (request.scheme().equalsIgnoreCase("https")) {
2272+ // 443 - Default HTTPS port
2273+ request.setServerPort(443);
2274+ } else {
2275+ // 80 - Default HTTTP port
2276+ request.setServerPort(80);
2277+ }
2278+ request.serverName().setChars(hostNameC, 0, valueL);
2279+ } else {
2280+
2281+ request.serverName().setChars(hostNameC, 0, colonPos);
2282+
2283+ int port = 0;
2284+ int mult = 1;
2285+ for (int i = valueL - 1; i > colonPos; i--) {
2286+ int charValue = HexUtils.DEC[(int) valueB[i + valueS]];
2287+ if (charValue == -1) {
2288+ // Invalid character
2289+ error = true;
2290+ // 400 - Bad request
2291+ response.setStatus(400);
2292+ adapter.log(request, response, 0);
2293+ break;
2294+ }
2295+ port = port + (charValue * mult);
2296+ mult = 10 * mult;
2297+ }
2298+ request.setServerPort(port);
2299+
2300+ }
2301+
2302+ }
2303+
2304+
2305+ /**
2306+ * When committing the response, we have to validate the set of headers, as
2307+ * well as setup the response filters.
2308+ */
2309+ protected void prepareResponse()
2310+ throws IOException {
2311+
2312+ response.setCommitted(true);
2313+
2314+ responseHeaderMessage.reset();
2315+ responseHeaderMessage.appendByte(Constants.JK_AJP13_SEND_HEADERS);
2316+
2317+ // HTTP header contents
2318+ responseHeaderMessage.appendInt(response.getStatus());
2319+ String message = null;
2320+ if (org.apache.coyote.Constants.USE_CUSTOM_STATUS_MSG_IN_HEADER &&
2321+ HttpMessages.isSafeInHttpHeader(response.getMessage())) {
2322+ message = response.getMessage();
2323+ }
2324+ if (message == null){
2325+ message = HttpMessages.getMessage(response.getStatus());
2326+ }
2327+ if (message == null) {
2328+ // mod_jk + httpd 2.x fails with a null status message - bug 45026
2329+ message = Integer.toString(response.getStatus());
2330+ }
2331+ tmpMB.setString(message);
2332+ responseHeaderMessage.appendBytes(tmpMB);
2333+
2334+ // Special headers
2335+ MimeHeaders headers = response.getMimeHeaders();
2336+ String contentType = response.getContentType();
2337+ if (contentType != null) {
2338+ headers.setValue("Content-Type").setString(contentType);
2339+ }
2340+ String contentLanguage = response.getContentLanguage();
2341+ if (contentLanguage != null) {
2342+ headers.setValue("Content-Language").setString(contentLanguage);
2343+ }
2344+ long contentLength = response.getContentLengthLong();
2345+ if (contentLength >= 0) {
2346+ headers.setValue("Content-Length").setLong(contentLength);
2347+ }
2348+
2349+ // Other headers
2350+ int numHeaders = headers.size();
2351+ responseHeaderMessage.appendInt(numHeaders);
2352+ for (int i = 0; i < numHeaders; i++) {
2353+ MessageBytes hN = headers.getName(i);
2354+ int hC = Constants.getResponseAjpIndex(hN.toString());
2355+ if (hC > 0) {
2356+ responseHeaderMessage.appendInt(hC);
2357+ }
2358+ else {
2359+ responseHeaderMessage.appendBytes(hN);
2360+ }
2361+ MessageBytes hV=headers.getValue(i);
2362+ responseHeaderMessage.appendBytes(hV);
2363+ }
2364+
2365+ // Write to buffer
2366+ responseHeaderMessage.end();
2367+ output.write(responseHeaderMessage.getBuffer(), 0, responseHeaderMessage.getLen());
2368+
2369+ }
2370+
2371+
2372+ /**
2373+ * Finish AJP response.
2374+ */
2375+ protected void finish()
2376+ throws IOException {
2377+
2378+ if (!response.isCommitted()) {
2379+ // Validate and write response headers
2380+ try {
2381+ prepareResponse();
2382+ } catch (IOException e) {
2383+ // Set error flag
2384+ error = true;
2385+ }
2386+ }
2387+
2388+ if (finished)
2389+ return;
2390+
2391+ finished = true;
2392+
2393+ // Add the end message
2394+ output.write(endMessageArray);
2395+
2396+ }
2397+
2398+
2399+ /**
2400+ * Read at least the specified amount of bytes, and place them
2401+ * in the input buffer.
2402+ */
2403+ protected boolean read(byte[] buf, int pos, int n)
2404+ throws IOException {
2405+
2406+ int read = 0;
2407+ int res = 0;
2408+ while (read < n) {
2409+ res = input.read(buf, read + pos, n - read);
2410+ if (res > 0) {
2411+ read += res;
2412+ } else {
2413+ throw new IOException(sm.getString("ajpprotocol.failedread"));
2414+ }
2415+ }
2416+
2417+ return true;
2418+
2419+ }
2420+
2421+
2422+ /** Receive a chunk of data. Called to implement the
2423+ * 'special' packet in ajp13 and to receive the data
2424+ * after we send a GET_BODY packet
2425+ */
2426+ public boolean receive() throws IOException {
2427+
2428+ first = false;
2429+ bodyMessage.reset();
2430+ readMessage(bodyMessage);
2431+
2432+ // No data received.
2433+ if (bodyMessage.getLen() == 0) {
2434+ // just the header
2435+ // Don't mark 'end of stream' for the first chunk.
2436+ return false;
2437+ }
2438+ int blen = bodyMessage.peekInt();
2439+ if (blen == 0) {
2440+ return false;
2441+ }
2442+
2443+ bodyMessage.getBytes(bodyBytes);
2444+ empty = false;
2445+ return true;
2446+ }
2447+
2448+ /**
2449+ * Get more request body data from the web server and store it in the
2450+ * internal buffer.
2451+ *
2452+ * @return true if there is more data, false if not.
2453+ */
2454+ private boolean refillReadBuffer() throws IOException {
2455+ // If the server returns an empty packet, assume that that end of
2456+ // the stream has been reached (yuck -- fix protocol??).
2457+ // FORM support
2458+ if (replay) {
2459+ endOfStream = true; // we've read everything there is
2460+ }
2461+ if (endOfStream) {
2462+ return false;
2463+ }
2464+
2465+ // Request more data immediately
2466+ output.write(getBodyMessageArray);
2467+
2468+ boolean moreData = receive();
2469+ if( !moreData ) {
2470+ endOfStream = true;
2471+ }
2472+ return moreData;
2473+ }
2474+
2475+
2476+ /**
2477+ * Read an AJP message.
2478+ *
2479+ * @return true if the message has been read, false if the short read
2480+ * didn't return anything
2481+ * @throws IOException any other failure, including incomplete reads
2482+ */
2483+ protected boolean readMessage(AjpMessage message)
2484+ throws IOException {
2485+
2486+ byte[] buf = message.getBuffer();
2487+
2488+ read(buf, 0, message.getHeaderLength());
2489+
2490+ message.processHeader();
2491+ read(buf, message.getHeaderLength(), message.getLen());
2492+
2493+ return true;
2494+
2495+ }
2496+
2497+
2498+ /**
2499+ * Recycle the processor.
2500+ */
2501+ public void recycle() {
2502+
2503+ // Recycle Request object
2504+ first = true;
2505+ endOfStream = false;
2506+ empty = true;
2507+ replay = false;
2508+ finished = false;
2509+ request.recycle();
2510+ response.recycle();
2511+ certificates.recycle();
2512+
2513+ }
2514+
2515+
2516+ /**
2517+ * Callback to write data from the buffer.
2518+ */
2519+ protected void flush()
2520+ throws IOException {
2521+ // Send the flush message
2522+ output.write(flushMessageArray);
2523+ }
2524+
2525+
2526+ // ------------------------------------- InputStreamInputBuffer Inner Class
2527+
2528+
2529+ /**
2530+ * This class is an input buffer which will read its data from an input
2531+ * stream.
2532+ */
2533+ protected class SocketInputBuffer
2534+ implements InputBuffer {
2535+
2536+
2537+ /**
2538+ * Read bytes into the specified chunk.
2539+ */
2540+ public int doRead(ByteChunk chunk, Request req )
2541+ throws IOException {
2542+
2543+ if (endOfStream) {
2544+ return -1;
2545+ }
2546+ if (first && req.getContentLengthLong() > 0) {
2547+ // Handle special first-body-chunk
2548+ if (!receive()) {
2549+ return 0;
2550+ }
2551+ } else if (empty) {
2552+ if (!refillReadBuffer()) {
2553+ return -1;
2554+ }
2555+ }
2556+ ByteChunk bc = bodyBytes.getByteChunk();
2557+ chunk.setBytes(bc.getBuffer(), bc.getStart(), bc.getLength());
2558+ empty = true;
2559+ return chunk.getLength();
2560+
2561+ }
2562+
2563+ }
2564+
2565+
2566+ // ----------------------------------- OutputStreamOutputBuffer Inner Class
2567+
2568+
2569+ /**
2570+ * This class is an output buffer which will write data to an output
2571+ * stream.
2572+ */
2573+ protected class SocketOutputBuffer
2574+ implements OutputBuffer {
2575+
2576+
2577+ /**
2578+ * Write chunk.
2579+ */
2580+ public int doWrite(ByteChunk chunk, Response res)
2581+ throws IOException {
2582+
2583+ if (!response.isCommitted()) {
2584+ // Validate and write response headers
2585+ try {
2586+ prepareResponse();
2587+ } catch (IOException e) {
2588+ // Set error flag
2589+ error = true;
2590+ }
2591+ }
2592+
2593+ int len = chunk.getLength();
2594+ // 4 - hardcoded, byte[] marshalling overhead
2595+ // Adjust allowed size if packetSize != default (Constants.MAX_PACKET_SIZE)
2596+ int chunkSize = Constants.MAX_SEND_SIZE + packetSize - Constants.MAX_PACKET_SIZE;
2597+ int off = 0;
2598+ while (len > 0) {
2599+ int thisTime = len;
2600+ if (thisTime > chunkSize) {
2601+ thisTime = chunkSize;
2602+ }
2603+ len -= thisTime;
2604+ responseHeaderMessage.reset();
2605+ responseHeaderMessage.appendByte(Constants.JK_AJP13_SEND_BODY_CHUNK);
2606+ responseHeaderMessage.appendBytes(chunk.getBytes(), chunk.getOffset() + off, thisTime);
2607+ responseHeaderMessage.end();
2608+ output.write(responseHeaderMessage.getBuffer(), 0, responseHeaderMessage.getLen());
2609+
2610+ off += thisTime;
2611+ }
2612+
2613+ return chunk.getLength();
2614+
2615+ }
2616+
2617+
2618+ }
2619+
2620+
2621+}
2622
2623=== modified file '.pc/applied-patches'
2624--- .pc/applied-patches 2011-07-06 21:23:58 +0000
2625+++ .pc/applied-patches 2011-09-15 08:37:39 +0000
2626@@ -9,3 +9,4 @@
2627 0010-Use-java.security.policy-file-in-catalina.sh.patch
2628 0011-623242.patch
2629 0012-CVE-2011-2204.patch
2630+0013-CVE-2011-3190.patch
2631
2632=== modified file 'debian/changelog'
2633--- debian/changelog 2011-07-06 21:23:58 +0000
2634+++ debian/changelog 2011-09-15 08:37:39 +0000
2635@@ -1,3 +1,9 @@
2636+tomcat6 (6.0.32-5ubuntu1) oneiric; urgency=low
2637+
2638+ * Added patch for CVE-2011-3190 (LP: #843701).
2639+
2640+ -- James Page <james.page@ubuntu.com> Thu, 08 Sep 2011 14:45:34 +0100
2641+
2642 tomcat6 (6.0.32-5) unstable; urgency=low
2643
2644 * Team upload.
2645
2646=== modified file 'debian/control'
2647--- debian/control 2011-07-06 21:23:58 +0000
2648+++ debian/control 2011-09-15 08:37:39 +0000
2649@@ -1,7 +1,8 @@
2650 Source: tomcat6
2651 Section: java
2652 Priority: optional
2653-Maintainer: Debian Java Maintainers <pkg-java-maintainers@lists.alioth.debian.org>
2654+Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
2655+XSBC-Original-Maintainer: Debian Java Maintainers <pkg-java-maintainers@lists.alioth.debian.org>
2656 Uploaders: Torsten Werner <twerner@debian.org>,
2657 Ludovic Claude <ludovic.claude@laposte.net>, Michael Koch <konqueror@gmx.de>,
2658 Damien Raude-Morvan <drazzib@debian.org>, Niels Thykier <niels@thykier.net>
2659
2660=== added file 'debian/patches/0013-CVE-2011-3190.patch'
2661--- debian/patches/0013-CVE-2011-3190.patch 1970-01-01 00:00:00 +0000
2662+++ debian/patches/0013-CVE-2011-3190.patch 2011-09-15 08:37:39 +0000
2663@@ -0,0 +1,72 @@
2664+Description: [PATCH] Fix CVE-2011-3190 Fix
2665+ https://issues.apache.org/bugzilla/show_bug.cgi?id=51698
2666+ Prevent AJP request forgery via unread request body packet
2667+Origin: https://github.com/apache/tomcat60/commit/1a04877e07c8ac9f924b130cbc372a11c273de66
2668+
2669+Index: tomcat6/java/org/apache/coyote/ajp/AjpAprProcessor.java
2670+===================================================================
2671+--- tomcat6.orig/java/org/apache/coyote/ajp/AjpAprProcessor.java 2011-09-08 14:25:11.619833000 +0100
2672++++ tomcat6/java/org/apache/coyote/ajp/AjpAprProcessor.java 2011-09-08 14:44:12.771697501 +0100
2673+@@ -390,11 +390,13 @@
2674+ }
2675+ continue;
2676+ } else if(type != Constants.JK_AJP13_FORWARD_REQUEST) {
2677+- // Usually the servlet didn't read the previous request body
2678+- if(log.isDebugEnabled()) {
2679+- log.debug("Unexpected message: "+type);
2680++ // Unexpected packet type. Unread body packets should have
2681++ // been swallowed in finish().
2682++ if (log.isDebugEnabled()) {
2683++ log.debug("Unexpected message: " + type);
2684+ }
2685+- continue;
2686++ error = true;
2687++ break;
2688+ }
2689+
2690+ keptAlive = true;
2691+@@ -1033,6 +1035,11 @@
2692+
2693+ finished = true;
2694+
2695++ // Swallow the unread body packet if present
2696++ if (first && request.getContentLengthLong() > 0) {
2697++ receive();
2698++ }
2699++
2700+ // Add the end message
2701+ if (outputBuffer.position() + endMessageArray.length > outputBuffer.capacity()) {
2702+ flush();
2703+Index: tomcat6/java/org/apache/coyote/ajp/AjpProcessor.java
2704+===================================================================
2705+--- tomcat6.orig/java/org/apache/coyote/ajp/AjpProcessor.java 2011-09-08 14:25:11.619833000 +0100
2706++++ tomcat6/java/org/apache/coyote/ajp/AjpProcessor.java 2011-09-08 14:44:12.771697501 +0100
2707+@@ -408,11 +408,13 @@
2708+ }
2709+ continue;
2710+ } else if(type != Constants.JK_AJP13_FORWARD_REQUEST) {
2711+- // Usually the servlet didn't read the previous request body
2712+- if(log.isDebugEnabled()) {
2713+- log.debug("Unexpected message: "+type);
2714++ // Unexpected packet type. Unread body packets should have
2715++ // been swallowed in finish().
2716++ if (log.isDebugEnabled()) {
2717++ log.debug("Unexpected message: " + type);
2718+ }
2719+- continue;
2720++ error = true;
2721++ break;
2722+ }
2723+
2724+ request.setStartTime(System.currentTimeMillis());
2725+@@ -1038,6 +1040,11 @@
2726+
2727+ finished = true;
2728+
2729++ // Swallow the unread body packet if present
2730++ if (first && request.getContentLengthLong() > 0) {
2731++ receive();
2732++ }
2733++
2734+ // Add the end message
2735+ output.write(endMessageArray);
2736
2737=== modified file 'debian/patches/series'
2738--- debian/patches/series 2011-07-06 21:23:58 +0000
2739+++ debian/patches/series 2011-09-15 08:37:39 +0000
2740@@ -9,3 +9,4 @@
2741 0010-Use-java.security.policy-file-in-catalina.sh.patch
2742 0011-623242.patch
2743 0012-CVE-2011-2204.patch
2744+0013-CVE-2011-3190.patch
2745
2746=== modified file 'java/org/apache/coyote/ajp/AjpAprProcessor.java'
2747--- java/org/apache/coyote/ajp/AjpAprProcessor.java 2011-02-15 22:41:42 +0000
2748+++ java/org/apache/coyote/ajp/AjpAprProcessor.java 2011-09-15 08:37:39 +0000
2749@@ -390,11 +390,13 @@
2750 }
2751 continue;
2752 } else if(type != Constants.JK_AJP13_FORWARD_REQUEST) {
2753- // Usually the servlet didn't read the previous request body
2754- if(log.isDebugEnabled()) {
2755- log.debug("Unexpected message: "+type);
2756+ // Unexpected packet type. Unread body packets should have
2757+ // been swallowed in finish().
2758+ if (log.isDebugEnabled()) {
2759+ log.debug("Unexpected message: " + type);
2760 }
2761- continue;
2762+ error = true;
2763+ break;
2764 }
2765
2766 keptAlive = true;
2767@@ -1033,6 +1035,11 @@
2768
2769 finished = true;
2770
2771+ // Swallow the unread body packet if present
2772+ if (first && request.getContentLengthLong() > 0) {
2773+ receive();
2774+ }
2775+
2776 // Add the end message
2777 if (outputBuffer.position() + endMessageArray.length > outputBuffer.capacity()) {
2778 flush();
2779
2780=== modified file 'java/org/apache/coyote/ajp/AjpProcessor.java'
2781--- java/org/apache/coyote/ajp/AjpProcessor.java 2011-02-15 22:41:42 +0000
2782+++ java/org/apache/coyote/ajp/AjpProcessor.java 2011-09-15 08:37:39 +0000
2783@@ -408,11 +408,13 @@
2784 }
2785 continue;
2786 } else if(type != Constants.JK_AJP13_FORWARD_REQUEST) {
2787- // Usually the servlet didn't read the previous request body
2788- if(log.isDebugEnabled()) {
2789- log.debug("Unexpected message: "+type);
2790+ // Unexpected packet type. Unread body packets should have
2791+ // been swallowed in finish().
2792+ if (log.isDebugEnabled()) {
2793+ log.debug("Unexpected message: " + type);
2794 }
2795- continue;
2796+ error = true;
2797+ break;
2798 }
2799
2800 request.setStartTime(System.currentTimeMillis());
2801@@ -1038,6 +1040,11 @@
2802
2803 finished = true;
2804
2805+ // Swallow the unread body packet if present
2806+ if (first && request.getContentLengthLong() > 0) {
2807+ receive();
2808+ }
2809+
2810 // Add the end message
2811 output.write(endMessageArray);
2812

Subscribers

People subscribed via source and target branches

to all changes: