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.codec;
021
022
023import java.nio.ByteBuffer;
024
025import org.apache.directory.api.asn1.DecoderException;
026import org.apache.directory.api.asn1.ber.Asn1Decoder;
027import org.apache.directory.api.asn1.ber.tlv.TLVStateEnum;
028import org.apache.directory.api.ldap.model.constants.Loggers;
029import org.apache.directory.api.util.Strings;
030import org.apache.directory.shared.kerberos.codec.KerberosMessageContainer;
031import org.apache.mina.core.buffer.IoBuffer;
032import org.apache.mina.core.session.IoSession;
033import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
034import org.apache.mina.filter.codec.ProtocolDecoderOutput;
035import org.slf4j.Logger;
036import org.slf4j.LoggerFactory;
037
038
039/**
040 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
041 */
042public class MinaKerberosDecoder extends CumulativeProtocolDecoder
043{
044    /** the key used while storing message container in the session */
045    private static final String KERBEROS_MESSAGE_CONTAINER = "kerberosMessageContainer";
046
047    /** The ASN 1 decoder instance */
048    private Asn1Decoder asn1Decoder = new Asn1Decoder();
049
050    private static final int DEFAULT_MAX_PDU_SIZE = 1024 * 7; // 7KB
051    
052    /** the maximum allowed PDU size for a Kerberos request */
053    private int maxPduSize = DEFAULT_MAX_PDU_SIZE;
054    
055    private static final Logger LOG_KRB = LoggerFactory.getLogger( Loggers.KERBEROS_LOG.getName() );
056    
057    /** A speedup for logger */
058    private static final boolean IS_DEBUG = LOG_KRB.isDebugEnabled();
059
060    @Override
061    public boolean doDecode( IoSession session, IoBuffer in, ProtocolDecoderOutput out ) throws Exception
062    {
063        ByteBuffer incomingBuf = in.buf();
064
065        KerberosMessageContainer krbMsgContainer = ( KerberosMessageContainer ) session
066            .getAttribute( KERBEROS_MESSAGE_CONTAINER );
067
068        if ( krbMsgContainer == null )
069        {
070            krbMsgContainer = new KerberosMessageContainer();
071            krbMsgContainer.setMaxPDUSize( maxPduSize );
072            session.setAttribute( KERBEROS_MESSAGE_CONTAINER, krbMsgContainer );
073            krbMsgContainer.setGathering( true );
074            
075            boolean tcp = !session.getTransportMetadata().isConnectionless();
076            krbMsgContainer.setTCP( tcp );
077            
078            if ( tcp )
079            {
080                if ( incomingBuf.remaining() > 4 )
081                {
082                    int len = incomingBuf.getInt();
083                    
084                    if ( len > maxPduSize )
085                    {
086                        session.removeAttribute( KERBEROS_MESSAGE_CONTAINER );
087                        
088                        String err = "Request length %d exceeds allowed max PDU size %d";
089                        err = String.format( err, len, maxPduSize );
090                        
091                        throw new DecoderException( err );
092                    }
093                    
094                    krbMsgContainer.setTcpLength( len );
095                    incomingBuf.mark();
096                    
097                    ByteBuffer tmp = ByteBuffer.allocate( len );
098                    tmp.put( incomingBuf );
099                    
100                    krbMsgContainer.setStream( tmp );
101                }
102                else
103                {
104                    String err = "Could not determine the length of TCP buffer";
105                    LOG_KRB.warn( "{} {}", err, Strings.dumpBytes( incomingBuf.array() ) );
106                    throw new IllegalStateException( err );
107                }
108            }
109            else // UDP
110            {
111                krbMsgContainer.setStream( incomingBuf );
112            }
113        }
114        else // must be a fragmented TCP stream, copy the incomingBuf into the existing buffer of the container
115        {
116            int totLen = incomingBuf.limit() + krbMsgContainer.getStream().position();
117            if ( totLen > maxPduSize )
118            {
119                session.removeAttribute( KERBEROS_MESSAGE_CONTAINER );
120                
121                String err = "Total length of recieved bytes %d exceeds allowed max PDU size %d";
122                err = String.format( err, totLen, maxPduSize );
123                
124                throw new DecoderException( err );
125            }
126            
127            krbMsgContainer.getStream().put( incomingBuf );
128        }
129
130        if ( krbMsgContainer.isTCP() )
131        {
132            int curLen = krbMsgContainer.getStream().position();
133            if ( curLen < krbMsgContainer.getTcpLength() )
134            {
135                return false;
136            }
137        }
138
139        try
140        {
141            ByteBuffer stream = krbMsgContainer.getStream();
142            if ( stream.position() != 0 )
143            {
144                stream.flip();
145            }
146            
147            asn1Decoder.decode( stream, krbMsgContainer );
148            
149            if ( krbMsgContainer.getState() == TLVStateEnum.PDU_DECODED )
150            {
151                if ( IS_DEBUG )
152                {
153                    LOG_KRB.debug( "Decoded KerberosMessage : " + krbMsgContainer.getMessage() );
154                    incomingBuf.mark();
155                }
156                
157                out.write( krbMsgContainer.getMessage() );
158                
159                return true;
160            }
161        }
162        catch ( DecoderException de )
163        {
164            LOG_KRB.warn( "Error while decoding kerberos message", de );
165            incomingBuf.clear();
166            krbMsgContainer.clean();
167            throw de;
168        }
169        finally
170        {
171            session.removeAttribute( KERBEROS_MESSAGE_CONTAINER );
172        }
173        
174        throw new DecoderException( "Invalid buffer" );
175   }
176
177    
178    /**
179     * @return the maxPduSize
180     */
181    public int getMaxPduSize()
182    {
183        return maxPduSize;
184    }
185
186    
187    /**
188     * @param maxPduSize the maxPduSize to set
189     */
190    public void setMaxPduSize( int maxPduSize )
191    {
192        this.maxPduSize = maxPduSize;
193    }
194}