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.text.ParseException; 026import java.util.ArrayList; 027import java.util.List; 028 029import javax.security.auth.kerberos.KerberosPrincipal; 030 031import org.apache.directory.api.asn1.Asn1Object; 032import org.apache.directory.api.asn1.EncoderException; 033import org.apache.directory.api.asn1.ber.tlv.BerValue; 034import org.apache.directory.api.asn1.ber.tlv.TLV; 035import org.apache.directory.api.asn1.ber.tlv.UniversalTag; 036import org.apache.directory.api.util.Strings; 037import org.apache.directory.server.i18n.I18n; 038import org.apache.directory.shared.kerberos.KerberosUtils; 039import org.apache.directory.shared.kerberos.codec.types.PrincipalNameType; 040import org.slf4j.Logger; 041import org.slf4j.LoggerFactory; 042 043 044/** 045 * A principal Name, composed of a type and N names. 046 * <pre> 047 * PrincipalName ::= SEQUENCE { 048 * name-type [0] Int32, 049 * name-string [1] SEQUENCE OF KerberosString 050 * } 051 * </pre> 052 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 053 */ 054public class PrincipalName implements Asn1Object 055{ 056 /** The logger */ 057 private static final Logger LOG = LoggerFactory.getLogger( PrincipalName.class ); 058 059 /** Speedup for logs */ 060 private static final boolean IS_DEBUG = LOG.isDebugEnabled(); 061 062 /** The type for this principal */ 063 private PrincipalNameType nameType; 064 065 /** The principal name - we may have more than one - */ 066 private List<String> nameString = new ArrayList<>(); 067 068 /** The realm part */ 069 private String realm; 070 071 /** The principal name as a byte[], for encoding purpose */ 072 private List<byte[]> nameBytes; 073 074 // Storage for computed lengths 075 private int principalNameSeqLength; 076 private int principalTypeTagLength; 077 private int principalTypeLength; 078 private int principalStringsTagLength; 079 private int principalStringsSeqLength; 080 081 082 /** 083 * Creates a new empty instance of PrincipalName. 084 */ 085 public PrincipalName() 086 { 087 } 088 089 090 /** 091 * Creates a new instance of PrincipalName, given a KerberosPrincipal. 092 * 093 * We assume that a principal has only one type, even if there are 094 * more than one name component. 095 * 096 * @param principal A Sun kerberosPrincipal instance 097 */ 098 public PrincipalName( KerberosPrincipal principal ) 099 { 100 try 101 { 102 nameString = KerberosUtils.getNames( principal ); 103 realm = principal.getRealm(); 104 } 105 catch ( ParseException pe ) 106 { 107 nameString = KerberosUtils.EMPTY_PRINCIPAL_NAME; 108 } 109 110 this.nameType = PrincipalNameType.getTypeByValue( principal.getNameType() ); 111 } 112 113 114 /** 115 * Creates a new instance of PrincipalName given a String and an 116 * principal type. 117 * 118 * @param nameString The name string, which can contains more than one nameComponent 119 * @param nameType The principal name 120 */ 121 public PrincipalName( String nameString, PrincipalNameType nameType ) throws ParseException 122 { 123 this.nameString = KerberosUtils.getNames( nameString ); 124 this.nameType = nameType; 125 } 126 127 128 /** 129 * Creates a new instance of PrincipalName given a String[] and an 130 * principal type. 131 * 132 * @param nameParts The name string, which can contains more than one nameComponent 133 * @param nameType The principal name type 134 */ 135 public PrincipalName( String[] nameParts, int nameType ) 136 { 137 if ( nameParts == null || nameParts.length == 0 ) 138 { 139 throw new IllegalArgumentException( "Empty name parts" ); 140 } 141 142 List<String> nameComponents = new ArrayList<>(); 143 for ( String np : nameParts ) 144 { 145 nameComponents.add( np ); 146 } 147 148 this.nameString = nameComponents; 149 this.nameType = PrincipalNameType.getTypeByValue( nameType ); 150 } 151 152 153 /** 154 * Creates a new instance of PrincipalName. 155 * 156 * @param nameString 157 * @param nameType 158 */ 159 public PrincipalName( String nameString, int nameType ) 160 { 161 try 162 { 163 this.nameString = KerberosUtils.getNames( nameString ); 164 } 165 catch ( ParseException pe ) 166 { 167 throw new IllegalArgumentException( pe ); 168 } 169 170 this.nameType = PrincipalNameType.getTypeByValue( nameType ); 171 } 172 173 174 /** 175 * Returns the type of the {@link PrincipalName}. 176 * 177 * @return The type of the {@link PrincipalName}. 178 */ 179 public PrincipalNameType getNameType() 180 { 181 return nameType; 182 } 183 184 185 /** 186 * Set the Principal name Type 187 * @param nameType the Principal name Type 188 */ 189 public void setNameType( PrincipalNameType nameType ) 190 { 191 this.nameType = nameType; 192 } 193 194 195 /** 196 * Set the Principal name Type 197 * @param nameType the Principal name Type 198 */ 199 public void setNameType( int nameType ) 200 { 201 this.nameType = PrincipalNameType.getTypeByValue( nameType ); 202 } 203 204 205 /** 206 * Set the realm for the principal 207 * @param realm the realm of the principal 208 */ 209 public void setRealm( String realm ) 210 { 211 this.realm = realm; 212 } 213 214 215 /** 216 * Get the realm for the principal 217 * @return realm the realm of the principal 218 */ 219 public String getRealm() 220 { 221 return realm; 222 } 223 224 225 /** 226 * Returns the name components. 227 * 228 * @return The name components. 229 */ 230 public List<String> getNames() 231 { 232 return nameString; 233 } 234 235 236 /** 237 * @return A String representing the principal names as a String 238 */ 239 public String getNameString() 240 { 241 if ( ( nameString == null ) || nameString.isEmpty() ) 242 { 243 return ""; 244 } 245 else 246 { 247 StringBuilder sb = new StringBuilder(); 248 boolean isFirst = true; 249 250 for ( String name : nameString ) 251 { 252 if ( isFirst ) 253 { 254 isFirst = false; 255 } 256 else 257 { 258 sb.append( '/' ); 259 } 260 261 sb.append( name ); 262 } 263 264 return sb.toString(); 265 } 266 } 267 268 269 /** 270 * Add a new name to the PrincipalName 271 * @param name The name to add 272 */ 273 public void addName( String name ) 274 { 275 if ( nameString == null ) 276 { 277 nameString = new ArrayList<>(); 278 } 279 280 nameString.add( name ); 281 } 282 283 284 /** 285 * Compute the PrincipalName length 286 * <pre> 287 * PrincipalName : 288 * 289 * 0x30 L1 PrincipalName sequence 290 * | 291 * +--> 0xA1 L2 name-type tag 292 * | | 293 * | +--> 0x02 L2-1 addressType (int) 294 * | 295 * +--> 0xA2 L3 name-string tag 296 * | 297 * +--> 0x30 L3-1 name-string (SEQUENCE OF KerberosString) 298 * | 299 * +--> 0x1B L4[1] value (KerberosString) 300 * | 301 * +--> 0x1B L4[2] value (KerberosString) 302 * | 303 * ... 304 * | 305 * +--> 0x1B L4[n] value (KerberosString) 306 * </pre> 307 */ 308 public int computeLength() 309 { 310 // The principalName can't be empty. 311 principalTypeLength = BerValue.getNbBytes( nameType.getValue() ); 312 principalTypeTagLength = 1 + 1 + principalTypeLength; 313 314 principalNameSeqLength = 1 + TLV.getNbBytes( principalTypeTagLength ) + principalTypeTagLength; 315 316 // Compute the keyValue 317 if ( ( nameString == null ) || nameString.isEmpty() ) 318 { 319 principalStringsSeqLength = 0; 320 } 321 else 322 { 323 principalStringsSeqLength = 0; 324 nameBytes = new ArrayList<>( nameString.size() ); 325 326 for ( String name : nameString ) 327 { 328 if ( name != null ) 329 { 330 byte[] bytes = Strings.getBytesUtf8( name ); 331 nameBytes.add( bytes ); 332 principalStringsSeqLength += 1 + TLV.getNbBytes( bytes.length ) + bytes.length; 333 } 334 else 335 { 336 nameBytes.add( Strings.EMPTY_BYTES ); 337 principalStringsSeqLength += 1 + 1; 338 } 339 } 340 } 341 342 principalStringsTagLength = 1 + TLV.getNbBytes( principalStringsSeqLength ) + principalStringsSeqLength; 343 principalNameSeqLength += 1 + TLV.getNbBytes( principalStringsTagLength ) + principalStringsTagLength; 344 345 // Compute the whole sequence length 346 return 1 + TLV.getNbBytes( principalNameSeqLength ) + principalNameSeqLength; 347 } 348 349 350 /** 351 * Encode the PrincipalName message to a PDU. 352 * <pre> 353 * PrincipalName : 354 * 355 * 0x30 LL 356 * 0xA0 LL 357 * 0x02 0x01 name-type (integer) 358 * 0xA1 LL 359 * 0x30 LL name-string (SEQUENCE OF KerberosString) 360 * 0x1B LL name-string[1] 361 * 0x1B LL name-string[2] 362 * ... 363 * 0x1B LL name-string[n] 364 * </pre> 365 * @param buffer The buffer where to put the PDU. It should have been allocated 366 * before, with the right size. 367 * @return The constructed PDU. 368 */ 369 public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException 370 { 371 if ( buffer == null ) 372 { 373 throw new EncoderException( I18n.err( I18n.ERR_148 ) ); 374 } 375 376 try 377 { 378 // The PrincipalName SEQ Tag 379 buffer.put( UniversalTag.SEQUENCE.getValue() ); 380 buffer.put( TLV.getBytes( principalNameSeqLength ) ); 381 382 // The name-type, first the tag, then the value 383 buffer.put( ( byte ) 0xA0 ); 384 buffer.put( TLV.getBytes( principalTypeTagLength ) ); 385 BerValue.encode( buffer, nameType.getValue() ); 386 387 // The name-string tag 388 buffer.put( ( byte ) 0xA1 ); 389 buffer.put( TLV.getBytes( principalStringsTagLength ) ); 390 391 // The name-string sequence 392 buffer.put( UniversalTag.SEQUENCE.getValue() ); 393 394 if ( ( nameString == null ) || nameString.isEmpty() ) 395 { 396 buffer.put( ( byte ) 0x00 ); 397 } 398 else 399 { 400 buffer.put( TLV.getBytes( principalStringsSeqLength ) ); 401 402 // The kerberosStrings 403 for ( byte[] name : nameBytes ) 404 { 405 buffer.put( UniversalTag.GENERAL_STRING.getValue() ); 406 407 if ( ( name == null ) || ( name.length == 0 ) ) 408 { 409 buffer.put( ( byte ) 0x00 ); 410 } 411 else 412 { 413 buffer.put( TLV.getBytes( name.length ) ); 414 buffer.put( name ); 415 } 416 } 417 } 418 } 419 catch ( BufferOverflowException boe ) 420 { 421 LOG.error( I18n.err( I18n.ERR_146, 1 + TLV.getNbBytes( principalNameSeqLength ) 422 + principalNameSeqLength, buffer.capacity() ) ); 423 throw new EncoderException( I18n.err( I18n.ERR_138 ), boe ); 424 } 425 426 if ( IS_DEBUG ) 427 { 428 LOG.debug( "PrinipalName encoding : {}", Strings.dumpBytes( buffer.array() ) ); 429 LOG.debug( "PrinipalName initial value : {}", this ); 430 } 431 432 return buffer; 433 } 434 435 436 /** 437 * @see Object#toString() 438 */ 439 public String toString() 440 { 441 StringBuilder sb = new StringBuilder(); 442 443 sb.append( "{ " ); 444 445 sb.append( "name-type: " ).append( nameType.name() ); 446 447 if ( ( nameString != null ) && !nameString.isEmpty() ) 448 { 449 sb.append( ", name-string : <" ); 450 boolean isFirst = true; 451 452 for ( String name : nameString ) 453 { 454 if ( isFirst ) 455 { 456 isFirst = false; 457 } 458 else 459 { 460 sb.append( ", " ); 461 } 462 463 sb.append( '\'' ).append( name ).append( '\'' ); 464 } 465 466 sb.append( ">" ); 467 } 468 else 469 { 470 sb.append( " no name-string" ); 471 } 472 473 if ( realm != null ) 474 { 475 sb.append( "realm: " ).append( realm ); 476 } 477 478 sb.append( " }" ); 479 480 return sb.toString(); 481 } 482 483 484 @Override 485 public int hashCode() 486 { 487 final int prime = 31; 488 int result = 1; 489 result = prime * result + ( ( nameString == null ) ? 0 : nameString.hashCode() ); 490 result = prime * result + ( ( nameType == null ) ? 0 : nameType.hashCode() ); 491 return result; 492 } 493 494 495 /** 496 * {@inheritDoc} 497 */ 498 @Override 499 public boolean equals( Object obj ) 500 { 501 if ( this == obj ) 502 { 503 return true; 504 } 505 506 if ( !( obj instanceof PrincipalName ) ) 507 { 508 return false; 509 } 510 511 PrincipalName other = ( PrincipalName ) obj; 512 513 if ( nameString == null ) 514 { 515 if ( other.nameString != null ) 516 { 517 return false; 518 } 519 } 520 else if ( !nameString.equals( other.nameString ) ) 521 { 522 return false; 523 } 524 525 return nameType == other.nameType; 526 } 527 528}