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 12:27:15 +0100 (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    }