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 021package org.apache.directory.server.kerberos.changepwd.protocol; 022 023 024import java.io.UnsupportedEncodingException; 025import java.net.InetAddress; 026import java.net.InetSocketAddress; 027import java.nio.ByteBuffer; 028 029import javax.security.auth.kerberos.KerberosPrincipal; 030 031import org.apache.directory.server.i18n.I18n; 032import org.apache.directory.server.kerberos.changepwd.ChangePasswordServer; 033import org.apache.directory.server.kerberos.changepwd.exceptions.ChangePasswdErrorType; 034import org.apache.directory.server.kerberos.changepwd.exceptions.ChangePasswordException; 035import org.apache.directory.server.kerberos.changepwd.messages.ChangePasswordError; 036import org.apache.directory.server.kerberos.changepwd.messages.ChangePasswordRequest; 037import org.apache.directory.server.kerberos.changepwd.service.ChangePasswordContext; 038import org.apache.directory.server.kerberos.changepwd.service.ChangePasswordService; 039import org.apache.directory.server.kerberos.shared.store.PrincipalStore; 040import org.apache.directory.shared.kerberos.KerberosTime; 041import org.apache.directory.shared.kerberos.components.PrincipalName; 042import org.apache.directory.shared.kerberos.exceptions.ErrorType; 043import org.apache.directory.shared.kerberos.exceptions.KerberosException; 044import org.apache.directory.shared.kerberos.messages.KrbError; 045import org.apache.mina.core.service.IoHandlerAdapter; 046import org.apache.mina.core.session.IdleStatus; 047import org.apache.mina.core.session.IoSession; 048import org.apache.mina.filter.codec.ProtocolCodecFilter; 049import org.slf4j.Logger; 050import org.slf4j.LoggerFactory; 051 052 053/** 054 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 055 */ 056public class ChangePasswordProtocolHandler extends IoHandlerAdapter 057{ 058 private static final Logger LOG = LoggerFactory.getLogger( ChangePasswordProtocolHandler.class ); 059 060 private ChangePasswordServer server; 061 private PrincipalStore store; 062 private String contextKey = "context"; 063 064 065 /** 066 * Creates a new instance of ChangePasswordProtocolHandler. 067 * 068 * @param config The ChangePassword server configuration 069 * @param store The Principal store 070 */ 071 public ChangePasswordProtocolHandler( ChangePasswordServer config, PrincipalStore store ) 072 { 073 this.server = config; 074 this.store = store; 075 } 076 077 078 @Override 079 public void sessionCreated( IoSession session ) throws Exception 080 { 081 if ( LOG.isDebugEnabled() ) 082 { 083 LOG.debug( "{} CREATED: {}", session.getRemoteAddress(), session.getTransportMetadata() ); 084 } 085 086 session.getFilterChain().addFirst( "codec", 087 new ProtocolCodecFilter( ChangePasswordProtocolCodecFactory.getInstance() ) ); 088 } 089 090 091 @Override 092 public void sessionOpened( IoSession session ) 093 { 094 LOG.debug( "{} OPENED", session.getRemoteAddress() ); 095 } 096 097 098 @Override 099 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 request = ( ChangePasswordRequest ) message; 127 128 try 129 { 130 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 KrbError krbError = new KrbError(); 187 188 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}