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  
21  package org.apache.directory.server.kerberos.changepwd.protocol;
22  
23  
24  import java.io.UnsupportedEncodingException;
25  import java.net.InetAddress;
26  import java.net.InetSocketAddress;
27  import java.nio.ByteBuffer;
28  
29  import javax.security.auth.kerberos.KerberosPrincipal;
30  
31  import org.apache.directory.server.i18n.I18n;
32  import org.apache.directory.server.kerberos.changepwd.ChangePasswordServer;
33  import org.apache.directory.server.kerberos.changepwd.exceptions.ChangePasswdErrorType;
34  import org.apache.directory.server.kerberos.changepwd.exceptions.ChangePasswordException;
35  import org.apache.directory.server.kerberos.changepwd.messages.ChangePasswordError;
36  import org.apache.directory.server.kerberos.changepwd.messages.ChangePasswordRequest;
37  import org.apache.directory.server.kerberos.changepwd.service.ChangePasswordContext;
38  import org.apache.directory.server.kerberos.changepwd.service.ChangePasswordService;
39  import org.apache.directory.server.kerberos.shared.store.PrincipalStore;
40  import org.apache.directory.shared.kerberos.KerberosTime;
41  import org.apache.directory.shared.kerberos.components.PrincipalName;
42  import org.apache.directory.shared.kerberos.exceptions.ErrorType;
43  import org.apache.directory.shared.kerberos.exceptions.KerberosException;
44  import org.apache.directory.shared.kerberos.messages.KrbError;
45  import org.apache.mina.core.service.IoHandlerAdapter;
46  import org.apache.mina.core.session.IdleStatus;
47  import org.apache.mina.core.session.IoSession;
48  import org.apache.mina.filter.codec.ProtocolCodecFilter;
49  import org.slf4j.Logger;
50  import org.slf4j.LoggerFactory;
51  
52  
53  /**
54   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
55   */
56  public class ChangePasswordProtocolHandler extends IoHandlerAdapter
57  {
58      private static final Logger LOG = LoggerFactory.getLogger( ChangePasswordProtocolHandler.class );
59  
60      private ChangePasswordServer server;
61      private PrincipalStore store;
62      private String contextKey = "context";
63  
64  
65      /**
66       * Creates a new instance of ChangePasswordProtocolHandler.
67       *
68       * @param config The ChangePassword server configuration
69       * @param store The Principal store
70       */
71      public ChangePasswordProtocolHandler( ChangePasswordServer config, PrincipalStore store )
72      {
73          this.server = config;
74          this.store = store;
75      }
76  
77  
78      @Override
79      public void sessionCreated( IoSession session ) throws Exception
80      {
81          if ( LOG.isDebugEnabled() )
82          {
83              LOG.debug( "{} CREATED:  {}", session.getRemoteAddress(), session.getTransportMetadata() );
84          }
85  
86          session.getFilterChain().addFirst( "codec",
87              new ProtocolCodecFilter( ChangePasswordProtocolCodecFactory.getInstance() ) );
88      }
89  
90  
91      @Override
92      public void sessionOpened( IoSession session )
93      {
94          LOG.debug( "{} OPENED", session.getRemoteAddress() );
95      }
96  
97  
98      @Override
99      public void sessionClosed( IoSession session )
100     {
101         LOG.debug( "{} CLOSED", session.getRemoteAddress() );
102     }
103 
104 
105     @Override
106     public void sessionIdle( IoSession session, IdleStatus status )
107     {
108         LOG.debug( "{} IDLE ({})", session.getRemoteAddress(), status );
109     }
110 
111 
112     @Override
113     public void exceptionCaught( IoSession session, Throwable cause )
114     {
115         LOG.debug( session.getRemoteAddress() + " EXCEPTION", cause );
116         session.closeNow();
117     }
118 
119 
120     @Override
121     public void messageReceived( IoSession session, Object message )
122     {
123         LOG.debug( "{} RCVD:  {}", session.getRemoteAddress(), message );
124 
125         InetAddress clientAddress = ( ( InetSocketAddress ) session.getRemoteAddress() ).getAddress();
126         ChangePasswordRequest/../org/apache/directory/server/kerberos/changepwd/messages/ChangePasswordRequest.html#ChangePasswordRequest">ChangePasswordRequest request = ( ChangePasswordRequest ) message;
127 
128         try
129         {
130             ChangePasswordContextpwd/service/ChangePasswordContext.html#ChangePasswordContext">ChangePasswordContext changepwContext = new ChangePasswordContext();
131             changepwContext.setConfig( server.getConfig() );
132             changepwContext.setStore( store );
133             changepwContext.setClientAddress( clientAddress );
134             changepwContext.setRequest( request );
135             changepwContext.setReplayCache( server.getReplayCache() );
136             session.setAttribute( getContextKey(), changepwContext );
137 
138             ChangePasswordService.execute( session, changepwContext );
139 
140             session.write( changepwContext.getReply() );
141         }
142         catch ( KerberosException ke )
143         {
144             if ( LOG.isDebugEnabled() )
145             {
146                 LOG.warn( ke.getLocalizedMessage(), ke );
147             }
148             else
149             {
150                 LOG.warn( ke.getLocalizedMessage() );
151             }
152 
153             KrbError errorMessage = getErrorMessage( server.getConfig().getServicePrincipal(), ke );
154 
155             session.write( new ChangePasswordError( request.getVersionNumber(), errorMessage ) );
156         }
157         catch ( Exception e )
158         {
159             LOG.error( I18n.err( I18n.ERR_152, e.getLocalizedMessage() ), e );
160 
161             KrbError error = getErrorMessage( server.getConfig().getServicePrincipal(), new ChangePasswordException(
162                 ChangePasswdErrorType.KRB5_KPASSWD_UNKNOWN_ERROR ) );
163             session.write( new ChangePasswordError( request.getVersionNumber(), error ) );
164         }
165     }
166 
167 
168     @Override
169     public void messageSent( IoSession session, Object message )
170     {
171         if ( LOG.isDebugEnabled() )
172         {
173             LOG.debug( "{} SENT:  {}", session.getRemoteAddress(), message );
174         }
175     }
176 
177 
178     protected String getContextKey()
179     {
180         return ( this.contextKey );
181     }
182 
183 
184     private KrbError getErrorMessage( KerberosPrincipal principal, KerberosException exception )
185     {
186         KrbErrorared/kerberos/messages/KrbError.html#KrbError">KrbError krbError = new KrbError();
187 
188         KerberosTimehared/kerberos/KerberosTime.html#KerberosTime">KerberosTime now = new KerberosTime();
189 
190         //FIXME not sure if this is the correct error to set for KrbError instance
191         // the correct change password protocol related error code is set in e-data anyway
192         krbError.setErrorCode( ErrorType.KRB_ERR_GENERIC );
193         krbError.setEText( exception.getLocalizedMessage() );
194         krbError.setSName( new PrincipalName( principal ) );
195         krbError.setSTime( now );
196         krbError.setSusec( 0 );
197         krbError.setRealm( principal.getRealm() );
198         krbError.setEData( buildExplanatoryData( exception ) );
199 
200         return krbError;
201     }
202 
203 
204     private byte[] buildExplanatoryData( KerberosException exception )
205     {
206         short resultCode = ( short ) exception.getErrorCode();
207 
208         byte[] resultString =
209             { ( byte ) 0x00 };
210 
211         if ( exception.getExplanatoryData() == null || exception.getExplanatoryData().length == 0 )
212         {
213             try
214             {
215                 resultString = exception.getLocalizedMessage().getBytes( "UTF-8" );
216             }
217             catch ( UnsupportedEncodingException uee )
218             {
219                 LOG.error( uee.getLocalizedMessage() );
220             }
221         }
222         else
223         {
224             resultString = exception.getExplanatoryData();
225         }
226 
227         ByteBuffer byteBuffer = ByteBuffer.allocate( 2 + resultString.length );
228         byteBuffer.putShort( resultCode );
229         byteBuffer.put( resultString );
230 
231         return byteBuffer.array();
232     }
233 
234     
235     @Override
236     public void inputClosed( IoSession session )
237     {
238     }
239 }