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 public void sessionCreated( IoSession session ) throws Exception 079 { 080 if ( LOG.isDebugEnabled() ) 081 { 082 LOG.debug( "{} CREATED: {}", session.getRemoteAddress(), session.getTransportMetadata() ); 083 } 084 085 session.getFilterChain().addFirst( "codec", 086 new ProtocolCodecFilter( ChangePasswordProtocolCodecFactory.getInstance() ) ); 087 } 088 089 090 public void sessionOpened( IoSession session ) 091 { 092 LOG.debug( "{} OPENED", session.getRemoteAddress() ); 093 } 094 095 096 public void sessionClosed( IoSession session ) 097 { 098 LOG.debug( "{} CLOSED", session.getRemoteAddress() ); 099 } 100 101 102 public void sessionIdle( IoSession session, IdleStatus status ) 103 { 104 LOG.debug( "{} IDLE ({})", session.getRemoteAddress(), status ); 105 } 106 107 108 public void exceptionCaught( IoSession session, Throwable cause ) 109 { 110 LOG.debug( session.getRemoteAddress() + " EXCEPTION", cause ); 111 session.closeNow(); 112 } 113 114 115 public void messageReceived( IoSession session, Object message ) 116 { 117 LOG.debug( "{} RCVD: {}", session.getRemoteAddress(), message ); 118 119 InetAddress clientAddress = ( ( InetSocketAddress ) session.getRemoteAddress() ).getAddress(); 120 ChangePasswordRequest request = ( ChangePasswordRequest ) message; 121 122 try 123 { 124 ChangePasswordContext changepwContext = new ChangePasswordContext(); 125 changepwContext.setConfig( server.getConfig() ); 126 changepwContext.setStore( store ); 127 changepwContext.setClientAddress( clientAddress ); 128 changepwContext.setRequest( request ); 129 changepwContext.setReplayCache( server.getReplayCache() ); 130 session.setAttribute( getContextKey(), changepwContext ); 131 132 ChangePasswordService.execute( session, changepwContext ); 133 134 session.write( changepwContext.getReply() ); 135 } 136 catch ( KerberosException ke ) 137 { 138 if ( LOG.isDebugEnabled() ) 139 { 140 LOG.warn( ke.getLocalizedMessage(), ke ); 141 } 142 else 143 { 144 LOG.warn( ke.getLocalizedMessage() ); 145 } 146 147 KrbError errorMessage = getErrorMessage( server.getConfig().getServicePrincipal(), ke ); 148 149 session.write( new ChangePasswordError( request.getVersionNumber(), errorMessage ) ); 150 } 151 catch ( Exception e ) 152 { 153 LOG.error( I18n.err( I18n.ERR_152, e.getLocalizedMessage() ), e ); 154 155 KrbError error = getErrorMessage( server.getConfig().getServicePrincipal(), new ChangePasswordException( 156 ChangePasswdErrorType.KRB5_KPASSWD_UNKNOWN_ERROR ) ); 157 session.write( new ChangePasswordError( request.getVersionNumber(), error ) ); 158 } 159 } 160 161 162 public void messageSent( IoSession session, Object message ) 163 { 164 if ( LOG.isDebugEnabled() ) 165 { 166 LOG.debug( "{} SENT: {}", session.getRemoteAddress(), message ); 167 } 168 } 169 170 171 protected String getContextKey() 172 { 173 return ( this.contextKey ); 174 } 175 176 177 private KrbError getErrorMessage( KerberosPrincipal principal, KerberosException exception ) 178 { 179 KrbError krbError = new KrbError(); 180 181 KerberosTime now = new KerberosTime(); 182 183 //FIXME not sure if this is the correct error to set for KrbError instance 184 // the correct change password protocol related error code is set in e-data anyway 185 krbError.setErrorCode( ErrorType.KRB_ERR_GENERIC ); 186 krbError.setEText( exception.getLocalizedMessage() ); 187 krbError.setSName( new PrincipalName( principal ) ); 188 krbError.setSTime( now ); 189 krbError.setSusec( 0 ); 190 krbError.setRealm( principal.getRealm() ); 191 krbError.setEData( buildExplanatoryData( exception ) ); 192 193 return krbError; 194 } 195 196 197 private byte[] buildExplanatoryData( KerberosException exception ) 198 { 199 short resultCode = ( short ) exception.getErrorCode(); 200 201 byte[] resultString = 202 { ( byte ) 0x00 }; 203 204 if ( exception.getExplanatoryData() == null || exception.getExplanatoryData().length == 0 ) 205 { 206 try 207 { 208 resultString = exception.getLocalizedMessage().getBytes( "UTF-8" ); 209 } 210 catch ( UnsupportedEncodingException uee ) 211 { 212 LOG.error( uee.getLocalizedMessage() ); 213 } 214 } 215 else 216 { 217 resultString = exception.getExplanatoryData(); 218 } 219 220 ByteBuffer byteBuffer = ByteBuffer.allocate( 2 + resultString.length ); 221 byteBuffer.putShort( resultCode ); 222 byteBuffer.put( resultString ); 223 224 return byteBuffer.array(); 225 } 226 227 228 public void inputClosed( IoSession session ) 229 { 230 } 231}