KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > ajp > NegociationHandler


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 JavaDoc;
20 import java.security.MessageDigest JavaDoc;
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 JavaDoc containerSignature="Ajp14-based container";
150     String JavaDoc seed="seed";// will use random
151
String JavaDoc 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 JavaDoc 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 JavaDoc pseed)
177     {
178     String JavaDoc[] credentials = new String JavaDoc[1];
179     credentials[0] = pseed;
180     seed = digest(credentials, "md5");
181     }
182
183     /**
184      * Get the original entropy seed
185      */

186     public String JavaDoc getSeed()
187     {
188     return seed;
189     }
190
191     /**
192      * Set the secret password
193      */

194     public void setPassword(String JavaDoc ppwd)
195     {
196     password = ppwd;
197     }
198     
199     /**
200      * Get the secret password
201      */

202     public String JavaDoc 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 JavaDoc
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 JavaDoc
288     {
289     webserverNegociation = msg.getLongInt();
290     String JavaDoc 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 JavaDoc[] credentials = new String JavaDoc[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 JavaDoc
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 JavaDoc[] credentials = new String JavaDoc[2];
325     credentials[0] = getSeed();
326     credentials[1] = getPassword();
327     String JavaDoc computed = digest(credentials, "md5");
328     String JavaDoc received = new String JavaDoc(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 JavaDoc
367     {
368     log("in handleContextQuery :");
369     String JavaDoc 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 JavaDoc
399     {
400     log("in handleStatus :");
401     return (304);
402     }
403
404     private int handleShutdown( Ajp13Packet msg, Ajp13Packet outBuf )
405         throws IOException JavaDoc
406     {
407     log("in handleShutdown :");
408     return (304);
409     }
410     
411     private int handleContextState( Ajp13Packet msg , Ajp13Packet outBuf)
412         throws IOException JavaDoc
413     {
414     log("in handleContextState :");
415     return (304);
416     }
417     
418     private int handleUnknowPacket( Ajp13Packet msg, Ajp13Packet outBuf )
419         throws IOException JavaDoc
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 JavaDoc digest(String JavaDoc[] credentials, String JavaDoc algorithm) {
462         try {
463             // Obtain a new message digest with MD5 encryption
464
MessageDigest JavaDoc md =
465                 (MessageDigest JavaDoc)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 JavaDoc 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 JavaDoc decodeNegociation(int nego)
489     {
490     StringBuffer JavaDoc buf = new StringBuffer JavaDoc(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 JavaDoc s) {
530         if (log.isDebugEnabled())
531         log.debug("Ajp14Negotiation: " + s );
532     }
533  }
534
Popular Tags