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}