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; 024import java.util.Locale; 025 026import org.apache.directory.api.asn1.DecoderException; 027import org.apache.directory.api.asn1.ber.Asn1Decoder; 028import org.apache.directory.api.asn1.ber.tlv.TLVStateEnum; 029import org.apache.directory.api.ldap.model.constants.Loggers; 030import org.apache.directory.api.util.Strings; 031import org.apache.directory.shared.kerberos.codec.KerberosMessageContainer; 032import org.apache.mina.core.buffer.IoBuffer; 033import org.apache.mina.core.session.IoSession; 034import org.apache.mina.filter.codec.CumulativeProtocolDecoder; 035import org.apache.mina.filter.codec.ProtocolDecoderOutput; 036import org.slf4j.Logger; 037import org.slf4j.LoggerFactory; 038 039 040/** 041 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 042 */ 043public class MinaKerberosDecoder extends CumulativeProtocolDecoder 044{ 045 /** the key used while storing message container in the session */ 046 private static final String KERBEROS_MESSAGE_CONTAINER = "kerberosMessageContainer"; 047 048 private static final int DEFAULT_MAX_PDU_SIZE = 1024 * 7; // 7KB 049 050 /** the maximum allowed PDU size for a Kerberos request */ 051 private int maxPduSize = DEFAULT_MAX_PDU_SIZE; 052 053 private static final Logger LOG_KRB = LoggerFactory.getLogger( Loggers.KERBEROS_LOG.getName() ); 054 055 /** A speedup for logger */ 056 private static final boolean IS_DEBUG = LOG_KRB.isDebugEnabled(); 057 058 @Override 059 public boolean doDecode( IoSession session, IoBuffer in, ProtocolDecoderOutput out ) throws Exception 060 { 061 ByteBuffer incomingBuf = in.buf(); 062 063 KerberosMessageContainer krbMsgContainer = ( KerberosMessageContainer ) session 064 .getAttribute( KERBEROS_MESSAGE_CONTAINER ); 065 066 if ( krbMsgContainer == null ) 067 { 068 krbMsgContainer = new KerberosMessageContainer(); 069 krbMsgContainer.setMaxPDUSize( maxPduSize ); 070 session.setAttribute( KERBEROS_MESSAGE_CONTAINER, krbMsgContainer ); 071 krbMsgContainer.setGathering( true ); 072 073 boolean tcp = !session.getTransportMetadata().isConnectionless(); 074 krbMsgContainer.setTCP( tcp ); 075 076 if ( tcp ) 077 { 078 if ( incomingBuf.remaining() > 4 ) 079 { 080 int len = incomingBuf.getInt(); 081 082 if ( len > maxPduSize ) 083 { 084 session.removeAttribute( KERBEROS_MESSAGE_CONTAINER ); 085 086 String err = "Request length %d exceeds allowed max PDU size %d"; 087 err = String.format( Locale.ROOT, err, len, maxPduSize ); 088 089 throw new DecoderException( err ); 090 } 091 092 krbMsgContainer.setTcpLength( len ); 093 incomingBuf.mark(); 094 095 ByteBuffer tmp = ByteBuffer.allocate( len ); 096 tmp.put( incomingBuf ); 097 098 krbMsgContainer.setStream( tmp ); 099 } 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}