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.messages;
021
022
023import java.nio.BufferOverflowException;
024import java.nio.ByteBuffer;
025
026import org.apache.directory.api.asn1.EncoderException;
027import org.apache.directory.api.asn1.ber.tlv.BerValue;
028import org.apache.directory.api.asn1.ber.tlv.TLV;
029import org.apache.directory.api.asn1.ber.tlv.UniversalTag;
030import org.apache.directory.api.util.Strings;
031import org.apache.directory.server.i18n.I18n;
032import org.apache.directory.shared.kerberos.KerberosConstants;
033import org.apache.directory.shared.kerberos.KerberosMessageType;
034import org.apache.directory.shared.kerberos.components.EncTicketPart;
035import org.apache.directory.shared.kerberos.components.EncryptedData;
036import org.apache.directory.shared.kerberos.components.PrincipalName;
037import org.apache.directory.shared.kerberos.exceptions.InvalidTicketException;
038import org.slf4j.Logger;
039import org.slf4j.LoggerFactory;
040
041
042/**
043 * Ticket message component as handed out by the ticket granting service. It will store
044 * the object described by the ASN.1 grammar :
045 * <pre>
046 * Ticket          ::= [APPLICATION 1] SEQUENCE {
047 *         tkt-vno         [0] INTEGER (5),
048 *         realm           [1] Realm,
049 *         sname           [2] &lt;PrincipalName&gt;,
050 *         enc-part        [3] &lt;EncryptedData&gt; -- EncTicketPart
051 * }
052 * </pre>
053 * 
054 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
055 */
056public class Ticket extends KerberosMessage
057{
058    /** The logger */
059    private static final Logger LOG = LoggerFactory.getLogger( Ticket.class );
060
061    /** Speedup for logs */
062    private static final boolean IS_DEBUG = LOG.isDebugEnabled();
063
064    /** Constant for the {@link Ticket} version number (5) */
065    public static final int TICKET_VNO = KerberosConstants.KERBEROS_V5;
066
067    /** A storage for a byte array representation of the realm */
068    private byte[] realmBytes;
069
070    /** The server principal name */
071    private PrincipalName sName;
072
073    /** The server realm */
074    private String realm;
075
076    /** The encoded part */
077    private EncryptedData encPart;
078
079    /** The encoded ticket part, stored in its original form (not encoded) */
080    private EncTicketPart encTicketPart;
081
082    // Storage for computed lengths
083    private int tktvnoLength;
084    private int realmLength;
085    private int sNameLength;
086    private int encPartLength;
087    private int ticketSeqLength;
088    private int ticketLength;
089
090
091    /**
092     * Creates a new instance of Ticket.
093     *
094     * @param sName The server principal
095     * @param encPart The encoded part
096     */
097    public Ticket( PrincipalName sName, EncryptedData encPart ) throws InvalidTicketException
098    {
099        this( TICKET_VNO, sName, encPart );
100
101        setSName( sName );
102    }
103
104
105    /**
106     * Creates a new instance of Ticket.
107     */
108    public Ticket()
109    {
110        super( KerberosMessageType.TICKET );
111    }
112
113
114    /**
115     * Creates a new instance of Ticket.
116     *
117     * @param tktvno The Kerberos version number
118     * @param sName The server principal
119     * @param encPart The encoded part
120     */
121    public Ticket( int tktvno, PrincipalName sName, EncryptedData encPart )
122    {
123        super( tktvno, KerberosMessageType.TICKET );
124        this.encPart = encPart;
125        setSName( sName );
126    }
127
128
129    /**
130     * Returns the {@link EncryptedData}.
131     *
132     * @return The {@link EncryptedData}.
133     */
134    public EncryptedData getEncPart()
135    {
136        return encPart;
137    }
138
139
140    /**
141     * Set the encrypted ticket part
142     * @param encPart the encrypted ticket part
143     */
144    public void setEncPart( EncryptedData encPart )
145    {
146        this.encPart = encPart;
147    }
148
149
150    /**
151     * Returns the server realm.
152     *
153     * @return The server realm.
154     */
155    public String getRealm()
156    {
157        return realm;
158    }
159
160
161    /**
162     * Set the server realm
163     * @param realm the server realm
164     */
165    public void setRealm( String realm )
166    {
167        this.realm = realm;
168    }
169
170
171    /**
172     * Returns the server {@link PrincipalName}.
173     *
174     * @return The server {@link PrincipalName}.
175     */
176    public PrincipalName getSName()
177    {
178        return sName;
179    }
180
181
182    /**
183     * Set the server principalName
184     * @param sName the server principalName
185     */
186    public void setSName( PrincipalName sName )
187    {
188        this.sName = sName;
189    }
190
191
192    /**
193     * Gets the Ticket Version number
194     * @return The ticket version number
195     */
196    public int getTktVno()
197    {
198        return getProtocolVersionNumber();
199    }
200
201
202    /**
203     * Sets the Ticket Version number
204     * @param tktVno The new version number
205     */
206    public void setTktVno( int tktVno )
207    {
208        setProtocolVersionNumber( tktVno );
209    }
210
211
212    /**
213     * @return the encTicketPart
214     */
215    public EncTicketPart getEncTicketPart()
216    {
217        return encTicketPart;
218    }
219
220
221    /**
222     * @param encTicketPart the encTicketPart to set
223     */
224    public void setEncTicketPart( EncTicketPart encTicketPart )
225    {
226        this.encTicketPart = encTicketPart;
227    }
228
229
230    /**
231     * Compute the Ticket length
232     * <pre>
233     * Ticket :
234     * 
235     * 0x61 L1 Ticket [APPLICATION 1]
236     *  |
237     *  +--&gt; 0x30 L2 Ticket SEQUENCE
238     *        |
239     *        +--&gt; 0xA0 L3 tkt-vno tag
240     *        |     |
241     *        |     +--&gt; 0x02 L3-1 tkt-vno (int, 5)
242     *        |
243     *        +--&gt; 0xA1 L4 realm tag
244     *        |     |
245     *        |     +--&gt; 0x1B L4-1 realm (KerberosString)
246     *        |
247     *        +--&gt; 0xA2 L5 sname (PrincipalName)
248     *        |
249     *        +--&gt; 0xA3 L6 enc-part (EncryptedData)
250     * </pre>
251     */
252    public int computeLength()
253    {
254        // Compute the Ticket version length.
255        tktvnoLength = 1 + 1 + BerValue.getNbBytes( getProtocolVersionNumber() );
256
257        // Compute the Ticket realm length.
258        realmBytes = Strings.getBytesUtf8( realm );
259        realmLength = 1 + TLV.getNbBytes( realmBytes.length ) + realmBytes.length;
260
261        // Compute the principal length
262        sNameLength = sName.computeLength();
263
264        // Compute the encrypted data
265        encPartLength = encPart.computeLength();
266
267        // Compute the sequence size
268        ticketSeqLength =
269            1 + TLV.getNbBytes( tktvnoLength ) + tktvnoLength +
270                1 + TLV.getNbBytes( realmLength ) + realmLength +
271                1 + TLV.getNbBytes( sNameLength ) + sNameLength +
272                1 + TLV.getNbBytes( encPartLength ) + encPartLength;
273
274        // compute the global size
275        ticketLength = 1 + TLV.getNbBytes( ticketSeqLength ) + ticketSeqLength;
276
277        return 1 + TLV.getNbBytes( ticketLength ) + ticketLength;
278    }
279
280
281    /**
282     * Encode the Ticket message to a PDU. 
283     * <pre>
284     * Ticket :
285     * 
286     * 0x61 LL
287     *   0x30 LL
288     *     0xA0 LL tktvno 
289     *     0xA1 LL realm
290     *     0xA2 LL
291     *       sname (PrincipalName)
292     *     0xA3 LL
293     *       enc-part (EncryptedData)
294     * </pre>
295     * @return The constructed PDU.
296     */
297    public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
298    {
299        if ( buffer == null )
300        {
301            buffer = ByteBuffer.allocate( computeLength() );
302        }
303
304        try
305        {
306            // The Ticket APPLICATION Tag
307            buffer.put( ( byte ) KerberosConstants.TICKET_TAG );
308            buffer.put( TLV.getBytes( ticketLength ) );
309
310            // The Ticket SEQUENCE Tag
311            buffer.put( UniversalTag.SEQUENCE.getValue() );
312            buffer.put( TLV.getBytes( ticketSeqLength ) );
313
314            // The tkt-vno Tag and value
315            buffer.put( ( byte ) KerberosConstants.TICKET_TKT_VNO_TAG );
316            buffer.put( TLV.getBytes( tktvnoLength ) );
317            BerValue.encode( buffer, getProtocolVersionNumber() );
318
319            // The realm Tag and value
320            buffer.put( ( byte ) KerberosConstants.TICKET_REALM_TAG );
321            buffer.put( TLV.getBytes( realmLength ) );
322            buffer.put( UniversalTag.GENERAL_STRING.getValue() );
323            buffer.put( TLV.getBytes( realmBytes.length ) );
324            buffer.put( realmBytes );
325
326            // The sname Tag and value
327            buffer.put( ( byte ) KerberosConstants.TICKET_SNAME_TAG );
328            buffer.put( TLV.getBytes( sNameLength ) );
329            sName.encode( buffer );
330
331            // The encPartLength Tag and value
332            buffer.put( ( byte ) KerberosConstants.TICKET_ENC_PART_TAG );
333            buffer.put( TLV.getBytes( encPartLength ) );
334            encPart.encode( buffer );
335        }
336        catch ( BufferOverflowException boe )
337        {
338            LOG.error( I18n.err( I18n.ERR_137, 1 + TLV.getNbBytes( ticketLength ) + ticketLength,
339                buffer.capacity() ) );
340            throw new EncoderException( I18n.err( I18n.ERR_138 ), boe );
341        }
342
343        if ( IS_DEBUG )
344        {
345            LOG.debug( "Ticket encoding : {}", Strings.dumpBytes( buffer.array() ) );
346            LOG.debug( "Ticket initial value : {}", this );
347        }
348
349        return buffer;
350    }
351
352
353    /**
354     * {@inheritDoc}
355     */
356    @Override
357    public int hashCode()
358    {
359        final int prime = 31;
360        int result = 1;
361        result = prime * result + ( ( encPart == null ) ? 0 : encPart.hashCode() );
362        result = prime * result + ( ( realm == null ) ? 0 : realm.hashCode() );
363        result = prime * result + ( ( sName == null ) ? 0 : sName.hashCode() );
364        return result;
365    }
366
367
368    /**
369     * {@inheritDoc}
370     */
371    @Override
372    public boolean equals( Object obj )
373    {
374        if ( this == obj )
375        {
376            return true;
377        }
378
379        if ( !( obj instanceof Ticket ) )
380        {
381            return false;
382        }
383
384        Ticket other = ( Ticket ) obj;
385
386        if ( encPart == null )
387        {
388            if ( other.encPart != null )
389            {
390                return false;
391            }
392        }
393        else if ( !encPart.equals( other.encPart ) )
394        {
395            return false;
396        }
397
398        if ( realm == null )
399        {
400            if ( other.realm != null )
401            {
402                return false;
403            }
404        }
405        else if ( !realm.equals( other.realm ) )
406        {
407            return false;
408        }
409
410        if ( sName == null )
411        {
412            if ( other.sName != null )
413            {
414                return false;
415            }
416        }
417        else if ( !sName.equals( other.sName ) )
418        {
419            return false;
420        }
421
422        return true;
423    }
424
425
426    /**
427     * Pretty print the instance
428     */
429    public String toString( String tabs )
430    {
431        StringBuilder sb = new StringBuilder();
432
433        sb.append( tabs ).append( "Ticket :\n" );
434        sb.append( tabs ).append( "  tkt-vno : " ).append( getProtocolVersionNumber() ).append( "\n" );
435        sb.append( tabs ).append( "  realm : " ).append( realm ).append( "\n" );
436        sb.append( tabs ).append( "  sname : " ).append( sName ).append( "\n" );
437        sb.append( tabs ).append( "  enc-part : " ).append( encPart ).append( "\n" );
438
439        return sb.toString();
440    }
441
442
443    /**
444     * @see Object#toString()
445     */
446    public String toString()
447    {
448        return toString( "" );
449    }
450}