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  package org.apache.directory.server.kerberos.protocol.codec;
21  
22  
23  import java.nio.ByteBuffer;
24  import java.util.Locale;
25  
26  import org.apache.directory.api.asn1.DecoderException;
27  import org.apache.directory.api.asn1.ber.Asn1Decoder;
28  import org.apache.directory.api.asn1.ber.tlv.TLVStateEnum;
29  import org.apache.directory.api.ldap.model.constants.Loggers;
30  import org.apache.directory.api.util.Strings;
31  import org.apache.directory.shared.kerberos.codec.KerberosMessageContainer;
32  import org.apache.mina.core.buffer.IoBuffer;
33  import org.apache.mina.core.session.IoSession;
34  import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
35  import org.apache.mina.filter.codec.ProtocolDecoderOutput;
36  import org.slf4j.Logger;
37  import org.slf4j.LoggerFactory;
38  
39  
40  /**
41   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
42   */
43  public class MinaKerberosDecoder extends CumulativeProtocolDecoder
44  {
45      /** the key used while storing message container in the session */
46      private static final String KERBEROS_MESSAGE_CONTAINER = "kerberosMessageContainer";
47  
48      private static final int DEFAULT_MAX_PDU_SIZE = 1024 * 7; // 7KB
49      
50      /** the maximum allowed PDU size for a Kerberos request */
51      private int maxPduSize = DEFAULT_MAX_PDU_SIZE;
52      
53      private static final Logger LOG_KRB = LoggerFactory.getLogger( Loggers.KERBEROS_LOG.getName() );
54      
55      /** A speedup for logger */
56      private static final boolean IS_DEBUG = LOG_KRB.isDebugEnabled();
57  
58      @Override
59      public boolean doDecode( IoSession session, IoBuffer in, ProtocolDecoderOutput out ) throws Exception
60      {
61          ByteBuffer incomingBuf = in.buf();
62  
63          KerberosMessageContainerche/directory/shared/kerberos/codec/KerberosMessageContainer.html#KerberosMessageContainer">KerberosMessageContainer krbMsgContainer = ( KerberosMessageContainer ) session
64              .getAttribute( KERBEROS_MESSAGE_CONTAINER );
65  
66          if ( krbMsgContainer == null )
67          {
68              krbMsgContainer = new KerberosMessageContainer();
69              krbMsgContainer.setMaxPDUSize( maxPduSize );
70              session.setAttribute( KERBEROS_MESSAGE_CONTAINER, krbMsgContainer );
71              krbMsgContainer.setGathering( true );
72              
73              boolean tcp = !session.getTransportMetadata().isConnectionless();
74              krbMsgContainer.setTCP( tcp );
75              
76              if ( tcp )
77              {
78                  if ( incomingBuf.remaining() > 4 )
79                  {
80                      int len = incomingBuf.getInt();
81                      
82                      if ( len > maxPduSize )
83                      {
84                          session.removeAttribute( KERBEROS_MESSAGE_CONTAINER );
85                          
86                          String err = "Request length %d exceeds allowed max PDU size %d";
87                          err = String.format( Locale.ROOT, err, len, maxPduSize );
88                          
89                          throw new DecoderException( err );
90                      }
91                      
92                      krbMsgContainer.setTcpLength( len );
93                      incomingBuf.mark();
94                      
95                      ByteBuffer tmp = ByteBuffer.allocate( len );
96                      tmp.put( incomingBuf );
97                      
98                      krbMsgContainer.setStream( tmp );
99                  }
100                 else
101                 {
102                     String err = "Could not determine the length of TCP buffer";
103                     LOG_KRB.warn( "{} {}", err, Strings.dumpBytes( incomingBuf.array() ) );
104                     throw new IllegalStateException( err );
105                 }
106             }
107             else // UDP
108             {
109                 krbMsgContainer.setStream( incomingBuf );
110             }
111         }
112         else // must be a fragmented TCP stream, copy the incomingBuf into the existing buffer of the container
113         {
114             int totLen = incomingBuf.limit() + krbMsgContainer.getStream().position();
115             if ( totLen > maxPduSize )
116             {
117                 session.removeAttribute( KERBEROS_MESSAGE_CONTAINER );
118                 
119                 String err = "Total length of recieved bytes %d exceeds allowed max PDU size %d";
120                 err = String.format( Locale.ROOT, err, totLen, maxPduSize );
121                 
122                 throw new DecoderException( err );
123             }
124             
125             krbMsgContainer.getStream().put( incomingBuf );
126         }
127 
128         if ( krbMsgContainer.isTCP() )
129         {
130             int curLen = krbMsgContainer.getStream().position();
131             if ( curLen < krbMsgContainer.getTcpLength() )
132             {
133                 return false;
134             }
135         }
136 
137         try
138         {
139             ByteBuffer stream = krbMsgContainer.getStream();
140             if ( stream.position() != 0 )
141             {
142                 stream.flip();
143             }
144             
145             Asn1Decoder.decode( stream, krbMsgContainer );
146             
147             if ( krbMsgContainer.getState() == TLVStateEnum.PDU_DECODED )
148             {
149                 if ( IS_DEBUG )
150                 {
151                     LOG_KRB.debug( "Decoded KerberosMessage : {}", krbMsgContainer.getMessage() );
152                     incomingBuf.mark();
153                 }
154                 
155                 out.write( krbMsgContainer.getMessage() );
156                 
157                 return true;
158             }
159         }
160         catch ( DecoderException de )
161         {
162             LOG_KRB.warn( "Error while decoding kerberos message", de );
163             incomingBuf.clear();
164             krbMsgContainer.clean();
165             throw de;
166         }
167         finally
168         {
169             session.removeAttribute( KERBEROS_MESSAGE_CONTAINER );
170         }
171         
172         throw new DecoderException( "Invalid buffer" );
173    }
174 
175     
176     /**
177      * @return the maxPduSize
178      */
179     public int getMaxPduSize()
180     {
181         return maxPduSize;
182     }
183 
184     
185     /**
186      * @param maxPduSize the maxPduSize to set
187      */
188     public void setMaxPduSize( int maxPduSize )
189     {
190         this.maxPduSize = maxPduSize;
191     }
192 }