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.ByteBuffer;
024import java.util.ArrayList;
025import java.util.LinkedHashSet;
026import java.util.List;
027import java.util.Set;
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.apache.directory.shared.kerberos.KerberosTime;
038import org.apache.directory.shared.kerberos.codec.options.KdcOptions;
039import org.apache.directory.shared.kerberos.codec.types.EncryptionType;
040import org.apache.directory.shared.kerberos.messages.Ticket;
041
042
043/**
044 * The KDC-REQ-BODY data structure. It will store the object described by the ASN.1 grammar :
045 * <pre>
046 * KDC-REQ-BODY    ::= SEQUENCE {
047 *      kdc-options             [0] KDCOptions,
048 *      cname                   [1] PrincipalName OPTIONAL
049 *                                  -- Used only in AS-REQ --,
050 *      realm                   [2] Realm
051 *                                  -- Server's realm
052 *                                  -- Also client's in AS-REQ --,
053 *      sname                   [3] PrincipalName OPTIONAL,
054 *      from                    [4] KerberosTime OPTIONAL,
055 *      till                    [5] KerberosTime,
056 *      rtime                   [6] KerberosTime OPTIONAL,
057 *      nonce                   [7] UInt32,
058 *      etype                   [8] SEQUENCE OF Int32 -- EncryptionType
059 *                                  -- in preference order --,
060 *      addresses               [9] HostAddresses OPTIONAL,
061 *      enc-authorization-data  [10] EncryptedData OPTIONAL
062 *                                  -- AuthorizationData --,
063 *      additional-tickets      [11] SEQUENCE OF Ticket OPTIONAL
064 *                                      -- NOTE: not empty
065 * }
066 * </pre>
067 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
068 */
069public class KdcReqBody implements Asn1Object
070{
071    /** The KDC options */
072    private KdcOptions kdcOptions;
073
074    /** The Client Principal, if the request is an AS-REQ */
075    private PrincipalName cName;
076
077    /** The realm */
078    private String realm;
079
080    /** The Server Principal */
081    private PrincipalName sName;
082
083    /** The start time for the requested ticket */
084    private KerberosTime from;
085
086    /** The expiration date for the requested ticket */
087    private KerberosTime till;
088
089    /** The renew-till date for the requested ticket */
090    private KerberosTime rtime;
091
092    /** Random number to avoid MiM attacks */
093    private int nonce;
094
095    /** Set of desired encryption types */
096    private Set<EncryptionType> eType;
097
098    /** Addresses valid for the requested ticket */
099    private HostAddresses addresses;
100
101    /** Encoded authorizationData, used by the TGS-REQ only */
102    private EncryptedData encAuthorizationData;
103
104    /** Additional tickets */
105    private List<Ticket> additionalTickets;
106
107    // Storage for computed lengths
108    private int kdcOptionsLength;
109    private int cNameLength;
110    private int realmLength;
111    private byte[] realmBytes;
112    private int sNameLength;
113    private int fromLength;
114    private int tillLength;
115    private int rtimeLength;
116    private int nonceLength;
117    private int eTypeLength;
118    private int eTypeSeqLength;
119    private int[] eTypeLengths;
120    private int addressesLength;
121    private int encAuthzDataLength;
122    private int additionalTicketLength;
123    private int additionalTicketSeqLength;
124    private int[] additionalTicketsLengths;
125    private int kdcReqBodySeqLength;
126    private int kdcReqBodyLength;
127
128
129    /**
130     * Creates a new instance of RequestBody.
131     */
132    public KdcReqBody()
133    {
134        additionalTickets = new ArrayList<>();
135        eType = new LinkedHashSet<>();
136    }
137
138
139    /**
140     * Returns the additional {@link Ticket}s.
141     *
142     * @return The additional {@link Ticket}s.
143     */
144    public Ticket[] getAdditionalTickets()
145    {
146        return additionalTickets.toArray( new Ticket[]
147            {} );
148    }
149
150
151    /**
152     * Set the list of additional Ticket
153     * @param additionalTickets the additionalTickets to set
154     */
155    public void setAdditionalTickets( List<Ticket> additionalTickets )
156    {
157        this.additionalTickets = additionalTickets;
158    }
159
160
161    /**
162     * Add a new Ticket to the list of additional tickets
163     * @param additionalTicket the additionalTicket to set
164     */
165    public void addAdditionalTicket( Ticket additionalTicket )
166    {
167        this.additionalTickets.add( additionalTicket );
168    }
169
170
171    /**
172     * Returns the {@link HostAddresses}.
173     *
174     * @return The {@link HostAddresses}.
175     */
176    public HostAddresses getAddresses()
177    {
178        return addresses;
179    }
180
181
182    /**
183     * @param addresses the addresses to set
184     */
185    public void setAddresses( HostAddresses addresses )
186    {
187        this.addresses = addresses;
188    }
189
190
191    /**
192     * @return the client PrincipalName
193     */
194    public PrincipalName getCName()
195    {
196        return cName;
197    }
198
199
200    /**
201     * @param cName the cName to set
202     */
203    public void setCName( PrincipalName cName )
204    {
205        this.cName = cName;
206    }
207
208
209    /**
210     * Returns the encrypted {@link AuthorizationData} as {@link EncryptedData}.
211     *
212     * @return The encrypted {@link AuthorizationData} as {@link EncryptedData}.
213     */
214    public EncryptedData getEncAuthorizationData()
215    {
216        return encAuthorizationData;
217    }
218
219
220    /**
221     * @param encAuthorizationData the encAuthorizationData to set
222     */
223    public void setEncAuthorizationData( EncryptedData encAuthorizationData )
224    {
225        this.encAuthorizationData = encAuthorizationData;
226    }
227
228
229    /**
230     * Returns the requested {@link EncryptionType}s.
231     *
232     * @return The requested {@link EncryptionType}s.
233     */
234    public Set<EncryptionType> getEType()
235    {
236        return eType;
237    }
238
239
240    /**
241     * @param eType the eType to set
242     */
243    public void setEType( Set<EncryptionType> eType )
244    {
245        this.eType = eType;
246    }
247
248
249    /**
250     * @param eType the eType to add
251     */
252    public void addEType( EncryptionType eType )
253    {
254        this.eType.add( eType );
255    }
256
257
258    /**
259     * Returns the from {@link KerberosTime}.
260     *
261     * @return The from {@link KerberosTime}.
262     */
263    public KerberosTime getFrom()
264    {
265        return from;
266    }
267
268
269    /**
270     * @param from the from to set
271     */
272    public void setFrom( KerberosTime from )
273    {
274        this.from = from;
275    }
276
277
278    /**
279     * Returns the {@link KdcOptions}.
280     *
281     * @return The {@link KdcOptions}.
282     */
283    public KdcOptions getKdcOptions()
284    {
285        return kdcOptions;
286    }
287
288
289    /**
290     * @param kdcOptions the kdcOptions to set
291     */
292    public void setKdcOptions( KdcOptions kdcOptions )
293    {
294        this.kdcOptions = kdcOptions;
295    }
296
297
298    /**
299     * Returns the nonce.
300     *
301     * @return The nonce.
302     */
303    public int getNonce()
304    {
305        return nonce;
306    }
307
308
309    /**
310     * @param nonce the nonce to set
311     */
312    public void setNonce( int nonce )
313    {
314        this.nonce = nonce;
315    }
316
317
318    /**
319     * @return the realm
320     */
321    public String getRealm()
322    {
323        return realm;
324    }
325
326
327    /**
328     * @param realm the realm to set
329     */
330    public void setRealm( String realm )
331    {
332        this.realm = realm;
333    }
334
335
336    /**
337     * Returns the RenewTime {@link KerberosTime}.
338     *
339     * @return The RenewTime {@link KerberosTime}.
340     */
341    public KerberosTime getRTime()
342    {
343        return rtime;
344    }
345
346
347    /**
348     * @param rtime the renewTime to set
349     */
350    public void setRtime( KerberosTime rtime )
351    {
352        this.rtime = rtime;
353    }
354
355
356    /**
357     * Returns the server {@link PrincipalName}.
358     *
359     * @return The server {@link PrincipalName}.
360     */
361    public PrincipalName getSName()
362    {
363        return sName;
364    }
365
366
367    /**
368     * @param sName the sName to set
369     */
370    public void setSName( PrincipalName sName )
371    {
372        this.sName = sName;
373    }
374
375
376    /**
377     * Returns the till {@link KerberosTime}.
378     *
379     * @return The till {@link KerberosTime}.
380     */
381    public KerberosTime getTill()
382    {
383        return till;
384    }
385
386
387    /**
388     * @param till the till to set
389     */
390    public void setTill( KerberosTime till )
391    {
392        this.till = till;
393    }
394
395
396    /**
397     * Compute the KdcReqBody length
398     * <pre>
399     * KdcReqBody :
400     *
401     * 0x30 L1 KdcReqBody sequence
402     *  |
403     *  +--&gt; 0xA0 L2 kdc-options tag
404     *  |     |
405     *  |     +--&gt; 0x03 L2-1 kdc-options (BitString)
406     *  |
407     *  +--&gt; 0xA1 L3 cname tag
408     *  |     |
409     *  |     +--&gt; 0x30 L3-1 cname (PrincipalName)
410     *  |
411     *  +--&gt; 0xA2 L4 realm tag
412     *  |     |
413     *  |     +--&gt; 0x1B L4-1 realm (Realm, KerberosString)
414     *  |
415     *  +--&gt; 0xA3 L5 sname tag
416     *  |     |
417     *  |     +--&gt; 0x30 L5-1 sname (PrincipalName)
418     *  |
419     *  +--&gt; 0xA4 L6 from tag
420     *  |     |
421     *  |     +--&gt; 0x18 L6-1 from (KerberosTime)
422     *  |
423     *  +--&gt; 0xA5 L7 till tag
424     *  |     |
425     *  |     +--&gt; 0x18 L7-1 till (KerberosTime)
426     *  |
427     *  +--&gt; 0xA6 L8 rtime tag
428     *  |     |
429     *  |     +--&gt; 0x18 L8-1 rtime (KerberosTime)
430     *  |
431     *  +--&gt; 0xA7 L9 nonce tag
432     *  |     |
433     *  |     +--&gt; 0x02 L9-1 nonce (Int)
434     *  |
435     *  +--&gt; 0xA8 L10 etype tag
436     *  |     |
437     *  |     +--&gt; 0x30 L10-1 SEQ
438     *  |           |
439     *  |           +--&gt; 0x02 L10-1-1 etype
440     *  |           |
441     *  |           +--&gt; 0x02 L10-1-2 etype
442     *  |           |
443     *  |           :
444     *  |
445     *  +--&gt; 0xA9 L11 addresses tag
446     *  |     |
447     *  |     +--&gt; 0x30 L11-1 addresses (HostAddresses)
448     *  |
449     *  +--&gt; 0xAA L12 enc-authorization-data tag
450     *  |     |
451     *  |     +--&gt; 0x30 L12-1 enc-authorization-data
452     *  |
453     *  +--&gt; 0xAB L13 additional-tickets tag
454     *        |
455     *        +--&gt; 0x30 L13-1 additional-tickets
456     *              |
457     *              +--&gt; 0x61 L13-1-1 Ticket
458     *              |
459     *              +--&gt; 0x61 L13-1-2 Ticket
460     *              |
461     *              :
462     * </pre>
463     */
464    @Override
465    public int computeLength()
466    {
467        reset();
468
469        // The KdcOptions length
470        kdcOptionsLength = 1 + 1 + kdcOptions.getBytes().length;
471        kdcReqBodySeqLength = 1 + TLV.getNbBytes( kdcOptionsLength ) + kdcOptionsLength;
472
473        // The cname length
474        if ( cName != null )
475        {
476            cNameLength = cName.computeLength();
477            kdcReqBodySeqLength += 1 + TLV.getNbBytes( cNameLength ) + cNameLength;
478        }
479
480        // Compute the realm length.
481        realmBytes = Strings.getBytesUtf8( realm );
482        realmLength = 1 + TLV.getNbBytes( realmBytes.length ) + realmBytes.length;
483        kdcReqBodySeqLength += 1 + TLV.getNbBytes( realmLength ) + realmLength;
484
485        // The sname length
486        if ( sName != null )
487        {
488            sNameLength = sName.computeLength();
489            kdcReqBodySeqLength += 1 + TLV.getNbBytes( sNameLength ) + sNameLength;
490        }
491
492        // The from length
493        if ( from != null )
494        {
495            fromLength = 1 + 1 + 0x0F;
496            kdcReqBodySeqLength += 1 + 1 + fromLength;
497        }
498
499        // The till length
500        tillLength = 1 + 1 + 0x0F;
501        kdcReqBodySeqLength += 1 + 1 + tillLength;
502
503        // The rtime length
504        if ( rtime != null )
505        {
506            rtimeLength = 1 + 1 + 0x0F;
507            kdcReqBodySeqLength += 1 + 1 + rtimeLength;
508        }
509
510        // The nonce length
511        nonceLength = 1 + 1 + BerValue.getNbBytes( nonce );
512        kdcReqBodySeqLength += 1 + 1 + nonceLength;
513
514        // The eType length
515        eTypeLengths = new int[eType.size()];
516        int pos = 0;
517        eTypeSeqLength = 0;
518
519        for ( EncryptionType encryptionType : eType )
520        {
521            eTypeLengths[pos] = 1 + 1 + BerValue.getNbBytes( encryptionType.getValue() );
522            eTypeSeqLength += eTypeLengths[pos];
523            pos++;
524        }
525
526        eTypeLength = 1 + TLV.getNbBytes( eTypeSeqLength ) + eTypeSeqLength;
527        kdcReqBodySeqLength += 1 + TLV.getNbBytes( eTypeLength ) + eTypeLength;
528
529        // The Addresses length
530        if ( addresses != null )
531        {
532            addressesLength = addresses.computeLength();
533            kdcReqBodySeqLength += 1 + TLV.getNbBytes( addressesLength ) + addressesLength;
534        }
535
536        // The EncAuthorizationData length
537        if ( encAuthorizationData != null )
538        {
539            encAuthzDataLength = encAuthorizationData.computeLength();
540            kdcReqBodySeqLength += 1 + TLV.getNbBytes( encAuthzDataLength ) + encAuthzDataLength;
541        }
542
543        // The additionalTickets length
544        if ( !additionalTickets.isEmpty() )
545        {
546            additionalTicketsLengths = new int[additionalTickets.size()];
547            additionalTicketSeqLength = 0;
548            pos = 0;
549
550            for ( Ticket ticket : additionalTickets )
551            {
552                additionalTicketsLengths[pos] = ticket.computeLength();
553                additionalTicketSeqLength += additionalTicketsLengths[pos];
554                pos++;
555            }
556
557            additionalTicketLength = 1 + TLV.getNbBytes( additionalTicketSeqLength ) + additionalTicketSeqLength;
558            kdcReqBodySeqLength += 1 + TLV.getNbBytes( additionalTicketLength ) + additionalTicketLength;
559        }
560
561        // compute the global size
562        kdcReqBodyLength = 1 + TLV.getNbBytes( kdcReqBodySeqLength ) + kdcReqBodySeqLength;
563
564        return kdcReqBodyLength;
565    }
566
567
568    /**
569     * Encode the KDC-REQ-BODY component
570     *
571     * @param buffer The buffer containing the encoded result
572     * @return The encoded component
573     * @throws EncoderException If the encoding failed
574     */
575    @Override
576    public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
577    {
578        if ( buffer == null )
579        {
580            throw new EncoderException( I18n.err( I18n.ERR_148 ) );
581        }
582
583        // The KDC-REQ-BODY SEQ Tag
584        buffer.put( UniversalTag.SEQUENCE.getValue() );
585        buffer.put( TLV.getBytes( kdcReqBodySeqLength ) );
586
587        // The KdcOptions -----------------------------------------------------
588        // The tag
589        buffer.put( ( byte ) KerberosConstants.KDC_REQ_BODY_KDC_OPTIONS_TAG );
590        buffer.put( TLV.getBytes( kdcOptionsLength ) );
591
592        // The value
593        BerValue.encode( buffer, kdcOptions );
594
595        // The cname if any ---------------------------------------------------
596        if ( cName != null )
597        {
598            // The tag
599            buffer.put( ( byte ) KerberosConstants.KDC_REQ_BODY_CNAME_TAG );
600            buffer.put( TLV.getBytes( cNameLength ) );
601
602            // The value
603            cName.encode( buffer );
604        }
605
606        // The realm ----------------------------------------------------------
607        // The tag
608        buffer.put( ( byte ) KerberosConstants.KDC_REQ_BODY_REALM_TAG );
609        buffer.put( TLV.getBytes( realmLength ) );
610
611        // The value
612        buffer.put( UniversalTag.GENERAL_STRING.getValue() );
613        buffer.put( TLV.getBytes( realmBytes.length ) );
614        buffer.put( realmBytes );
615
616        // The sname, if any --------------------------------------------------
617        if ( sName != null )
618        {
619            // The tag
620            buffer.put( ( byte ) KerberosConstants.KDC_REQ_BODY_SNAME_TAG );
621            buffer.put( TLV.getBytes( sNameLength ) );
622
623            // The value
624            sName.encode( buffer );
625        }
626
627        // The from, if any ---------------------------------------------------
628        if ( from != null )
629        {
630            // The tag
631            buffer.put( ( byte ) KerberosConstants.KDC_REQ_BODY_FROM_TAG );
632            buffer.put( TLV.getBytes( fromLength ) );
633
634            // The value
635            buffer.put( UniversalTag.GENERALIZED_TIME.getValue() );
636            buffer.put( ( byte ) 0x0F );
637            buffer.put( from.getBytes() );
638        }
639
640        // The till -----------------------------------------------------------
641        // The tag
642        buffer.put( ( byte ) KerberosConstants.KDC_REQ_BODY_TILL_TAG );
643        buffer.put( TLV.getBytes( tillLength ) );
644
645        // The value
646        buffer.put( UniversalTag.GENERALIZED_TIME.getValue() );
647        buffer.put( ( byte ) 0x0F );
648        buffer.put( till.getBytes() );
649
650        // The rtime if any ---------------------------------------------------
651        if ( rtime != null )
652        {
653            // The tag
654            buffer.put( ( byte ) KerberosConstants.KDC_REQ_BODY_RTIME_TAG );
655            buffer.put( TLV.getBytes( rtimeLength ) );
656
657            // The value
658            buffer.put( UniversalTag.GENERALIZED_TIME.getValue() );
659            buffer.put( ( byte ) 0x0F );
660            buffer.put( rtime.getBytes() );
661        }
662
663        // The nonce ----------------------------------------------------------
664        // The tag
665        buffer.put( ( byte ) KerberosConstants.KDC_REQ_BODY_NONCE_TAG );
666        buffer.put( TLV.getBytes( nonceLength ) );
667
668        // The value
669        BerValue.encode( buffer, nonce );
670
671        // The etype ----------------------------------------------------------
672        // The tag
673        buffer.put( ( byte ) KerberosConstants.KDC_REQ_BODY_ETYPE_TAG );
674        buffer.put( TLV.getBytes( eTypeLength ) );
675
676        // The sequence
677        buffer.put( UniversalTag.SEQUENCE.getValue() );
678        buffer.put( TLV.getBytes( eTypeSeqLength ) );
679
680        // The values
681        for ( EncryptionType encryptionType : eType )
682        {
683            BerValue.encode( buffer, encryptionType.getValue() );
684        }
685
686        // The addresses if any -----------------------------------------------
687        if ( addresses != null )
688        {
689            // The tag
690            buffer.put( ( byte ) KerberosConstants.KDC_REQ_BODY_ADDRESSES_TAG );
691            buffer.put( TLV.getBytes( addressesLength ) );
692
693            // The value
694            addresses.encode( buffer );
695        }
696
697        // The enc-authorization-data, if any ---------------------------------
698        if ( encAuthorizationData != null )
699        {
700            // The tag
701            buffer.put( ( byte ) KerberosConstants.KDC_REQ_BODY_ENC_AUTHZ_DATA_TAG );
702            buffer.put( TLV.getBytes( encAuthzDataLength ) );
703
704            // The value
705            encAuthorizationData.encode( buffer );
706        }
707
708        // The additional-tickets, if any -------------------------------------
709        if ( !additionalTickets.isEmpty() )
710        {
711            // The tag
712            buffer.put( ( byte ) KerberosConstants.KDC_REQ_BODY_ADDITIONAL_TICKETS_TAG );
713            buffer.put( TLV.getBytes( additionalTicketLength ) );
714
715            // The sequence
716            buffer.put( UniversalTag.SEQUENCE.getValue() );
717            buffer.put( TLV.getBytes( additionalTicketSeqLength ) );
718
719            // The values
720            for ( Ticket ticket : additionalTickets )
721            {
722                ticket.encode( buffer );
723            }
724        }
725
726        return buffer;
727    }
728
729
730    /**
731     * reset the fields used while computing length
732     */
733    private void reset()
734    {
735        kdcOptionsLength = 0;
736        cNameLength = 0;
737        realmLength = 0;
738        realmBytes = null;
739        sNameLength = 0;
740        fromLength = 0;
741        tillLength = 0;
742        rtimeLength = 0;
743        nonceLength = 0;
744        eTypeLength = 0;
745        eTypeSeqLength = 0;
746        eTypeLengths = null;
747        addressesLength = 0;
748        encAuthzDataLength = 0;
749        additionalTicketLength = 0;
750        additionalTicketSeqLength = 0;
751        additionalTicketsLengths = null;
752        kdcReqBodySeqLength = 0;
753        kdcReqBodyLength = 0;
754    }
755
756
757    /**
758     * Pretty print the instance
759     */
760    public String toString( String tabs )
761    {
762        StringBuilder sb = new StringBuilder();
763
764        if ( ( kdcOptions != null ) && ( kdcOptions.size() > 0 ) )
765        {
766            sb.append( tabs ).append( "KDCOptions : " ).append( kdcOptions ).append( '\n' );
767        }
768
769        if ( cName != null )
770        {
771            sb.append( tabs ).append( "cname : " ).append( cName ).append( '\n' );
772        }
773
774        sb.append( tabs ).append( "realm : " ).append( realm ).append( '\n' );
775
776        if ( sName != null )
777        {
778            sb.append( tabs ).append( "sname : " ).append( sName ).append( '\n' );
779        }
780
781        if ( from != null )
782        {
783            sb.append( tabs ).append( "from : " ).append( from ).append( '\n' );
784        }
785
786        sb.append( tabs ).append( "till : " ).append( till ).append( '\n' );
787
788        if ( rtime != null )
789        {
790            sb.append( tabs ).append( "rtime : " ).append( rtime ).append( '\n' );
791        }
792
793        sb.append( tabs ).append( "nonce : " ).append( nonce ).append( '\n' );
794
795        sb.append( tabs ).append( "etype : " );
796        boolean isFirst = true;
797
798        for ( EncryptionType encryptionType : eType )
799        {
800            if ( isFirst )
801            {
802                isFirst = false;
803            }
804            else
805            {
806                sb.append( " " );
807            }
808
809            sb.append( encryptionType );
810        }
811
812        if ( addresses != null )
813        {
814            sb.append( '\n' );
815            sb.append( tabs ).append( "addresses : " );
816            isFirst = true;
817
818            for ( HostAddress hostAddress : addresses.getAddresses() )
819            {
820                if ( isFirst )
821                {
822                    isFirst = false;
823                }
824                else
825                {
826                    sb.append( " " );
827                }
828
829                sb.append( hostAddress );
830            }
831        }
832
833        if ( encAuthorizationData != null )
834        {
835            sb.append( '\n' );
836            sb.append( tabs ).append( "enc-authorization-data" ).append( encAuthorizationData );
837        }
838
839        if ( !additionalTickets.isEmpty() )
840        {
841            sb.append( '\n' );
842            sb.append( tabs ).append( "Tickets : " );
843            isFirst = true;
844
845            for ( Ticket ticket : additionalTickets )
846            {
847                if ( isFirst )
848                {
849                    isFirst = false;
850                    sb.append( '\n' );
851                }
852
853                sb.append( ticket.toString( tabs + "    " ) );
854            }
855        }
856
857        return sb.toString();
858    }
859
860
861    /**
862     * @see Object#toString()
863     */
864    public String toString()
865    {
866        return toString( "" );
867    }
868}