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 */ 020package org.apache.directory.server.kerberos.protocol; 021 022 023import java.net.InetAddress; 024import java.net.InetSocketAddress; 025 026import javax.security.auth.kerberos.KerberosPrincipal; 027 028import org.apache.directory.api.ldap.model.constants.Loggers; 029import org.apache.directory.server.i18n.I18n; 030import org.apache.directory.server.kerberos.kdc.KdcServer; 031import org.apache.directory.server.kerberos.kdc.authentication.AuthenticationContext; 032import org.apache.directory.server.kerberos.kdc.authentication.AuthenticationService; 033import org.apache.directory.server.kerberos.kdc.ticketgrant.TicketGrantingContext; 034import org.apache.directory.server.kerberos.kdc.ticketgrant.TicketGrantingService; 035import org.apache.directory.server.kerberos.shared.store.PrincipalStore; 036import org.apache.directory.shared.kerberos.KerberosMessageType; 037import org.apache.directory.shared.kerberos.KerberosTime; 038import org.apache.directory.shared.kerberos.components.KdcReq; 039import org.apache.directory.shared.kerberos.components.PrincipalName; 040import org.apache.directory.shared.kerberos.exceptions.ErrorType; 041import org.apache.directory.shared.kerberos.exceptions.KerberosException; 042import org.apache.directory.shared.kerberos.messages.KrbError; 043import org.apache.mina.core.service.IoHandlerAdapter; 044import org.apache.mina.core.session.IdleStatus; 045import org.apache.mina.core.session.IoSession; 046import org.slf4j.Logger; 047import org.slf4j.LoggerFactory; 048 049 050/** 051 * The Kerberos protocol handler for MINA which handles requests for the authentication 052 * service and the ticket granting service of the KDC. 053 * 054 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 055 */ 056public class KerberosProtocolHandler extends IoHandlerAdapter 057{ 058 /** The loggers for this class */ 059 private static final Logger LOG = LoggerFactory.getLogger( KerberosProtocolHandler.class ); 060 private static final Logger LOG_KRB = LoggerFactory.getLogger( Loggers.KERBEROS_LOG.getName() ); 061 062 /** The KDC server */ 063 private KdcServer kdcServer; 064 065 /** The principal Name store */ 066 private PrincipalStore store; 067 068 private static final String CONTEXT_KEY = "context"; 069 070 071 /** 072 * Creates a new instance of KerberosProtocolHandler. 073 * 074 * @param kdcServer The KdcServer instance 075 * @param store The Principal store 076 */ 077 public KerberosProtocolHandler( KdcServer kdcServer, PrincipalStore store ) 078 { 079 this.kdcServer = kdcServer; 080 this.store = store; 081 } 082 083 084 /** 085 * {@inheritDoc} 086 */ 087 public void sessionCreated( IoSession session ) throws Exception 088 { 089 if ( LOG.isDebugEnabled() ) 090 { 091 LOG.debug( "{} CREATED: {}", session.getRemoteAddress(), session.getTransportMetadata() ); 092 } 093 094 if ( LOG_KRB.isDebugEnabled() ) 095 { 096 LOG_KRB.debug( "{} CREATED: {}", session.getRemoteAddress(), session.getTransportMetadata() ); 097 } 098 } 099 100 101 /** 102 * {@inheritDoc} 103 */ 104 public void sessionOpened( IoSession session ) 105 { 106 if ( LOG.isDebugEnabled() ) 107 { 108 LOG.debug( "{} OPENED", session.getRemoteAddress() ); 109 } 110 111 if ( LOG_KRB.isDebugEnabled() ) 112 { 113 LOG_KRB.debug( "{} OPENED", session.getRemoteAddress() ); 114 } 115 } 116 117 118 /** 119 * {@inheritDoc} 120 */ 121 public void sessionClosed( IoSession session ) 122 { 123 if ( LOG.isDebugEnabled() ) 124 { 125 LOG.debug( "{} CLOSED", session.getRemoteAddress() ); 126 } 127 128 if ( LOG_KRB.isDebugEnabled() ) 129 { 130 LOG_KRB.debug( "{} CLOSED", session.getRemoteAddress() ); 131 } 132 } 133 134 135 /** 136 * {@inheritDoc} 137 */ 138 public void sessionIdle( IoSession session, IdleStatus status ) 139 { 140 if ( LOG.isDebugEnabled() ) 141 { 142 LOG.debug( "{} IDLE ({})", session.getRemoteAddress(), status ); 143 } 144 145 if ( LOG_KRB.isDebugEnabled() ) 146 { 147 LOG_KRB.debug( "{} IDLE ({})", session.getRemoteAddress(), status ); 148 } 149 } 150 151 152 /** 153 * {@inheritDoc} 154 */ 155 public void exceptionCaught( IoSession session, Throwable cause ) 156 { 157 LOG.error( "{} EXCEPTION", session.getRemoteAddress(), cause ); 158 LOG_KRB.error( "{} EXCEPTION", session.getRemoteAddress(), cause ); 159 session.closeNow(); 160 } 161 162 163 /** 164 * {@inheritDoc} 165 */ 166 public void messageReceived( IoSession session, Object message ) 167 { 168 if ( LOG.isDebugEnabled() ) 169 { 170 LOG.debug( "{} RCVD: {}", session.getRemoteAddress(), message ); 171 } 172 173 if ( LOG_KRB.isDebugEnabled() ) 174 { 175 LOG_KRB.debug( "{} RCVD: {}", session.getRemoteAddress(), message ); 176 } 177 178 InetAddress clientAddress = ( ( InetSocketAddress ) session.getRemoteAddress() ).getAddress(); 179 180 if ( !( message instanceof KdcReq ) ) 181 { 182 LOG.error( I18n.err( I18n.ERR_152, ErrorType.KRB_AP_ERR_BADDIRECTION ) ); 183 LOG_KRB.error( I18n.err( I18n.ERR_152, ErrorType.KRB_AP_ERR_BADDIRECTION ) ); 184 185 session.write( getErrorMessage( kdcServer.getConfig().getServicePrincipal(), new KerberosException( 186 ErrorType.KRB_AP_ERR_BADDIRECTION ) ) ); 187 return; 188 } 189 190 KdcReq request = ( KdcReq ) message; 191 192 KerberosMessageType messageType = request.getMessageType(); 193 194 try 195 { 196 switch ( messageType ) 197 { 198 case AS_REQ: 199 AuthenticationContext authContext = new AuthenticationContext(); 200 authContext.setConfig( kdcServer.getConfig() ); 201 authContext.setStore( store ); 202 203 if ( request.getKdcReqBody().getAddresses() != null ) 204 { 205 authContext.setClientAddress( clientAddress ); 206 } 207 208 authContext.setRequest( request ); 209 session.setAttribute( CONTEXT_KEY, authContext ); 210 211 AuthenticationService.execute( authContext ); 212 213 LOG_KRB.debug( "AuthenticationContext for AS_REQ : \n{}", authContext ); 214 215 session.write( authContext.getReply() ); 216 break; 217 218 case TGS_REQ: 219 TicketGrantingContext tgsContext = new TicketGrantingContext(); 220 tgsContext.setConfig( kdcServer.getConfig() ); 221 tgsContext.setReplayCache( kdcServer.getReplayCache() ); 222 tgsContext.setStore( store ); 223 tgsContext.setClientAddress( clientAddress ); 224 tgsContext.setRequest( request ); 225 session.setAttribute( CONTEXT_KEY, tgsContext ); 226 227 TicketGrantingService.execute( tgsContext ); 228 229 LOG_KRB.debug( "TGSContext for TGS_REQ : \n {}", tgsContext ); 230 231 session.write( tgsContext.getReply() ); 232 break; 233 234 case AS_REP: 235 case TGS_REP: 236 throw new KerberosException( ErrorType.KRB_AP_ERR_BADDIRECTION ); 237 238 default: 239 throw new KerberosException( ErrorType.KRB_AP_ERR_MSG_TYPE ); 240 } 241 } 242 catch ( KerberosException ke ) 243 { 244 String messageText = ke.getLocalizedMessage() + " (" + ke.getErrorCode() + ")"; 245 246 LOG.warn( messageText ); 247 LOG_KRB.warn( messageText ); 248 249 KrbError error = getErrorMessage( kdcServer.getConfig().getServicePrincipal(), ke ); 250 251 logErrorMessage( error ); 252 253 session.write( error ); 254 } 255 catch ( Exception e ) 256 { 257 LOG.error( I18n.err( I18n.ERR_152, e.getLocalizedMessage() ), e ); 258 LOG_KRB.error( I18n.err( I18n.ERR_152, e.getLocalizedMessage() ), e ); 259 260 session.write( getErrorMessage( kdcServer.getConfig().getServicePrincipal(), new KerberosException( 261 ErrorType.KDC_ERR_SVC_UNAVAILABLE ) ) ); 262 } 263 } 264 265 266 /** 267 * {@inheritDoc} 268 */ 269 public void messageSent( IoSession session, Object message ) 270 { 271 if ( LOG.isDebugEnabled() ) 272 { 273 LOG.debug( "{} SENT: {}", session.getRemoteAddress(), message ); 274 } 275 276 if ( LOG_KRB.isDebugEnabled() ) 277 { 278 LOG_KRB.debug( "{} SENT: {}", session.getRemoteAddress(), message ); 279 } 280 } 281 282 283 /** 284 * Construct an error message given some conditions 285 * 286 * @param principal The Kerberos Principal 287 * @param exception The Exception we've got 288 * @return The resulting KrbError 289 */ 290 protected KrbError getErrorMessage( KerberosPrincipal principal, KerberosException exception ) 291 { 292 KrbError krbError = new KrbError(); 293 294 KerberosTime now = new KerberosTime(); 295 296 krbError.setErrorCode( ErrorType.getTypeByValue( exception.getErrorCode() ) ); 297 krbError.setEText( exception.getLocalizedMessage() ); 298 krbError.setSName( new PrincipalName( principal ) ); 299 krbError.setRealm( principal.getRealm() ); 300 krbError.setSTime( now ); 301 krbError.setSusec( 0 ); 302 krbError.setEData( exception.getExplanatoryData() ); 303 304 return krbError; 305 } 306 307 308 /** 309 * Creates an explicit error message 310 * The error we've get 311 * 312 * @param error The Kerberos error to log 313 */ 314 protected void logErrorMessage( KrbError error ) 315 { 316 try 317 { 318 StringBuilder sb = new StringBuilder(); 319 320 sb.append( "Responding to request with error:" ); 321 sb.append( "\n\t" + "explanatory text: " + error.getEText() ); 322 sb.append( "\n\t" + "error code: " + error.getErrorCode() ); 323 sb.append( "\n\t" + "clientPrincipal: " + error.getCName() ).append( "@" ).append( error.getCRealm() ); 324 sb.append( "\n\t" + "client time: " + error.getCTime() ); 325 sb.append( "\n\t" + "serverPrincipal: " + error.getSName() ).append( "@" ).append( error.getRealm() ); 326 sb.append( "\n\t" + "server time: " + error.getSTime() ); 327 328 String message = sb.toString(); 329 330 LOG.debug( message ); 331 LOG_KRB.debug( message ); 332 } 333 catch ( Exception e ) 334 { 335 // This is a monitor. No exceptions should bubble up. 336 LOG.error( I18n.err( I18n.ERR_155 ), e ); 337 LOG_KRB.error( I18n.err( I18n.ERR_155 ), e ); 338 } 339 } 340 341 342 public void inputClosed( IoSession session ) 343 { 344 session.closeNow(); 345 } 346}