View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *  
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *  
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License. 
18   *  
19   */
20  package org.apache.directory.shared.kerberos.components;
21  
22  
23  import java.nio.BufferOverflowException;
24  import java.nio.ByteBuffer;
25  import java.text.ParseException;
26  import java.util.ArrayList;
27  import java.util.List;
28  
29  import javax.security.auth.kerberos.KerberosPrincipal;
30  
31  import org.apache.directory.api.asn1.Asn1Object;
32  import org.apache.directory.api.asn1.EncoderException;
33  import org.apache.directory.api.asn1.ber.tlv.BerValue;
34  import org.apache.directory.api.asn1.ber.tlv.TLV;
35  import org.apache.directory.api.asn1.ber.tlv.UniversalTag;
36  import org.apache.directory.api.util.Strings;
37  import org.apache.directory.server.i18n.I18n;
38  import org.apache.directory.shared.kerberos.KerberosUtils;
39  import org.apache.directory.shared.kerberos.codec.types.PrincipalNameType;
40  import org.slf4j.Logger;
41  import org.slf4j.LoggerFactory;
42  
43  
44  /**
45   * A principal Name, composed of a type and N names.
46   * <pre>
47   * PrincipalName   ::= SEQUENCE {
48   *        name-type       [0] Int32,
49   *        name-string     [1] SEQUENCE OF KerberosString
50   * }
51   * </pre>
52   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
53   */
54  public class PrincipalName implements Asn1Object
55  {
56      /** The logger */
57      private static final Logger LOG = LoggerFactory.getLogger( PrincipalName.class );
58  
59      /** Speedup for logs */
60      private static final boolean IS_DEBUG = LOG.isDebugEnabled();
61  
62      /** The type for this principal */
63      private PrincipalNameType nameType;
64  
65      /** The principal name - we may have more than one - */
66      private List<String> nameString = new ArrayList<>();
67  
68      /** The realm part */
69      private String realm;
70  
71      /** The principal name as a byte[], for encoding purpose */
72      private List<byte[]> nameBytes;
73  
74      // Storage for computed lengths
75      private int principalNameSeqLength;
76      private int principalTypeTagLength;
77      private int principalTypeLength;
78      private int principalStringsTagLength;
79      private int principalStringsSeqLength;
80  
81  
82      /**
83       * Creates a new empty instance of PrincipalName.
84       */
85      public PrincipalName()
86      {
87      }
88  
89  
90      /**
91       * Creates a new instance of PrincipalName, given a KerberosPrincipal.
92       * 
93       * We assume that a principal has only one type, even if there are
94       * more than one name component.
95       *
96       * @param principal A Sun kerberosPrincipal instance
97       */
98      public PrincipalName( KerberosPrincipal principal )
99      {
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      *  +--&gt; 0xA1 L2 name-type tag
292      *  |     |
293      *  |     +--&gt; 0x02 L2-1 addressType (int)
294      *  |
295      *  +--&gt; 0xA2 L3 name-string tag
296      *        |
297      *        +--&gt; 0x30 L3-1 name-string (SEQUENCE OF KerberosString)
298      *              |
299      *              +--&gt; 0x1B L4[1] value (KerberosString)
300      *              |
301      *              +--&gt; 0x1B L4[2] value (KerberosString)
302      *              |
303      *              ...
304      *              |
305      *              +--&gt; 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./../../../org/apache/directory/shared/kerberos/components/PrincipalName.html#PrincipalName">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 }