View Javadoc

1   /*
2    *   Licensed to the Apache Software Foundation (ASF) under one
3    *   or more contributor license agreements.  See the NOTICE file
4    *   distributed with this work for additional information
5    *   regarding copyright ownership.  The ASF licenses this file
6    *   to you under the Apache License, Version 2.0 (the
7    *   "License"); you may not use this file except in compliance
8    *   with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *   Unless required by applicable law or agreed to in writing,
13   *   software distributed under the License is distributed on an
14   *   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *   KIND, either express or implied.  See the License for the
16   *   specific language governing permissions and limitations
17   *   under the License.
18   *
19   */
20  package org.apache.directory.server.ldap;
21  
22  
23  import javax.naming.ldap.Control;
24  
25  import org.apache.directory.shared.ldap.message.ExtendedRequest;
26  import org.apache.directory.shared.ldap.message.ExtendedRequestImpl;
27  import org.apache.directory.shared.ldap.message.MutableControl;
28  import org.apache.directory.shared.ldap.message.Request;
29  import org.apache.directory.shared.ldap.message.ResponseCarryingMessageException;
30  import org.apache.directory.shared.ldap.message.ResultCodeEnum;
31  import org.apache.directory.shared.ldap.message.ResultResponse;
32  import org.apache.directory.shared.ldap.message.ResultResponseRequest;
33  import org.apache.directory.shared.ldap.message.extended.NoticeOfDisconnect;
34  import org.apache.mina.common.IoFilterChain;
35  import org.apache.mina.common.IoHandler;
36  import org.apache.mina.common.IoSession;
37  import org.apache.mina.filter.SSLFilter;
38  import org.apache.mina.filter.codec.ProtocolCodecFilter;
39  import org.apache.mina.handler.demux.DemuxingIoHandler;
40  import org.apache.mina.util.SessionLog;
41  import org.slf4j.Logger;
42  import org.slf4j.LoggerFactory;
43  
44  
45  /**
46   * The MINA IoHandler implementation extending {@link DemuxingIoHandler} for 
47   * the LDAP protocol.  THe {@link LdapService} creates this multiplexing 
48   * {@link IoHandler} handler and populates it with subordinate handlers for
49   * the various kinds of LDAP {@link Request} messages.  This is done in the
50   * setXxxHandler() methods of the LdapService where Xxxx is Add, Modify, etc.
51   *
52   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
53   * @version $Rev$, $Date$
54   */
55  class LdapProtocolHandler extends DemuxingIoHandler
56  {
57      /** The logger */
58      private static final Logger LOG = LoggerFactory.getLogger( LdapProtocolHandler.class );
59      
60      /** the {@link LdapService} this handler is associated with */
61      private final LdapService ldapService;
62  
63  
64      /**
65       * Creates a new instance of LdapProtocolHandler.
66       *
67       * @param ldapService
68       */
69      LdapProtocolHandler( LdapService ldapService )
70      {
71          this.ldapService = ldapService;
72      }
73  
74  
75      /*
76       * (non-Javadoc)
77       * @see org.apache.mina.common.IoHandlerAdapter#sessionCreated(org.apache.mina.common.IoSession)
78       */
79      public void sessionCreated( IoSession session ) throws Exception
80      {
81          LdapSession ldapSession = new LdapSession( session );
82          IoFilterChain filters = session.getFilterChain();
83          filters.addLast( "codec", new ProtocolCodecFilter( ldapService.getProtocolCodecFactory() ) );
84          ldapService.getLdapSessionManager().addLdapSession( ldapSession );
85      }
86  
87  
88      /*
89       * (non-Javadoc)
90       * @see org.apache.mina.common.IoHandlerAdapter#sessionClosed(org.apache.mina.common.IoSession)
91       */
92      public void sessionClosed( IoSession session )
93      {
94          LdapSession ldapSession = ldapService.getLdapSessionManager().removeLdapSession( session );
95          cleanUpSession( ldapSession );
96      }
97  
98      
99      /**
100      * Explicitly handles {@link LdapSession} and {@link IoSession} cleanup tasks.
101      *
102      * @param ldapSession the LdapSession to cleanup after being removed from 
103      * the {@link LdapSessionManager}
104      */
105     private void cleanUpSession( LdapSession ldapSession )
106     {
107         if ( ldapSession == null )
108         {
109             LOG.warn( "Null LdapSession given to cleanUpSession." );
110             return;
111         }
112         
113         if ( ldapSession != null )
114         {
115             ldapSession.abandonAllOutstandingRequests();
116         }
117         
118         if ( ! ldapSession.getIoSession().isClosing() || ldapSession.getIoSession().isConnected() )
119         {
120             try
121             {
122                 ldapSession.getIoSession().close();
123             }
124             catch ( Throwable t )
125             {
126                 LOG.warn( "Failed to close IoSession for LdapSession." );
127             }
128         }
129     }
130     
131 
132     /*
133      * (non-Javadoc)
134      * @see org.apache.mina.handler.demux.DemuxingIoHandler#messageReceived(org.apache.mina.common.IoSession, java.lang.Object)
135      */
136     public void messageReceived( IoSession session, Object message ) throws Exception
137     {
138         // Translate SSLFilter messages into LDAP extended request
139         // defined in RFC #2830, 'Lightweight Directory Access Protocol (v3):
140         // Extension for Transport Layer Security'.
141         // 
142         // The RFC specifies the payload should be empty, but we use
143         // it to notify the TLS state changes.  This hack should be
144         // OK from the viewpointd of security because StartTLS
145         // handler should react to only SESSION_UNSECURED message
146         // and degrade authentication level to 'anonymous' as specified
147         // in the RFC, and this is no threat.
148 
149         if ( message == SSLFilter.SESSION_SECURED )
150         {
151             ExtendedRequest req = new ExtendedRequestImpl( 0 );
152             req.setOid( "1.3.6.1.4.1.1466.20037" );
153             req.setPayload( "SECURED".getBytes( "ISO-8859-1" ) );
154             message = req;
155         }
156         else if ( message == SSLFilter.SESSION_UNSECURED )
157         {
158             ExtendedRequest req = new ExtendedRequestImpl( 0 );
159             req.setOid( "1.3.6.1.4.1.1466.20037" );
160             req.setPayload( "UNSECURED".getBytes( "ISO-8859-1" ) );
161             message = req;
162         }
163 
164         if ( ( ( Request ) message ).getControls().size() > 0 && message instanceof ResultResponseRequest )
165         {
166             ResultResponseRequest req = ( ResultResponseRequest ) message;
167             for ( Control control1 : req.getControls().values() )
168             {
169                 MutableControl control = ( MutableControl ) control1;
170                 if ( control.isCritical() && ! ldapService.getSupportedControls().contains( control.getID() ) )
171                 {
172                     ResultResponse resp = req.getResultResponse();
173                     resp.getLdapResult().setErrorMessage( "Unsupport critical control: " + control.getID() );
174                     resp.getLdapResult().setResultCode( ResultCodeEnum.UNAVAILABLE_CRITICAL_EXTENSION );
175                     session.write( resp );
176                     return;
177                 }
178             }
179         }
180 
181         super.messageReceived( session, message );
182     }
183 
184 
185     /*
186      * (non-Javadoc)
187      * @see org.apache.mina.common.IoHandlerAdapter#exceptionCaught(org.apache.mina.common.IoSession, java.lang.Throwable)
188      */
189     public void exceptionCaught( IoSession session, Throwable cause )
190     {
191         if ( cause.getCause() instanceof ResponseCarryingMessageException )
192         {
193             ResponseCarryingMessageException rcme = ( ResponseCarryingMessageException ) cause.getCause();
194 
195             if ( rcme.getResponse() != null )
196             {
197                 session.write( rcme.getResponse() );
198                 return;
199             }                
200         }
201         
202         SessionLog.warn( session,
203             "Unexpected exception forcing session to close: sending disconnect notice to client.", cause );
204 
205         session.write( NoticeOfDisconnect.PROTOCOLERROR );
206         LdapSession ldapSession = this.ldapService.getLdapSessionManager().removeLdapSession( session );
207         cleanUpSession( ldapSession );
208         session.close();
209     }
210 }