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 org.apache.directory.server.core.CoreSession; 024 import org.apache.directory.server.i18n.I18n; 025 import org.apache.directory.server.ldap.LdapServer; 026 import org.apache.directory.server.ldap.LdapSession; 027 import org.apache.directory.server.ldap.handlers.extended.StartTlsHandler; 028 import org.apache.directory.shared.ldap.exception.LdapOperationException; 029 import org.apache.directory.shared.ldap.exception.LdapReferralException; 030 import org.apache.directory.shared.ldap.message.BindRequestImpl; 031 import org.apache.directory.shared.ldap.message.BindResponseImpl; 032 import org.apache.directory.shared.ldap.message.ReferralImpl; 033 import org.apache.directory.shared.ldap.message.ResultCodeEnum; 034 import org.apache.directory.shared.ldap.message.internal.InternalAbandonRequest; 035 import org.apache.directory.shared.ldap.message.internal.InternalBindRequest; 036 import org.apache.directory.shared.ldap.message.internal.InternalBindResponse; 037 import org.apache.directory.shared.ldap.message.internal.InternalExtendedRequest; 038 import org.apache.directory.shared.ldap.message.internal.InternalLdapResult; 039 import org.apache.directory.shared.ldap.message.internal.InternalReferral; 040 import org.apache.directory.shared.ldap.message.internal.InternalRequest; 041 import org.apache.directory.shared.ldap.message.internal.InternalResultResponse; 042 import org.apache.directory.shared.ldap.message.internal.InternalResultResponseRequest; 043 import org.apache.directory.shared.ldap.name.DN; 044 import org.apache.directory.shared.ldap.util.ExceptionUtils; 045 import org.apache.mina.core.filterchain.IoFilterChain; 046 import org.apache.mina.core.session.IoSession; 047 import org.apache.mina.handler.demux.MessageHandler; 048 import org.slf4j.Logger; 049 import org.slf4j.LoggerFactory; 050 051 052 /** 053 * A base class for all LDAP request handlers. 054 * 055 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 056 * @version $Rev: 541827 $ 057 */ 058 public abstract class LdapRequestHandler<T extends InternalRequest> implements MessageHandler<T> 059 { 060 /** The logger for this class */ 061 private static final Logger LOG = LoggerFactory.getLogger( LdapRequestHandler.class ); 062 063 /** The reference on the Ldap server instance */ 064 protected LdapServer ldapServer; 065 066 067 /** 068 * @return The associated ldap server instance 069 */ 070 public final LdapServer getLdapServer() 071 { 072 return ldapServer; 073 } 074 075 076 /** 077 * Associates a Ldap server instance to the message handler 078 * @param ldapServer the associated ldap server instance 079 */ 080 public final void setLdapServer( LdapServer ldapServer ) 081 { 082 this.ldapServer = ldapServer; 083 } 084 085 086 /** 087 * Checks to see if confidentiality requirements are met. If the 088 * LdapServer requires confidentiality and the SSLFilter is engaged 089 * this will return true. If confidentiality is not required this 090 * will return true. If confidentially is required and the SSLFilter 091 * is not engaged in the IoFilterChain this will return false. 092 * 093 * This method is used by handlers to determine whether to send back 094 * {@link ResultCodeEnum#CONFIDENTIALITY_REQUIRED} error responses back 095 * to clients. 096 * 097 * @param session the MINA IoSession to check for TLS security 098 * @return true if confidentiality requirement is met, false otherwise 099 */ 100 public final boolean isConfidentialityRequirementSatisfied( IoSession session ) 101 { 102 103 if ( ! ldapServer.isConfidentialityRequired() ) 104 { 105 return true; 106 } 107 108 IoFilterChain chain = session.getFilterChain(); 109 return chain.contains( "sslFilter" ); 110 } 111 112 113 public void rejectWithoutConfidentiality( IoSession session, InternalResultResponse resp ) 114 { 115 InternalLdapResult result = resp.getLdapResult(); 116 result.setResultCode( ResultCodeEnum.CONFIDENTIALITY_REQUIRED ); 117 result.setErrorMessage( "Confidentiality (TLS secured connection) is required." ); 118 session.write( resp ); 119 return; 120 } 121 122 /** 123 *{@inheritDoc} 124 */ 125 public final void handleMessage( IoSession session, T message ) throws Exception 126 { 127 LdapSession ldapSession = ldapServer.getLdapSessionManager().getLdapSession( session ); 128 129 if( ldapSession == null ) 130 { 131 // in some cases the session is becoming null though the client is sending the UnbindRequest 132 // before closing 133 LOG.info( "ignoring the message {} received from null session", message ); 134 return; 135 } 136 137 // First check that the client hasn't issued a previous BindRequest, unless it 138 // was a SASL BindRequest 139 if ( ldapSession.isAuthPending() ) 140 { 141 // Only SASL BinRequest are allowed if we already are handling a 142 // SASL BindRequest 143 if ( !( message instanceof BindRequestImpl ) || 144 ((BindRequestImpl)message).isSimple() || 145 ldapSession.isSimpleAuthPending() ) 146 { 147 LOG.error( I18n.err( I18n.ERR_732 ) ); 148 InternalBindResponse bindResponse = new BindResponseImpl( message.getMessageId() ); 149 InternalLdapResult bindResult = bindResponse.getLdapResult(); 150 bindResult.setResultCode( ResultCodeEnum.UNWILLING_TO_PERFORM ); 151 bindResult.setErrorMessage( I18n.err( I18n.ERR_732 ) ); 152 ldapSession.getIoSession().write( bindResponse ); 153 return; 154 } 155 } 156 157 // TODO - session you get from LdapServer should have the ldapServer 158 // member already set no? Should remove these lines where ever they 159 // may be if that's the case. 160 ldapSession.setLdapServer( ldapServer ); 161 162 // protect against insecure conns when confidentiality is required 163 if ( ! isConfidentialityRequirementSatisfied( session ) ) 164 { 165 if ( message instanceof InternalExtendedRequest ) 166 { 167 // Reject all extended operations except StartTls 168 InternalExtendedRequest req = ( InternalExtendedRequest ) message; 169 170 if ( ! req.getID().equals( StartTlsHandler.EXTENSION_OID ) ) 171 { 172 rejectWithoutConfidentiality( session, req.getResultResponse() ); 173 return; 174 } 175 176 // Allow StartTls extended operations to go through 177 } 178 else if ( message instanceof InternalResultResponseRequest ) 179 { 180 // Reject all other operations that have a result response 181 rejectWithoutConfidentiality( session, ( ( InternalResultResponseRequest ) message ).getResultResponse() ); 182 return; 183 } 184 else // Just return from unbind, and abandon immediately 185 { 186 return; 187 } 188 } 189 190 // We should check that the server allows anonymous requests 191 // only if it's not a BindRequest 192 if ( message instanceof InternalBindRequest ) 193 { 194 handle( ldapSession, message ); 195 } 196 else 197 { 198 CoreSession coreSession = null; 199 200 /* 201 * All requests except bind automatically presume the authentication 202 * is anonymous if the session has not been authenticated. Hence a 203 * default bind is presumed as the anonymous identity. 204 */ 205 if ( ldapSession.isAuthenticated() ) 206 { 207 coreSession = ldapSession.getCoreSession(); 208 handle( ldapSession, message ); 209 return; 210 } 211 212 coreSession = getLdapServer().getDirectoryService().getSession(); 213 ldapSession.setCoreSession( coreSession ); 214 215 if ( message instanceof InternalAbandonRequest ) 216 { 217 return; 218 } 219 220 handle( ldapSession, message ); 221 return; 222 } 223 } 224 225 /** 226 * Handle a Ldap message associated with a session 227 * 228 * @param session The associated session 229 * @param message The message we have to handle 230 * @throws Exception If there is an error during the processing of this message 231 */ 232 public abstract void handle( LdapSession session, T message ) throws Exception; 233 234 235 /** 236 * Handles processing with referrals without ManageDsaIT control. 237 */ 238 public void handleException( LdapSession session, InternalResultResponseRequest req, Exception e ) 239 { 240 InternalLdapResult result = req.getResultResponse().getLdapResult(); 241 242 /* 243 * Set the result code or guess the best option. 244 */ 245 ResultCodeEnum code; 246 if ( e instanceof LdapOperationException ) 247 { 248 code = ( ( LdapOperationException ) e ).getResultCode(); 249 } 250 else 251 { 252 code = ResultCodeEnum.getBestEstimate( e, req.getType() ); 253 } 254 255 result.setResultCode( code ); 256 257 /* 258 * Setup the error message to put into the request and put entire 259 * exception into the message if we are in debug mode. Note we 260 * embed the result code name into the message. 261 */ 262 String msg = code.toString() + ": failed for " + req + ": " + e.getLocalizedMessage(); 263 264 if ( LOG.isDebugEnabled() ) 265 { 266 LOG.debug( msg, e ); 267 268 msg += ":\n" + ExceptionUtils.getStackTrace( e ); 269 } 270 271 result.setErrorMessage( msg ); 272 273 if ( e instanceof LdapOperationException ) 274 { 275 LdapOperationException ne = ( LdapOperationException ) e; 276 277 // Add the matchedDN if necessary 278 boolean setMatchedDn = 279 code == ResultCodeEnum.NO_SUCH_OBJECT || 280 code == ResultCodeEnum.ALIAS_PROBLEM || 281 code == ResultCodeEnum.INVALID_DN_SYNTAX || 282 code == ResultCodeEnum.ALIAS_DEREFERENCING_PROBLEM; 283 284 if ( ( ne.getResolvedDn() != null ) && setMatchedDn ) 285 { 286 result.setMatchedDn( ( DN ) ne.getResolvedDn() ); 287 } 288 289 // Add the referrals if necessary 290 if ( e instanceof LdapReferralException ) 291 { 292 InternalReferral referrals = new ReferralImpl(); 293 294 do 295 { 296 String ref = ((LdapReferralException)e).getReferralInfo(); 297 referrals.addLdapUrl( ref ); 298 } 299 while ( ((LdapReferralException)e).skipReferral() ); 300 301 result.setReferral( referrals ); 302 } 303 } 304 305 session.getIoSession().write( req.getResultResponse() ); 306 } 307 }