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 */ 020 021package org.apache.directory.shared.kerberos.components; 022 023 024import java.nio.BufferOverflowException; 025import java.nio.ByteBuffer; 026import java.util.ArrayList; 027import java.util.List; 028 029import org.apache.directory.api.asn1.Asn1Object; 030import org.apache.directory.api.asn1.EncoderException; 031import org.apache.directory.api.asn1.ber.tlv.BerValue; 032import org.apache.directory.api.asn1.ber.tlv.TLV; 033import org.apache.directory.api.asn1.ber.tlv.UniversalTag; 034import org.apache.directory.api.util.Strings; 035import org.apache.directory.server.i18n.I18n; 036import org.apache.directory.shared.kerberos.KerberosConstants; 037import org.slf4j.Logger; 038import org.slf4j.LoggerFactory; 039 040 041/** 042 * TYPED-DATA ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE { 043 * data-type [0] Int32, 044 * data-value [1] OCTET STRING OPTIONAL 045 * } 046 * 047 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 048 */ 049public class TypedData implements Asn1Object 050{ 051 052 // The inner class storing the individual TDs 053 public class TD 054 { 055 /** the type of TypedData */ 056 private int dataType; 057 058 /** the TypedData data */ 059 private byte[] dataValue; 060 061 062 /** 063 * @return the TD type 064 */ 065 public int getDataType() 066 { 067 return dataType; 068 } 069 070 071 /** 072 * @return the TD data 073 */ 074 public byte[] getDataValue() 075 { 076 return dataValue; 077 } 078 } 079 080 /** The list of TypedData elements */ 081 private List<TD> typedDataList = new ArrayList<>(); 082 083 /** The current TD being processed */ 084 private TD currentTD; 085 086 /** The logger */ 087 private static final Logger LOG = LoggerFactory.getLogger( TypedData.class ); 088 089 /** Speedup for logs */ 090 private static final boolean IS_DEBUG = LOG.isDebugEnabled(); 091 092 // Storage for computed lengths 093 private int dataTypeTagLength[]; 094 private int dataValueTagLength[]; 095 private int typedDataSeqLength[]; 096 private int typedDataSeqSeqLength; 097 098 099 /** 100 * @return the currentTD type 101 */ 102 public int getCurrentDataType() 103 { 104 return currentTD.dataType; 105 } 106 107 108 /** 109 * Set the current TD type 110 */ 111 public void setCurrentDataType( int tdType ) 112 { 113 currentTD.dataType = tdType; 114 } 115 116 117 /** 118 * @return the currentTD data 119 */ 120 public byte[] getCurrentDataValue() 121 { 122 return currentTD.dataValue; 123 } 124 125 126 /** 127 * Set the current TD data 128 */ 129 public void setCurrentDataValue( byte[] tdData ) 130 { 131 currentTD.dataValue = tdData; 132 } 133 134 135 /** 136 * @return the currentTD 137 */ 138 public TD getCurrentTD() 139 { 140 return currentTD; 141 } 142 143 144 /** 145 * Create a new currentTD 146 */ 147 public void createNewTD() 148 { 149 currentTD = new TD(); 150 typedDataList.add( currentTD ); 151 } 152 153 154 /** 155 * @return the TypedData 156 */ 157 public List<TD> getTypedData() 158 { 159 return typedDataList; 160 } 161 162 163 /** 164 * Compute the TypedData length 165 * <pre> 166 * 0x30 L1 TypedData sequence 167 * | 168 * +-- 0x30 L2 The TD sequence 169 * | 170 * +--> 0xA0 L3 tdType tag 171 * | | 172 * | +--> 0x02 L3-2 tdType (int) 173 * | 174 * +--> [0xA1 L4 tdData tag 175 * | 176 * +--> 0x04 L4-2 tdData (OCTET STRING)] 177 * </pre> 178 */ 179 @Override 180 public int computeLength() 181 { 182 int i = 0; 183 typedDataSeqLength = new int[typedDataList.size()]; 184 dataTypeTagLength = new int[typedDataList.size()]; 185 dataValueTagLength = new int[typedDataList.size()]; 186 typedDataSeqSeqLength = 0; 187 188 for ( TD td : typedDataList ) 189 { 190 int adTypeLen = BerValue.getNbBytes( td.dataType ); 191 dataTypeTagLength[i] = 1 + TLV.getNbBytes( adTypeLen ) + adTypeLen; 192 typedDataSeqLength[i] = 1 + TLV.getNbBytes( dataTypeTagLength[i] ) + dataTypeTagLength[i]; 193 194 if ( td.dataValue != null ) 195 { 196 dataValueTagLength[i] = 1 + TLV.getNbBytes( td.dataValue.length ) + td.dataValue.length; 197 typedDataSeqLength[i] += 1 + TLV.getNbBytes( dataValueTagLength[i] ) + dataValueTagLength[i]; 198 } 199 200 typedDataSeqSeqLength += 1 + TLV.getNbBytes( typedDataSeqLength[i] ) + typedDataSeqLength[i]; 201 i++; 202 } 203 204 return 1 + TLV.getNbBytes( typedDataSeqSeqLength ) + typedDataSeqSeqLength; 205 } 206 207 208 /** 209 * {@inheritDoc} 210 */ 211 @Override 212 public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException 213 { 214 if ( buffer == null ) 215 { 216 throw new EncoderException( I18n.err( I18n.ERR_148 ) ); 217 } 218 219 try 220 { 221 // The AuthorizationData SEQ OF Tag 222 buffer.put( UniversalTag.SEQUENCE.getValue() ); 223 buffer.put( TLV.getBytes( typedDataSeqSeqLength ) ); 224 225 int i = 0; 226 for ( TD td : typedDataList ) 227 { 228 buffer.put( UniversalTag.SEQUENCE.getValue() ); 229 buffer.put( TLV.getBytes( typedDataSeqLength[i] ) ); 230 231 // the tdType 232 buffer.put( ( byte ) KerberosConstants.TYPED_DATA_TDTYPE_TAG ); 233 buffer.put( TLV.getBytes( dataTypeTagLength[i] ) ); 234 BerValue.encode( buffer, td.dataType ); 235 236 if ( td.dataValue != null ) 237 { 238 // the tdData 239 buffer.put( ( byte ) KerberosConstants.TYPED_DATA_TDDATA_TAG ); 240 buffer.put( TLV.getBytes( dataValueTagLength[i] ) ); 241 BerValue.encode( buffer, td.dataValue ); 242 } 243 244 i++; 245 } 246 } 247 catch ( BufferOverflowException boe ) 248 { 249 LOG.error( I18n.err( I18n.ERR_743_CANNOT_ENCODE_TYPED_DATA, 1 + TLV.getNbBytes( typedDataSeqSeqLength ) 250 + typedDataSeqSeqLength, buffer.capacity() ) ); 251 throw new EncoderException( I18n.err( I18n.ERR_138 ), boe ); 252 } 253 254 if ( IS_DEBUG ) 255 { 256 LOG.debug( "TypedData encoding : {}", Strings.dumpBytes( buffer.array() ) ); 257 LOG.debug( "TypedData initial value : {}", this ); 258 } 259 260 return buffer; 261 } 262 263 264 /** 265 * @see Object#toString() 266 */ 267 public String toString() 268 { 269 return toString( "" ); 270 } 271 272 273 /** 274 * @see Object#toString() 275 */ 276 public String toString( String tabs ) 277 { 278 StringBuilder sb = new StringBuilder(); 279 280 sb.append( tabs ).append( "TypedData : \n" ); 281 282 for ( TD td : typedDataList ) 283 { 284 sb.append( tabs ).append( " {\n" ); 285 sb.append( tabs ).append( " tdType: " ).append( td.dataType ).append( '\n' ); 286 if ( td.dataValue != null ) 287 { 288 sb.append( tabs ).append( " tdData: " ).append( Strings.dumpBytes( td.dataValue ) ) 289 .append( '\n' ); 290 } 291 sb.append( tabs ).append( " }\n" ); 292 } 293 294 return sb.toString(); 295 } 296}