001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 *
019 */
020 package org.apache.directory.server.ldap.handlers;
021
022
023 import javax.naming.NamingException;
024
025 import org.apache.directory.server.core.CoreSession;
026 import org.apache.directory.server.ldap.LdapServer;
027 import org.apache.directory.server.ldap.LdapSession;
028 import org.apache.directory.server.ldap.handlers.extended.StartTlsHandler;
029 import org.apache.directory.shared.ldap.exception.LdapException;
030 import org.apache.directory.shared.ldap.exception.LdapReferralException;
031 import org.apache.directory.shared.ldap.message.InternalAbandonRequest;
032 import org.apache.directory.shared.ldap.message.InternalBindRequest;
033 import org.apache.directory.shared.ldap.message.InternalExtendedRequest;
034 import org.apache.directory.shared.ldap.message.InternalLdapResult;
035 import org.apache.directory.shared.ldap.message.InternalReferral;
036 import org.apache.directory.shared.ldap.message.ReferralImpl;
037 import org.apache.directory.shared.ldap.message.InternalRequest;
038 import org.apache.directory.shared.ldap.message.ResultCodeEnum;
039 import org.apache.directory.shared.ldap.message.InternalResultResponse;
040 import org.apache.directory.shared.ldap.message.InternalResultResponseRequest;
041 import org.apache.directory.shared.ldap.name.LdapDN;
042 import org.apache.directory.shared.ldap.util.ExceptionUtils;
043 import org.apache.mina.core.filterchain.IoFilterChain;
044 import org.apache.mina.core.session.IoSession;
045 import org.apache.mina.handler.demux.MessageHandler;
046 import org.slf4j.Logger;
047 import org.slf4j.LoggerFactory;
048
049
050 /**
051 * A base class for all LDAP request handlers.
052 *
053 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
054 * @version $Rev: 541827 $
055 */
056 public abstract class LdapRequestHandler<T extends InternalRequest> implements MessageHandler<T>
057 {
058 /** The logger for this class */
059 private static final Logger LOG = LoggerFactory.getLogger( LdapRequestHandler.class );
060
061 /** The reference on the Ldap server instance */
062 protected LdapServer ldapServer;
063
064
065 /**
066 * @return The associated ldap server instance
067 */
068 public final LdapServer getLdapServer()
069 {
070 return ldapServer;
071 }
072
073
074 /**
075 * Associates a Ldap server instance to the message handler
076 * @param ldapServer the associated ldap server instance
077 */
078 public final void setLdapServer( LdapServer ldapServer )
079 {
080 this.ldapServer = ldapServer;
081 }
082
083
084 /**
085 * Checks to see if confidentiality requirements are met. If the
086 * LdapServer requires confidentiality and the SSLFilter is engaged
087 * this will return true. If confidentiality is not required this
088 * will return true. If confidentially is required and the SSLFilter
089 * is not engaged in the IoFilterChain this will return false.
090 *
091 * This method is used by handlers to determine whether to send back
092 * {@link ResultCodeEnum#CONFIDENTIALITY_REQUIRED} error responses back
093 * to clients.
094 *
095 * @param session the MINA IoSession to check for TLS security
096 * @return true if confidentiality requirement is met, false otherwise
097 */
098 public final boolean isConfidentialityRequirementSatisfied( IoSession session )
099 {
100
101 if ( ! ldapServer.isConfidentialityRequired() )
102 {
103 return true;
104 }
105
106 IoFilterChain chain = session.getFilterChain();
107 return chain.contains( "sslFilter" );
108 }
109
110
111 public void rejectWithoutConfidentiality( IoSession session, InternalResultResponse resp )
112 {
113 InternalLdapResult result = resp.getLdapResult();
114 result.setResultCode( ResultCodeEnum.CONFIDENTIALITY_REQUIRED );
115 result.setErrorMessage( "Confidentiality (TLS secured connection) is required." );
116 session.write( resp );
117 return;
118 }
119
120
121 public final void handleMessage( IoSession session, T message ) throws Exception
122 {
123 LdapSession ldapSession = ldapServer.getLdapSessionManager().getLdapSession( session );
124
125 //handle( ldapSession, message );
126 // TODO - session you get from LdapServer should have the ldapServer
127 // member already set no? Should remove these lines where ever they
128 // may be if that's the case.
129 ldapSession.setLdapServer( ldapServer );
130
131 // protect against insecure conns when confidentiality is required
132 if ( ! isConfidentialityRequirementSatisfied( session ) )
133 {
134 if ( message instanceof InternalExtendedRequest )
135 {
136 // Reject all extended operations except StartTls
137 InternalExtendedRequest req = ( InternalExtendedRequest ) message;
138 if ( ! req.getID().equals( StartTlsHandler.EXTENSION_OID ) )
139 {
140 rejectWithoutConfidentiality( session, req.getResultResponse() );
141 return;
142 }
143
144 // Allow StartTls extended operations to go through
145 }
146 else if ( message instanceof InternalResultResponseRequest )
147 {
148 // Reject all other operations that have a result response
149 rejectWithoutConfidentiality( session, ( ( InternalResultResponseRequest ) message ).getResultResponse() );
150 return;
151 }
152 else // Just return from unbind, and abandon immediately
153 {
154 return;
155 }
156 }
157
158 // We should check that the server allows anonymous requests
159 // only if it's not a BindRequest
160 if ( message instanceof InternalBindRequest )
161 {
162 handle( ldapSession, message );
163 }
164 else
165 {
166 CoreSession coreSession = null;
167
168 /*
169 * All requests except bind automatically presume the authentication
170 * is anonymous if the session has not been authenticated. Hence a
171 * default bind is presumed as the anonymous identity.
172 */
173 if ( ldapSession.isAuthenticated() )
174 {
175 coreSession = ldapSession.getCoreSession();
176 handle( ldapSession, message );
177 return;
178 }
179
180 coreSession = getLdapServer().getDirectoryService().getSession();
181 ldapSession.setCoreSession( coreSession );
182
183 if ( message instanceof InternalAbandonRequest )
184 {
185 return;
186 }
187
188 handle( ldapSession, message );
189 return;
190 }
191 }
192
193 /**
194 * Handle a Ldap message associated with a session
195 *
196 * @param session The associated session
197 * @param message The message we have to handle
198 * @throws Exception If there is an error during the processing of this message
199 */
200 public abstract void handle( LdapSession session, T message ) throws Exception;
201
202
203 /**
204 * Handles processing with referrals without ManageDsaIT control.
205 */
206 public void handleException( LdapSession session, InternalResultResponseRequest req, Exception e )
207 {
208 InternalLdapResult result = req.getResultResponse().getLdapResult();
209
210 /*
211 * Set the result code or guess the best option.
212 */
213 ResultCodeEnum code;
214 if ( e instanceof LdapException )
215 {
216 code = ( ( LdapException ) e ).getResultCode();
217 }
218 else
219 {
220 code = ResultCodeEnum.getBestEstimate( e, req.getType() );
221 }
222
223 result.setResultCode( code );
224
225 /*
226 * Setup the error message to put into the request and put entire
227 * exception into the message if we are in debug mode. Note we
228 * embed the result code name into the message.
229 */
230 String msg = code.toString() + ": failed for " + req + ": " + e.getMessage();
231
232 if ( LOG.isDebugEnabled() )
233 {
234 LOG.debug( msg, e );
235
236 msg += ":\n" + ExceptionUtils.getStackTrace( e );
237 }
238
239 result.setErrorMessage( msg );
240
241 if ( e instanceof NamingException )
242 {
243 NamingException ne = ( NamingException ) e;
244
245 // Add the matchedDN if necessary
246 boolean setMatchedDn =
247 code == ResultCodeEnum.NO_SUCH_OBJECT ||
248 code == ResultCodeEnum.ALIAS_PROBLEM ||
249 code == ResultCodeEnum.INVALID_DN_SYNTAX ||
250 code == ResultCodeEnum.ALIAS_DEREFERENCING_PROBLEM;
251
252 if ( ( ne.getResolvedName() != null ) && setMatchedDn )
253 {
254 result.setMatchedDn( ( LdapDN ) ne.getResolvedName() );
255 }
256
257 // Add the referrals if necessary
258 if ( e instanceof LdapReferralException )
259 {
260 InternalReferral referrals = new ReferralImpl();
261
262 do
263 {
264 String ref = ((LdapReferralException)e).getReferralInfo();
265 referrals.addLdapUrl( ref );
266 }
267 while ( ((LdapReferralException)e).skipReferral() );
268
269 result.setReferral( referrals );
270 }
271 }
272
273 session.getIoSession().write( req.getResultResponse() );
274 }
275 }