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
021 package org.apache.directory.server.changepw.protocol;
022
023
024 import java.io.UnsupportedEncodingException;
025 import java.net.InetAddress;
026 import java.net.InetSocketAddress;
027 import java.nio.ByteBuffer;
028
029 import javax.security.auth.kerberos.KerberosPrincipal;
030
031 import org.apache.directory.server.changepw.ChangePasswordServer;
032 import org.apache.directory.server.changepw.exceptions.ChangePasswordException;
033 import org.apache.directory.server.changepw.exceptions.ErrorType;
034 import org.apache.directory.server.changepw.messages.ChangePasswordErrorModifier;
035 import org.apache.directory.server.changepw.messages.ChangePasswordRequest;
036 import org.apache.directory.server.changepw.service.ChangePasswordContext;
037 import org.apache.directory.server.changepw.service.ChangePasswordService;
038 import org.apache.directory.server.i18n.I18n;
039 import org.apache.directory.server.kerberos.shared.exceptions.KerberosException;
040 import org.apache.directory.server.kerberos.shared.messages.ErrorMessage;
041 import org.apache.directory.server.kerberos.shared.messages.ErrorMessageModifier;
042 import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
043 import org.apache.directory.server.kerberos.shared.store.PrincipalStore;
044 import org.apache.mina.core.service.IoHandler;
045 import org.apache.mina.core.session.IdleStatus;
046 import org.apache.mina.core.session.IoSession;
047 import org.apache.mina.filter.codec.ProtocolCodecFilter;
048 import org.slf4j.Logger;
049 import org.slf4j.LoggerFactory;
050
051
052 /**
053 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
054 * @version $Rev: 901657 $, $Date: 2010-01-21 13:27:15 +0200 (Thu, 21 Jan 2010) $
055 */
056 public class ChangePasswordProtocolHandler implements IoHandler
057 {
058 private static final Logger log = LoggerFactory.getLogger( ChangePasswordProtocolHandler.class );
059
060 private ChangePasswordServer config;
061 private PrincipalStore store;
062 private String contextKey = "context";
063
064
065 /**
066 * Creates a new instance of ChangePasswordProtocolHandler.
067 *
068 * @param config
069 * @param store
070 */
071 public ChangePasswordProtocolHandler( ChangePasswordServer config, PrincipalStore store )
072 {
073 this.config = config;
074 this.store = store;
075 }
076
077
078 public void sessionCreated( IoSession session ) throws Exception
079 {
080 if ( log.isDebugEnabled() )
081 {
082 log.debug( "{} CREATED: {}", session.getRemoteAddress(), session.getTransportMetadata() );
083 }
084
085 if ( session.getTransportMetadata().isConnectionless() )
086 {
087 session.getFilterChain().addFirst( "codec",
088 new ProtocolCodecFilter( ChangePasswordUdpProtocolCodecFactory.getInstance() ) );
089 }
090 else
091 {
092 session.getFilterChain().addFirst( "codec",
093 new ProtocolCodecFilter( ChangePasswordTcpProtocolCodecFactory.getInstance() ) );
094 }
095 }
096
097
098 public void sessionOpened( IoSession session )
099 {
100 log.debug( "{} OPENED", session.getRemoteAddress() );
101 }
102
103
104 public void sessionClosed( IoSession session )
105 {
106 log.debug( "{} CLOSED", session.getRemoteAddress() );
107 }
108
109
110 public void sessionIdle( IoSession session, IdleStatus status )
111 {
112 log.debug( "{} IDLE ({})", session.getRemoteAddress(), status );
113 }
114
115
116 public void exceptionCaught( IoSession session, Throwable cause )
117 {
118 log.debug( session.getRemoteAddress() + " EXCEPTION", cause );
119 session.close( true );
120 }
121
122
123 public void messageReceived( IoSession session, Object message )
124 {
125 log.debug( "{} RCVD: {}", session.getRemoteAddress(), message );
126
127 InetAddress clientAddress = ( ( InetSocketAddress ) session.getRemoteAddress() ).getAddress();
128 ChangePasswordRequest request = ( ChangePasswordRequest ) message;
129
130 try
131 {
132 ChangePasswordContext changepwContext = new ChangePasswordContext();
133 changepwContext.setConfig( config );
134 changepwContext.setStore( store );
135 changepwContext.setClientAddress( clientAddress );
136 changepwContext.setRequest( request );
137 session.setAttribute( getContextKey(), changepwContext );
138
139 ChangePasswordService.execute( session, changepwContext );
140
141 session.write( changepwContext.getReply() );
142 }
143 catch ( KerberosException ke )
144 {
145 if ( log.isDebugEnabled() )
146 {
147 log.warn( ke.getLocalizedMessage(), ke );
148 }
149 else
150 {
151 log.warn( ke.getLocalizedMessage() );
152 }
153
154 ErrorMessage errorMessage = getErrorMessage( config.getServicePrincipal(), ke );
155
156 ChangePasswordErrorModifier modifier = new ChangePasswordErrorModifier();
157 modifier.setErrorMessage( errorMessage );
158
159 session.write( modifier.getChangePasswordError() );
160 }
161 catch ( Exception e )
162 {
163 log.error( I18n.err( I18n.ERR_152, e.getLocalizedMessage() ), e );
164
165 session.write( getErrorMessage( config.getServicePrincipal(), new ChangePasswordException(
166 ErrorType.KRB5_KPASSWD_UNKNOWN_ERROR ) ) );
167 }
168 }
169
170
171 public void messageSent( IoSession session, Object message )
172 {
173 if ( log.isDebugEnabled() )
174 {
175 log.debug( "{} SENT: {}", session.getRemoteAddress(), message );
176 }
177 }
178
179
180 protected String getContextKey()
181 {
182 return ( this.contextKey );
183 }
184
185
186 private ErrorMessage getErrorMessage( KerberosPrincipal principal, KerberosException exception )
187 {
188 ErrorMessageModifier modifier = new ErrorMessageModifier();
189
190 KerberosTime now = new KerberosTime();
191
192 modifier.setErrorCode( exception.getErrorCode() );
193 modifier.setExplanatoryText( exception.getLocalizedMessage() );
194 modifier.setServerPrincipal( principal );
195 modifier.setServerTime( now );
196 modifier.setServerMicroSecond( 0 );
197 modifier.setExplanatoryData( buildExplanatoryData( exception ) );
198
199 return modifier.getErrorMessage();
200 }
201
202
203 private byte[] buildExplanatoryData( KerberosException exception )
204 {
205 short resultCode = ( short ) exception.getErrorCode();
206
207 byte[] resultString =
208 { ( byte ) 0x00 };
209
210 if ( exception.getExplanatoryData() == null || exception.getExplanatoryData().length == 0 )
211 {
212 try
213 {
214 resultString = exception.getLocalizedMessage().getBytes( "UTF-8" );
215 }
216 catch ( UnsupportedEncodingException uee )
217 {
218 log.error( uee.getLocalizedMessage() );
219 }
220 }
221 else
222 {
223 resultString = exception.getExplanatoryData();
224 }
225
226 ByteBuffer byteBuffer = ByteBuffer.allocate( 256 );
227 byteBuffer.putShort( resultCode );
228 byteBuffer.put( resultString );
229
230 byteBuffer.flip();
231 byte[] explanatoryData = new byte[byteBuffer.remaining()];
232 byteBuffer.get( explanatoryData, 0, explanatoryData.length );
233
234 return explanatoryData;
235 }
236 }