Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/apache/ajp/NegociationHandler.java


1   /*
2    *  Copyright 1999-2004 The Apache Software Foundation
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   */
16  
17  package org.apache.ajp;
18  
19  import java.io.IOException;
20  import java.security.MessageDigest;
21  
22  import org.apache.tomcat.util.buf.HexUtils;
23  import org.apache.tomcat.util.http.BaseRequest;
24  
25  
26  /**
27   * Handler for the protocol negotiation. It will authenticate and 
28   * exchange information about supported messages on each end.
29   * 
30   * 
31   * @author Henri Gomez [hgomez@apache.org]
32   * @author Dan Milstein [danmil@shore.net]
33   * @author Keith Wannamaker [Keith@Wannamaker.org]
34   * @author Costin Manolache
35   */
36  public class NegociationHandler extends AjpHandler
37  {
38      
39      private static org.apache.commons.logging.Log log=
40          org.apache.commons.logging.LogFactory.getLog(NegociationHandler.class );
41      
42      // Initial Login Phase (web server -> servlet engine)
43      public static final byte JK_AJP14_LOGINIT_CMD=0x10;
44      
45      // Second Login Phase (servlet engine -> web server), md5 seed is received
46      public static final byte JK_AJP14_LOGSEED_CMD=0x11;
47      
48      // Third Login Phase (web server -> servlet engine),
49      // md5 of seed + secret is sent
50      public static final byte JK_AJP14_LOGCOMP_CMD=0x12;
51      
52      // Login Accepted (servlet engine -> web server)
53      public static final byte JK_AJP14_LOGOK_CMD=0x13;
54  
55      // Login Rejected (servlet engine -> web server), will be logged
56      public static final byte JK_AJP14_LOGNOK_CMD=0x14;
57      
58      // Context Query (web server -> servlet engine),
59      // which URI are handled by servlet engine ?
60      public static final byte JK_AJP14_CONTEXT_QRY_CMD=0x15;
61      
62      // Context Info (servlet engine -> web server), URI handled response
63      public static final byte JK_AJP14_CONTEXT_INFO_CMD= 0x16;
64      
65      // Context Update (servlet engine -> web server), status of context changed
66      public static final byte JK_AJP14_CONTEXT_UPDATE_CMD= 0x17;
67      
68      // Servlet Engine Status (web server -> servlet engine),
69      // what's the status of the servlet engine ?
70      public static final byte JK_AJP14_STATUS_CMD= 0x18;
71      
72      // Secure Shutdown command (web server -> servlet engine),
73      //please servlet stop yourself.
74      public static final byte JK_AJP14_SHUTDOWN_CMD= 0x19;
75      
76      // Secure Shutdown command Accepted (servlet engine -> web server)
77      public static final byte JK_AJP14_SHUTOK_CMD= 0x1A;
78      
79      // Secure Shutdown Rejected (servlet engine -> web server)
80      public static final byte JK_AJP14_SHUTNOK_CMD= 0x1B;
81      
82      // Context Status (web server -> servlet engine),
83      //what's the status of the context ?
84      public static final byte JK_AJP14_CONTEXT_STATE_CMD= 0x1C;
85      
86      // Context Status Reply (servlet engine -> web server), status of context
87      public static final byte JK_AJP14_CONTEXT_STATE_REP_CMD = 0x1D;
88      
89      // Unknown Packet Reply (web server <-> servlet engine),
90      //when a packet couldn't be decoded
91      public static final byte JK_AJP14_UNKNOW_PACKET_CMD = 0x1E;
92  
93  
94      // -------------------- Other constants -------------------- 
95  
96      // Entropy Packet Size
97      public static final int AJP14_ENTROPY_SEED_LEN= 32;
98      public static final int AJP14_COMPUTED_KEY_LEN= 32;
99      
100     // web-server want context info after login
101     public static final int AJP14_CONTEXT_INFO_NEG= 0x80000000;
102     
103     // web-server want context updates
104     public static final int AJP14_CONTEXT_UPDATE_NEG= 0x40000000;
105     
106     // web-server want compressed stream
107     public static final int AJP14_GZIP_STREAM_NEG= 0x20000000;
108     
109     // web-server want crypted DES56 stream with secret key
110     public static final int AJP14_DES56_STREAM_NEG= 0x10000000;
111     
112     // Extended info on server SSL vars
113     public static final int AJP14_SSL_VSERVER_NEG= 0x08000000;
114     
115     // Extended info on client SSL vars
116     public static final int AJP14_SSL_VCLIENT_NEG= 0x04000000;
117     
118     // Extended info on crypto SSL vars
119     public static final int AJP14_SSL_VCRYPTO_NEG= 0x02000000;
120     
121     // Extended info on misc SSL vars
122     public static final int  AJP14_SSL_VMISC_NEG= 0x01000000;
123     
124     // mask of protocol supported
125     public static final int  AJP14_PROTO_SUPPORT_AJPXX_NEG=0x00FF0000;
126     
127     // communication could use AJP14
128     public static final int  AJP14_PROTO_SUPPORT_AJP14_NEG=0x00010000;
129     
130     // communication could use AJP15
131     public static final int  AJP14_PROTO_SUPPORT_AJP15_NEG=0x00020000;
132     
133     // communication could use AJP16
134     public static final int  AJP14_PROTO_SUPPORT_AJP16_NEG=0x00040000;
135     
136     // Some failure codes
137     public static final int AJP14_BAD_KEY_ERR= 0xFFFFFFFF;
138     public static final int AJP14_ENGINE_DOWN_ERR = 0xFFFFFFFE;
139     public static final int AJP14_RETRY_LATER_ERR = 0xFFFFFFFD;
140     public static final int AJP14_SHUT_AUTHOR_FAILED_ERR = 0xFFFFFFFC;
141     
142     // Some status codes
143     public static final byte AJP14_CONTEXT_DOWN= 0x01;
144     public static final byte AJP14_CONTEXT_UP= 0x02;
145     public static final byte AJP14_CONTEXT_OK= 0x03;
146 
147 
148     // -------------------- Parameters --------------------
149     String  containerSignature="Ajp14-based container";
150     String  seed="seed";// will use random
151     String  password;
152 
153     int  webserverNegociation=0;
154     //    String  webserverName;
155     
156     public NegociationHandler() {
157         setSeed("myveryrandomentropy");
158   setPassword("myverysecretkey");
159     }
160 
161     public void setContainerSignature( String s ) {
162   containerSignature=s;
163     }
164 
165     // -------------------- State --------------------
166 
167     //     public String getWebserverName() {
168     //   return webserverName;
169     //     }
170     
171     // -------------------- Parameters --------------------
172     
173     /**
174      * Set the original entropy seed
175      */
176     public void setSeed(String pseed) 
177     {
178   String[] credentials = new String[1];
179   credentials[0] = pseed;
180   seed = digest(credentials, "md5");
181     }
182 
183     /**
184      * Get the original entropy seed
185      */
186     public String getSeed()
187     {
188   return seed;
189     }
190 
191     /**
192      * Set the secret password
193      */
194     public void setPassword(String ppwd) 
195     {
196   password = ppwd;
197     }
198     
199     /**
200      * Get the secret password
201      */
202     public String getPassword()
203     {
204   return password;
205     }
206 
207     // -------------------- Initialization --------------------
208 
209     public void init( Ajp13 ajp14 ) {
210         super.init(ajp14);
211   // register incoming message handlers
212   ajp14.registerMessageType( JK_AJP14_LOGINIT_CMD,"JK_AJP14_LOGINIT_CMD",
213            this, null); //
214   ajp14.registerMessageType( JK_AJP14_LOGCOMP_CMD,"JK_AJP14_LOGCOMP_CMD",
215            this, null); //
216   ajp14.registerMessageType( RequestHandler.JK_AJP13_SHUTDOWN,"JK_AJP13_SHUTDOWN",
217            this, null); //
218   ajp14.registerMessageType( JK_AJP14_CONTEXT_QRY_CMD,
219            "JK_AJP14_CONTEXT_QRY_CMD",
220            this, null); //
221   ajp14.registerMessageType( JK_AJP14_STATUS_CMD,"JK_AJP14_STATUS_CMD",
222            this, null); //
223   ajp14.registerMessageType( JK_AJP14_SHUTDOWN_CMD,
224                                    "JK_AJP14_SHUTDOWN_CMD",
225            this, null); //
226   ajp14.registerMessageType( JK_AJP14_CONTEXT_STATE_CMD,
227            "JK_AJP14_CONTEXT_STATE_CMD",
228            this, null); //
229   ajp14.registerMessageType( JK_AJP14_UNKNOW_PACKET_CMD,
230            "JK_AJP14_UNKNOW_PACKET_CMD",
231            this, null); //
232   
233   // register outgoing messages handler
234   ajp14.registerMessageType( JK_AJP14_LOGNOK_CMD,"JK_AJP14_LOGNOK_CMD",
235            this,null );
236   
237     }
238     
239 
240     
241     // -------------------- Dispatch --------------------
242 
243     public int handleAjpMessage( int type, Ajp13 ch, Ajp13Packet hBuf,
244          BaseRequest req )
245   throws IOException
246     {
247         if (log.isDebugEnabled())
248             log.debug("handleAjpMessage: " + type );
249   Ajp13Packet outBuf=ch.outBuf;
250   // Valid requests when not logged:
251   switch( type ) {
252   case JK_AJP14_LOGINIT_CMD :
253       return handleLogInit(ch, hBuf, outBuf);
254   case JK_AJP14_LOGCOMP_CMD :
255       return handleLogComp(ch, hBuf, outBuf);
256   case RequestHandler.JK_AJP13_SHUTDOWN:
257       return -2;
258   case JK_AJP14_CONTEXT_QRY_CMD :
259       return handleContextQuery(ch, hBuf, outBuf);
260   case JK_AJP14_STATUS_CMD :
261       return handleStatus(hBuf, outBuf);
262   case JK_AJP14_SHUTDOWN_CMD :
263       return handleShutdown(hBuf, outBuf);
264   case JK_AJP14_CONTEXT_STATE_CMD :
265       return handleContextState(hBuf, outBuf);
266   case JK_AJP14_UNKNOW_PACKET_CMD :
267       return handleUnknowPacket(hBuf, outBuf);
268   default:
269       log("unknown command " + type + " received");
270       return 200; // XXX This is actually an error condition
271   }
272   //return UNKNOWN;
273     }
274     
275     //----------- Implementation for various protocol commands -----------
276 
277     /**
278      * Handle the Initial Login Message from Web-Server
279      *
280      * Get the requested Negociation Flags
281      * Get also the Web-Server Name
282      * 
283      * Send Login Seed (MD5 of seed)
284      */
285     private int handleLogInit( Ajp13 ch, Ajp13Packet msg,
286              Ajp13Packet outBuf )
287   throws IOException
288     {
289   webserverNegociation = msg.getLongInt();
290   String webserverName  = msg.getString();
291   log("in handleLogInit with nego " +
292       decodeNegociation(webserverNegociation) +
293             " from webserver " + webserverName);
294   
295   outBuf.reset();
296         outBuf.appendByte(JK_AJP14_LOGSEED_CMD);
297   String[] credentials = new String[1];
298   credentials[0] = getSeed();
299         outBuf.appendXBytes(getSeed().getBytes(), 0, AJP14_ENTROPY_SEED_LEN);
300   log("in handleLogInit: sent entropy " + getSeed());
301         outBuf.end();
302         ch.send(outBuf);
303   return 304;
304     }
305     
306     /**
307      * Handle the Second Phase of Login (accreditation)
308      * 
309      * Get the MD5 digest of entropy + secret password
310      * If the authentification is valid send back LogOk
311      * If the authentification failed send back LogNok
312      */
313     private int handleLogComp( Ajp13 ch, Ajp13Packet msg,
314              Ajp13Packet outBuf )
315   throws IOException
316     {
317   // log("in handleLogComp :");
318   
319   byte [] rdigest = new byte[AJP14_ENTROPY_SEED_LEN];
320   
321   if (msg.getXBytes(rdigest, AJP14_ENTROPY_SEED_LEN) < 0)
322       return 200;
323   
324   String[] credentials = new String[2];
325   credentials[0] = getSeed();
326   credentials[1] = getPassword();
327   String computed = digest(credentials, "md5");
328   String received = new String(rdigest);
329   
330   // XXX temp workaround, to test the rest of the connector.
331   
332   if ( ! computed.equalsIgnoreCase(received)) {
333       log("in handleLogComp : authentification failure received=" +
334     received + " awaited=" + computed);
335   }
336   
337   if (false ) { // ! computed.equalsIgnoreCase(received)) {
338       log("in handleLogComp : authentification failure received=" +
339     received + " awaited=" + computed);
340       
341       // we should have here a security mecanism which could maintain
342       // a list of remote IP which failed too many times
343       // so we could reject them quickly at next connect
344       outBuf.reset();
345       outBuf.appendByte(JK_AJP14_LOGNOK_CMD);
346       outBuf.appendLongInt(AJP14_BAD_KEY_ERR);
347       outBuf.end();
348       ch.send(outBuf);
349       return 200;
350   } else {
351             // logged we can go process requests
352       channel.setLogged(true);
353       outBuf.reset();
354       outBuf.appendByte(JK_AJP14_LOGOK_CMD);
355       outBuf.appendLongInt(getProtocolFlags(webserverNegociation));
356       outBuf.appendString( containerSignature );
357       outBuf.end(); 
358       ch.send(outBuf);
359   }
360   
361   return (304);
362     }
363 
364     private int handleContextQuery( Ajp13 ch, Ajp13Packet msg,
365             Ajp13Packet outBuf )
366   throws IOException
367     {
368   log("in handleContextQuery :");
369     String virtualHost = msg.getString();
370     log("in handleContextQuery for virtual" + virtualHost); 
371 
372     outBuf.reset();
373     outBuf.appendByte(JK_AJP14_CONTEXT_INFO_CMD);
374     outBuf.appendString( virtualHost );
375 
376     log("in handleContextQuery for virtual " + virtualHost +
377         "examples URI/MIMES");
378     outBuf.appendString("examples");    // first context - examples
379     outBuf.appendString("servlet/*");   // examples/servlet/*
380     outBuf.appendString("*.jsp");       // examples/*.jsp
381     outBuf.appendString("");            // no more URI/MIMES
382 
383     log("in handleContextQuery for virtual " + virtualHost +
384         "send admin URI/MIMES"); 
385     outBuf.appendString("admin");       // second context - admin
386     outBuf.appendString("servlet/*");   // /admin//servlet/*
387     outBuf.appendString("*.jsp");       // /admin/*.jsp
388     outBuf.appendString("");            // no more URI/MIMES
389 
390     outBuf.appendString("");            // no more contexts
391     outBuf.end();
392     ch.send(outBuf);
393     
394   return (304);
395     }
396     
397     private int handleStatus( Ajp13Packet msg, Ajp13Packet outBuf )
398         throws IOException
399     {
400   log("in handleStatus :");
401   return (304);
402     }
403 
404     private int handleShutdown( Ajp13Packet msg, Ajp13Packet outBuf )
405         throws IOException
406     {
407   log("in handleShutdown :");
408   return (304);
409     }
410     
411     private int handleContextState( Ajp13Packet msg , Ajp13Packet outBuf)
412         throws IOException
413     {
414   log("in handleContextState :");
415   return (304);
416     }
417     
418     private int handleUnknowPacket( Ajp13Packet msg, Ajp13Packet outBuf )
419         throws IOException
420     {
421   log("in handleUnknowPacket :");
422   return (304);
423     }
424 
425     // -------------------- Utils --------------------
426 
427     /**
428      * Compute the Protocol Negociation Flags
429      * 
430      * Depending the protocol fatures implemented on servet-engine,
431      * we'll drop requested features which could be asked by web-server
432      *
433      * Hopefully this functions could be overrided by decendants
434      */
435     private int getProtocolFlags(int wanted)
436     {
437                     // no real-time context update
438   wanted &= ~(AJP14_CONTEXT_UPDATE_NEG |
439                     // no gzip compression yet
440         AJP14_GZIP_STREAM_NEG    | 
441                      // no DES56 cyphering yet
442         AJP14_DES56_STREAM_NEG   |
443                     // no Extended info on server SSL vars yet
444         AJP14_SSL_VSERVER_NEG    |
445                     // no Extended info on client SSL vars yet
446         AJP14_SSL_VCLIENT_NEG    |
447                     // no Extended info on crypto SSL vars yet
448         AJP14_SSL_VCRYPTO_NEG    |
449                     // no Extended info on misc SSL vars yet
450         AJP14_SSL_VMISC_NEG       |
451                     // Reset AJP protocol mask
452         AJP14_PROTO_SUPPORT_AJPXX_NEG);
453         
454   // Only strict AJP14 supported
455   return (wanted | AJP14_PROTO_SUPPORT_AJP14_NEG);  
456     }
457 
458     /**
459      * Compute a digest (MD5 in AJP14) for an array of String
460      */
461     public final String digest(String[] credentials, String algorithm) {
462         try {
463             // Obtain a new message digest with MD5 encryption
464             MessageDigest md =
465                 (MessageDigest)MessageDigest.getInstance(algorithm).clone();
466             // encode the credentials items
467       for (int i = 0; i < credentials.length; i++) {
468     if( debug > 0 )
469                     log("Credentials : " + i + " " + credentials[i]);
470     if( credentials[i] != null  )
471         md.update(credentials[i].getBytes());
472       }
473             // obtain the byte array from the digest
474             byte[] dig = md.digest();
475       return HexUtils.convert(dig);
476         } catch (Exception ex) {
477             ex.printStackTrace();
478             return null;
479         }
480     }
481 
482     // -------------------- Debugging --------------------
483     // Very usefull for develoment
484 
485     /**
486      * Display Negociation field in human form 
487      */
488     private String decodeNegociation(int nego)
489     {
490   StringBuffer buf = new StringBuffer(128);
491   
492   if ((nego & AJP14_CONTEXT_INFO_NEG) != 0) 
493       buf.append(" CONTEXT-INFO");
494   
495   if ((nego & AJP14_CONTEXT_UPDATE_NEG) != 0)
496       buf.append(" CONTEXT-UPDATE");
497   
498   if ((nego & AJP14_GZIP_STREAM_NEG) != 0)
499       buf.append(" GZIP-STREAM");
500   
501   if ((nego & AJP14_DES56_STREAM_NEG) != 0)
502       buf.append(" DES56-STREAM");
503   
504   if ((nego & AJP14_SSL_VSERVER_NEG) != 0)
505       buf.append(" SSL-VSERVER");
506   
507   if ((nego & AJP14_SSL_VCLIENT_NEG) != 0)
508       buf.append(" SSL-VCLIENT");
509   
510   if ((nego & AJP14_SSL_VCRYPTO_NEG) != 0)
511       buf.append(" SSL-VCRYPTO");
512   
513   if ((nego & AJP14_SSL_VMISC_NEG) != 0)
514       buf.append(" SSL-VMISC");
515   
516   if ((nego & AJP14_PROTO_SUPPORT_AJP14_NEG) != 0)
517       buf.append(" AJP14");
518   
519   if ((nego & AJP14_PROTO_SUPPORT_AJP15_NEG) != 0)
520       buf.append(" AJP15");
521   
522   if ((nego & AJP14_PROTO_SUPPORT_AJP16_NEG) != 0)
523       buf.append(" AJP16");
524   
525   return (buf.toString());
526     }
527     
528     private static int debug=10;
529     void log(String s) {
530         if (log.isDebugEnabled())
531       log.debug("Ajp14Negotiation: " + s );
532     }
533  }