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    //package org.apache.directory.server.kerberos.shared.messages.value;
021    
022    
023    /**
024     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
025     * @version $Rev: 642496 $, $Date: 2008-03-29 05:09:22 +0200 (Sat, 29 Mar 2008) $
026     *
027    public class PrincipalName
028    {
029        private String nameComponent;
030        private int nameType;
031    
032    
033        /**
034         * Creates a new instance of PrincipalName.
035         *
036         * @param nameComponent
037         * @param nameType
038         *
039        public PrincipalName( String nameComponent, int nameType )
040        {
041            this.nameComponent = nameComponent;
042            this.nameType = nameType;
043        }
044    
045    
046        /**
047         * Returns the type of the {@link PrincipalName}.
048         *
049         * @return The type of the {@link PrincipalName}.
050         *
051        public int getNameType()
052        {
053            return nameType;
054        }
055    
056    
057        /**
058         * Returns the name component.
059         *
060         * @return The name component.
061         *
062        public String getNameComponent()
063        {
064            return nameComponent;
065        }
066    }*/
067    
068    /*
069     *  Licensed to the Apache Software Foundation (ASF) under one
070     *  or more contributor license agreements.  See the NOTICE file
071     *  distributed with this work for additional information
072     *  regarding copyright ownership.  The ASF licenses this file
073     *  to you under the Apache License, Version 2.0 (the
074     *  "License"); you may not use this file except in compliance
075     *  with the License.  You may obtain a copy of the License at
076     *  
077     *    http://www.apache.org/licenses/LICENSE-2.0
078     *  
079     *  Unless required by applicable law or agreed to in writing,
080     *  software distributed under the License is distributed on an
081     *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
082     *  KIND, either express or implied.  See the License for the
083     *  specific language governing permissions and limitations
084     *  under the License. 
085     *  
086     */
087    package org.apache.directory.server.kerberos.shared.messages.value;
088    
089    
090    import java.nio.BufferOverflowException;
091    import java.nio.ByteBuffer;
092    import java.text.ParseException;
093    import java.util.ArrayList;
094    import java.util.List;
095    
096    import javax.security.auth.kerberos.KerberosPrincipal;
097    
098    import org.apache.directory.server.kerberos.shared.KerberosUtils;
099    import org.apache.directory.server.kerberos.shared.messages.value.types.PrincipalNameType;
100    import org.apache.directory.shared.asn1.AbstractAsn1Object;
101    import org.apache.directory.shared.asn1.ber.tlv.TLV;
102    import org.apache.directory.shared.asn1.ber.tlv.UniversalTag;
103    import org.apache.directory.shared.asn1.ber.tlv.Value;
104    import org.apache.directory.shared.asn1.codec.EncoderException;
105    import org.apache.directory.shared.ldap.util.StringTools;
106    import org.slf4j.Logger;
107    import org.slf4j.LoggerFactory;
108    
109    
110    /**
111     * A principal Name, composed of a type and N names.
112     * 
113     * PrincipalName   ::= SEQUENCE {
114     *        name-type       [0] Int32,
115     *        name-string     [1] SEQUENCE OF KerberosString
116     * }
117     *
118     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
119     * @version $Rev: 642496 $, $Date: 2008-03-29 05:09:22 +0200 (Sat, 29 Mar 2008) $
120     */
121    public class PrincipalName extends AbstractAsn1Object
122    {
123        /** The logger */
124        private static final Logger LOG = LoggerFactory.getLogger( PrincipalName.class );
125    
126        /** Speedup for logs */
127        private static final boolean IS_DEBUG = LOG.isDebugEnabled();
128    
129        /** The type for this principal */
130        private PrincipalNameType nameType;
131    
132        /** The principal name - we may have more than one - */
133        private List<String> nameString;
134        
135        /** The principal name as a byte[], for encoding purpose */
136        private transient List<byte[]> nameBytes;
137        
138        // Storage for computed lengths
139        private transient int principalNameSeqLength;
140        private transient int principalTypeTagLength;
141        private transient int principalTypeLength;
142        private transient int principalStringsTagLength;
143        private transient int principalStringsSeqLength;
144    
145        /**
146         * Creates a new empty instance of PrincipalName.
147         */
148        public PrincipalName()
149        {
150        }
151    
152        /**
153         * Creates a new instance of PrincipalName, given a KerberosPrincipal.
154         * 
155         * We assume that a principal has only one type, even if there are
156         * more than one name component.
157         *
158         * @param principal A Sun kerberosPrincipal instance
159         */
160        public PrincipalName( KerberosPrincipal principal )
161        {
162            try
163            {
164                nameString = KerberosUtils.getNames( principal );
165            }
166            catch ( ParseException pe )
167            {
168                nameString = KerberosUtils.EMPTY_PRINCIPAL_NAME;
169            }
170    
171            this.nameType = PrincipalNameType.getTypeByOrdinal( principal.getNameType() );
172        }
173        
174        /**
175         * Creates a new instance of PrincipalName given a String and an 
176         * prinipal type.
177         * 
178         * @param nameString The name string, which can contains more than one nameComponent
179         * @param nameType The principal name
180         */
181        public PrincipalName( String nameString, PrincipalNameType nameType )  throws ParseException
182        {
183            this.nameString = KerberosUtils.getNames( nameString );
184            
185            this.nameType = nameType;
186        }
187    
188    
189        /**
190         * Creates a new instance of PrincipalName.
191         *
192         * @param nameString
193         * @param nameType
194         */
195        public PrincipalName( String nameString, int nameType ) throws ParseException
196        {
197            this.nameString = KerberosUtils.getNames( nameString );
198            
199            this.nameType = PrincipalNameType.getTypeByOrdinal( nameType );
200        }
201    
202    
203        /**
204         * Returns the type of the {@link PrincipalName}.
205         *
206         * @return The type of the {@link PrincipalName}.
207         */
208        public PrincipalNameType getNameType()
209        {
210            return nameType;
211        }
212                        
213        /** 
214         * Set the Principal name Type
215         * @param nameType the Principal name Type
216         */
217        public void setNameType( PrincipalNameType nameType )
218        {
219            this.nameType = nameType;
220        }
221    
222        /** 
223         * Set the Principal name Type
224         * @param nameType the Principal name Type
225         */
226        public void setNameType( int nameType )
227        {
228            this.nameType = PrincipalNameType.getTypeByOrdinal( nameType );
229        }
230    
231        /**
232         * Returns the name components.
233         *
234         * @return The name components.
235         */
236        public List<String> getNames()
237        {
238            return nameString;
239        }
240    
241    
242        /**
243         * @return A String representing the principal names as a String 
244         */
245        public String getNameString()
246        {
247            if ( ( nameString == null ) || ( nameString.size() == 0 ) )
248            {
249                return "";
250            }
251            else
252            {
253                StringBuilder sb = new StringBuilder();
254                boolean isFirst = true;
255    
256                for ( String name : nameString )
257                {
258                    if ( isFirst )
259                    {
260                        isFirst = false;
261                    }
262                    else
263                    {
264                        sb.append( '/' );
265                    }
266    
267                    sb.append( name );
268                }
269    
270                return sb.toString();
271            }
272        }
273    
274    
275        /**
276         * Add a new name to the PrincipalName
277         * @param name The name to add
278         */
279        public void addName( String name )
280        {
281            if ( nameString == null )
282            {
283                nameString = new ArrayList<String>();
284            }
285    
286            nameString.add( name );
287        }
288    
289    
290        /**
291         * Compute the PrincipalName length
292         * 
293         * PrincipalName :
294         * 
295         * 0x30 L1 PrincipalName sequence
296         *  |
297         *  +--> 0xA1 L2 name-type tag
298         *  |     |
299         *  |     +--> 0x02 L2-1 addressType (int)
300         *  |
301         *  +--> 0xA2 L3 name-string tag
302         *        |
303         *        +--> 0x30 L3-1 name-string (SEQUENCE OF KerberosString)
304         *              |
305         *              +--> 0x1B L4[1] value (KerberosString)
306         *              |
307         *              +--> 0x1B L4[2] value (KerberosString)
308         *              |
309         *              ...
310         *              |
311         *              +--> 0x1B L4[n] value (KerberosString)
312         */
313        public int computeLength()
314        {
315            // The principalName can't be empty.
316            principalTypeLength = Value.getNbBytes( nameType.getOrdinal() );
317            principalTypeTagLength = 1 + TLV.getNbBytes( principalTypeLength ) + principalTypeLength;
318            
319            principalNameSeqLength = 1 + TLV.getNbBytes( principalTypeTagLength ) + principalTypeTagLength;
320    
321            // Compute the keyValue
322            if ( ( nameString == null ) || ( nameString.size() == 0 ) )
323            {
324                principalStringsSeqLength = 0;
325            }
326            else
327            {
328                principalStringsSeqLength = 0;
329                nameBytes = new ArrayList<byte[]>( nameString.size() );
330    
331                for ( String name : nameString )
332                {
333                    if ( name != null )
334                    {
335                        byte[] bytes = StringTools.getBytesUtf8( name );
336                        nameBytes.add( bytes );
337                        principalStringsSeqLength += 1 + TLV.getNbBytes( bytes.length ) + bytes.length;
338                    }
339                    else
340                    {
341                        nameBytes.add( StringTools.EMPTY_BYTES );
342                        principalStringsSeqLength += 1 + 1;
343                    }
344                }
345            }
346    
347            principalStringsTagLength = 1 + TLV.getNbBytes( principalStringsSeqLength ) + principalStringsSeqLength;
348            principalNameSeqLength += 1 + TLV.getNbBytes( principalStringsTagLength ) + principalStringsTagLength;
349    
350            // Compute the whole sequence length
351            return 1 + TLV.getNbBytes( principalNameSeqLength ) + principalNameSeqLength;
352        }
353    
354    
355        /**
356         * Encode the PrincipalName message to a PDU. 
357         * 
358         * PrincipalName :
359         * 
360         * 0x30 LL
361         *   0xA0 LL 
362         *     0x02 0x01 name-type (integer)
363         *   0xA1 LL 
364         *     0x30 LL name-string (SEQUENCE OF KerberosString)
365         *       0x1B LL name-string[1]
366         *       0x1B LL name-string[2]
367         *       ...
368         *       0x1B LL name-string[n]
369         * 
370         * @param buffer The buffer where to put the PDU. It should have been allocated
371         * before, with the right size.
372         * @return The constructed PDU.
373         */
374        public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
375        {
376            if ( buffer == null )
377            {
378                throw new EncoderException( "Cannot put a PDU in a null buffer !" );
379            }
380    
381            try
382            {
383                // The PrincipalName SEQ Tag
384                buffer.put( UniversalTag.SEQUENCE_TAG );
385                buffer.put( TLV.getBytes( principalNameSeqLength ) );
386    
387                // The name-type, first the tag, then the value
388                buffer.put( ( byte ) 0xA0 );
389                buffer.put( TLV.getBytes( principalTypeTagLength ) );
390                Value.encode( buffer, nameType.getOrdinal() );
391    
392                // The name-string tag
393                buffer.put( ( byte ) 0xA1 );
394                buffer.put( TLV.getBytes( principalStringsTagLength ) );
395    
396                // The name-string sequence
397                buffer.put( UniversalTag.SEQUENCE_TAG );
398    
399                if ( ( nameString == null ) || ( nameString.size() == 0 ) )
400                {
401                    buffer.put( ( byte ) 0x00 );
402                }
403                else
404                {
405                    buffer.put( TLV.getBytes( principalStringsSeqLength ) );
406    
407                    // The kerberosStrings
408                    for ( byte[] name : nameBytes )
409                    {
410                        buffer.put( UniversalTag.GENERALIZED_STRING_TAG );
411    
412                        if ( ( name == null ) || ( name.length == 0 ) )
413                        {
414                            buffer.put( ( byte ) 0x00 );
415                        }
416                        else
417                        {
418                            buffer.put( TLV.getBytes( name.length ) );
419                            buffer.put( name );
420                        }
421                    }
422                }
423            }
424            catch ( BufferOverflowException boe )
425            {
426                LOG.error(
427                    "Cannot encode the principalName object, the PDU size is {} when only {} bytes has been allocated", 1
428                        + TLV.getNbBytes( principalNameSeqLength ) + principalNameSeqLength, buffer.capacity() );
429                throw new EncoderException( "The PDU buffer size is too small !" );
430            }
431    
432            if ( IS_DEBUG )
433            {
434                LOG.debug( "PrinipalName encoding : {}", StringTools.dumpBytes( buffer.array() ) );
435                LOG.debug( "PrinipalName initial value : {}", toString() );
436            }
437    
438            return buffer;
439        }
440    
441    
442        /**
443         * @see Object#toString()
444         */
445        public String toString()
446        {
447            StringBuilder sb = new StringBuilder();
448    
449            sb.append( "PincipalName : {\n" );
450    
451            sb.append( "    name-type: " ).append( nameType ).append( '\n' );
452    
453            if ( ( nameString != null ) && ( nameString.size() != 0 ) )
454            {
455                sb.append( "    name-string : <" );
456                boolean isFirst = true;
457    
458                for ( String name : nameString )
459                {
460                    if ( isFirst )
461                    {
462                        isFirst = false;
463                    }
464                    else
465                    {
466                        sb.append( ", " );
467                    }
468    
469                    sb.append( '\'' ).append( name ).append( '\'' );
470                }
471    
472                sb.append( ">\n}" );
473            }
474            else
475            {
476                sb.append( "    no name-string\n}" );
477            }
478    
479            return sb.toString();
480        }
481    }