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.shared.kerberos.components; 021 022 023import java.nio.BufferOverflowException; 024import java.nio.ByteBuffer; 025import java.util.ArrayList; 026import java.util.List; 027 028import org.apache.directory.api.asn1.Asn1Object; 029import org.apache.directory.api.asn1.EncoderException; 030import org.apache.directory.api.asn1.ber.tlv.BerValue; 031import org.apache.directory.api.asn1.ber.tlv.TLV; 032import org.apache.directory.api.asn1.ber.tlv.UniversalTag; 033import org.apache.directory.api.util.Strings; 034import org.apache.directory.server.i18n.I18n; 035import org.apache.directory.shared.kerberos.codec.types.PrincipalNameType; 036import org.slf4j.Logger; 037import org.slf4j.LoggerFactory; 038 039 040/** 041 * A principal Name, composed of a type and N names. 042 * <pre> 043 * PrincipalName ::= SEQUENCE { 044 * name-type [0] Int32, 045 * name-string [1] SEQUENCE OF KerberosString 046 * } 047 * </pre> 048 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 049 */ 050public class PrincipalName implements Asn1Object 051{ 052 /** The logger */ 053 private static final Logger LOG = LoggerFactory.getLogger( PrincipalName.class ); 054 055 /** Speedup for logs */ 056 private static final boolean IS_DEBUG = LOG.isDebugEnabled(); 057 058 /** The type for this principal */ 059 private PrincipalNameType nameType; 060 061 /** The principal name - we may have more than one - */ 062 private List<String> nameString = new ArrayList<>(); 063 064 /** The principal name as a byte[], for encoding purpose */ 065 private List<byte[]> nameBytes; 066 067 // Storage for computed lengths 068 private int principalNameSeqLength; 069 private int principalTypeTagLength; 070 private int principalTypeLength; 071 private int principalStringsTagLength; 072 private int principalStringsSeqLength; 073 074 075 /** 076 * Creates a new empty instance of PrincipalName. 077 */ 078 public PrincipalName() 079 { 080 } 081 082 083 /** 084 * Returns the type of the {@link PrincipalName}. 085 * 086 * @return The type of the {@link PrincipalName}. 087 */ 088 public PrincipalNameType getNameType() 089 { 090 return nameType; 091 } 092 093 094 /** 095 * Set the Principal name Type 096 * @param nameType the Principal name Type 097 */ 098 public void setNameType( PrincipalNameType nameType ) 099 { 100 this.nameType = nameType; 101 } 102 103 104 /** 105 * @return A String representing the principal names as a String 106 */ 107 public String getNameString() 108 { 109 if ( ( nameString == null ) || nameString.isEmpty() ) 110 { 111 return ""; 112 } 113 else 114 { 115 StringBuilder sb = new StringBuilder(); 116 boolean isFirst = true; 117 118 for ( String name : nameString ) 119 { 120 if ( isFirst ) 121 { 122 isFirst = false; 123 } 124 else 125 { 126 sb.append( '/' ); 127 } 128 129 sb.append( name ); 130 } 131 132 return sb.toString(); 133 } 134 } 135 136 137 /** 138 * Add a new name to the PrincipalName 139 * @param name The name to add 140 */ 141 public void addName( String name ) 142 { 143 if ( nameString == null ) 144 { 145 nameString = new ArrayList<>(); 146 } 147 148 nameString.add( name ); 149 } 150 151 152 /** 153 * Compute the PrincipalName length 154 * <pre> 155 * PrincipalName : 156 * 157 * 0x30 L1 PrincipalName sequence 158 * | 159 * +--> 0xA1 L2 name-type tag 160 * | | 161 * | +--> 0x02 L2-1 addressType (int) 162 * | 163 * +--> 0xA2 L3 name-string tag 164 * | 165 * +--> 0x30 L3-1 name-string (SEQUENCE OF KerberosString) 166 * | 167 * +--> 0x1B L4[1] value (KerberosString) 168 * | 169 * +--> 0x1B L4[2] value (KerberosString) 170 * | 171 * ... 172 * | 173 * +--> 0x1B L4[n] value (KerberosString) 174 * </pre> 175 */ 176 public int computeLength() 177 { 178 // The principalName can't be empty. 179 principalTypeLength = BerValue.getNbBytes( nameType.getValue() ); 180 principalTypeTagLength = 1 + 1 + principalTypeLength; 181 182 principalNameSeqLength = 1 + TLV.getNbBytes( principalTypeTagLength ) + principalTypeTagLength; 183 184 // Compute the keyValue 185 if ( ( nameString == null ) || nameString.isEmpty() ) 186 { 187 principalStringsSeqLength = 0; 188 } 189 else 190 { 191 principalStringsSeqLength = 0; 192 nameBytes = new ArrayList<>( nameString.size() ); 193 194 for ( String name : nameString ) 195 { 196 if ( name != null ) 197 { 198 byte[] bytes = Strings.getBytesUtf8( name ); 199 nameBytes.add( bytes ); 200 principalStringsSeqLength += 1 + TLV.getNbBytes( bytes.length ) + bytes.length; 201 } 202 else 203 { 204 nameBytes.add( Strings.EMPTY_BYTES ); 205 principalStringsSeqLength += 1 + 1; 206 } 207 } 208 } 209 210 principalStringsTagLength = 1 + TLV.getNbBytes( principalStringsSeqLength ) + principalStringsSeqLength; 211 principalNameSeqLength += 1 + TLV.getNbBytes( principalStringsTagLength ) + principalStringsTagLength; 212 213 // Compute the whole sequence length 214 return 1 + TLV.getNbBytes( principalNameSeqLength ) + principalNameSeqLength; 215 } 216 217 218 /** 219 * Encode the PrincipalName message to a PDU. 220 * <pre> 221 * PrincipalName : 222 * 223 * 0x30 LL 224 * 0xA0 LL 225 * 0x02 0x01 name-type (integer) 226 * 0xA1 LL 227 * 0x30 LL name-string (SEQUENCE OF KerberosString) 228 * 0x1B LL name-string[1] 229 * 0x1B LL name-string[2] 230 * ... 231 * 0x1B LL name-string[n] 232 * </pre> 233 * @param buffer The buffer where to put the PDU. It should have been allocated 234 * before, with the right size. 235 * @return The constructed PDU. 236 */ 237 public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException 238 { 239 if ( buffer == null ) 240 { 241 throw new EncoderException( I18n.err( I18n.ERR_148 ) ); 242 } 243 244 try 245 { 246 // The PrincipalName SEQ Tag 247 buffer.put( UniversalTag.SEQUENCE.getValue() ); 248 buffer.put( TLV.getBytes( principalNameSeqLength ) ); 249 250 // The name-type, first the tag, then the value 251 buffer.put( ( byte ) 0xA0 ); 252 buffer.put( TLV.getBytes( principalTypeTagLength ) ); 253 BerValue.encode( buffer, nameType.getValue() ); 254 255 // The name-string tag 256 buffer.put( ( byte ) 0xA1 ); 257 buffer.put( TLV.getBytes( principalStringsTagLength ) ); 258 259 // The name-string sequence 260 buffer.put( UniversalTag.SEQUENCE.getValue() ); 261 262 if ( ( nameString == null ) || nameString.isEmpty() ) 263 { 264 buffer.put( ( byte ) 0x00 ); 265 } 266 else 267 { 268 buffer.put( TLV.getBytes( principalStringsSeqLength ) ); 269 270 // The kerberosStrings 271 for ( byte[] name : nameBytes ) 272 { 273 buffer.put( UniversalTag.GENERAL_STRING.getValue() ); 274 275 if ( ( name == null ) || ( name.length == 0 ) ) 276 { 277 buffer.put( ( byte ) 0x00 ); 278 } 279 else 280 { 281 buffer.put( TLV.getBytes( name.length ) ); 282 buffer.put( name ); 283 } 284 } 285 } 286 } 287 catch ( BufferOverflowException boe ) 288 { 289 LOG.error( I18n.err( I18n.ERR_146, 1 + TLV.getNbBytes( principalNameSeqLength ) 290 + principalNameSeqLength, buffer.capacity() ) ); 291 throw new EncoderException( I18n.err( I18n.ERR_138 ), boe ); 292 } 293 294 if ( IS_DEBUG ) 295 { 296 LOG.debug( "PrinipalName encoding : {}", Strings.dumpBytes( buffer.array() ) ); 297 LOG.debug( "PrinipalName initial value : {}", this ); 298 } 299 300 return buffer; 301 } 302 303 304 /** 305 * @see Object#toString() 306 */ 307 public String toString() 308 { 309 StringBuilder sb = new StringBuilder(); 310 311 sb.append( "{ " ); 312 313 sb.append( "name-type: " ).append( nameType.name() ); 314 315 if ( ( nameString != null ) && !nameString.isEmpty() ) 316 { 317 sb.append( ", name-string : <" ); 318 boolean isFirst = true; 319 320 for ( String name : nameString ) 321 { 322 if ( isFirst ) 323 { 324 isFirst = false; 325 } 326 else 327 { 328 sb.append( ", " ); 329 } 330 331 sb.append( '\'' ).append( name ).append( '\'' ); 332 } 333 334 sb.append( ">" ); 335 } 336 else 337 { 338 sb.append( " no name-string" ); 339 } 340 341 sb.append( " }" ); 342 343 return sb.toString(); 344 } 345 346 347 @Override 348 public int hashCode() 349 { 350 final int prime = 31; 351 int result = 1; 352 result = prime * result + ( ( nameString == null ) ? 0 : nameString.hashCode() ); 353 result = prime * result + ( ( nameType == null ) ? 0 : nameType.hashCode() ); 354 return result; 355 } 356 357 358 /** 359 * {@inheritDoc} 360 */ 361 @Override 362 public boolean equals( Object obj ) 363 { 364 if ( this == obj ) 365 { 366 return true; 367 } 368 369 if ( !( obj instanceof PrincipalName ) ) 370 { 371 return false; 372 } 373 374 PrincipalName other = ( PrincipalName ) obj; 375 376 if ( nameString == null ) 377 { 378 if ( other.nameString != null ) 379 { 380 return false; 381 } 382 } 383 else if ( !nameString.equals( other.nameString ) ) 384 { 385 return false; 386 } 387 388 return nameType == other.nameType; 389 } 390 391}