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.components;
021    
022    
023    import java.nio.BufferOverflowException;
024    import java.nio.ByteBuffer;
025    import java.text.ParseException;
026    
027    import javax.security.auth.kerberos.KerberosPrincipal;
028    
029    import org.apache.directory.server.kerberos.shared.KerberosConstants;
030    import org.apache.directory.server.kerberos.shared.KerberosUtils;
031    import org.apache.directory.server.kerberos.shared.exceptions.ErrorType;
032    import org.apache.directory.server.kerberos.shared.messages.value.AuthorizationData;
033    import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
034    import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
035    import org.apache.directory.server.kerberos.shared.messages.value.HostAddresses;
036    import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
037    import org.apache.directory.server.kerberos.shared.messages.value.PrincipalName;
038    import org.apache.directory.server.kerberos.shared.messages.value.TransitedEncoding;
039    import org.apache.directory.server.kerberos.shared.messages.value.flags.TicketFlags;
040    import org.apache.directory.shared.asn1.AbstractAsn1Object;
041    import org.apache.directory.shared.asn1.ber.tlv.TLV;
042    import org.apache.directory.shared.asn1.ber.tlv.UniversalTag;
043    import org.apache.directory.shared.asn1.ber.tlv.Value;
044    import org.apache.directory.shared.asn1.codec.EncoderException;
045    import org.apache.directory.shared.ldap.util.StringTools;
046    import org.slf4j.Logger;
047    import org.slf4j.LoggerFactory;
048    
049    
050    /**
051     * Ticket message component as handed out by the ticket granting service.
052     * 
053     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
054     * @version $Rev: 642496 $, $Date: 2008-03-29 05:09:22 +0200 (Sat, 29 Mar 2008) $
055     */
056    public class Ticket extends AbstractAsn1Object
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        /** The Kerberos version number. Should be 5 */
068        private int tktvno;
069        
070        /** A storage for a byte array representation of the realm */
071        private byte[] realmBytes;
072        
073        /** The server principal name */
074        private PrincipalName sName;
075        
076        /** The server realm */
077        private String realm;
078        
079        /** The encoded part */
080        private EncryptedData encPart;
081        
082        /** The decoded ticket part */
083        private EncTicketPart encTicketPart;
084    
085        // Storage for computed lengths
086        private transient int tktvnoLength;
087        private transient int realmLength;
088        private transient int sNameLength;
089        private transient int encPartLength;
090        private transient int ticketSeqLength;
091        private transient int ticketLength;
092    
093        /**
094         * Creates a new instance of Ticket.
095         *
096         * @param serverPrincipal The server principal
097         * @param encPart The encoded part
098         */
099        public Ticket( KerberosPrincipal serverPrincipal, EncryptedData encPart ) throws InvalidTicketException
100        {
101            this( TICKET_VNO, serverPrincipal, encPart );
102    
103            setServerPrincipal( serverPrincipal );
104        }
105    
106    
107        /**
108         * Creates a new instance of Ticket.
109         */
110        public Ticket()
111        {
112        }
113        
114        
115        /**
116         * Creates a new instance of Ticket.
117         *
118         * @param tktvno The Kerberos version number
119         * @param serverPrincipal The server principal
120         * @param encPart The encoded part
121         */
122        public Ticket( int tktvno, KerberosPrincipal serverPrincipal, EncryptedData encPart ) throws InvalidTicketException
123        {
124            this.tktvno = tktvno;
125            this.encPart = encPart;
126            setServerPrincipal( serverPrincipal );
127        }
128    
129    
130        /**
131         * Sets the {@link EncTicketPart}.
132         *
133         * @param decryptedPart
134         */
135        public void setEncTicketPart( EncTicketPart decryptedPart )
136        {
137            encTicketPart = decryptedPart;
138        }
139    
140    
141        /**
142         * Returns the version number.
143         *
144         * @return The version number.
145         */
146        public int getTktVno()
147        {
148            return tktvno;
149        }
150        
151        
152        /**
153         * Set the ticket version number
154         * @param tktvno the ticket version number
155         */
156        public void setTktVno( int tktvno )
157        {
158            this.tktvno = tktvno;
159        }
160    
161    
162        /**
163         * Returns the server {@link PrincipalName}.
164         *
165         * @return The server {@link PrincipalName}.
166         */
167        public PrincipalName getSName()
168        {
169            return sName;
170        }
171    
172        
173        /**
174         * Returns the server {@link KerberosPrincipal}.
175         *
176         * @return The server {@link KerberosPrincipal}.
177         */
178        public KerberosPrincipal getServerPrincipal()
179        {
180            return KerberosUtils.getKerberosPrincipal( sName, realm );
181        }
182    
183        
184        /**
185         * Set the server principalName
186         * @param sName the server principalName
187         */
188        public void setSName( PrincipalName sName )
189        {
190            this.sName = sName;
191        }
192        
193    
194        /**
195         * Set the server KerberosPrincipal
196         * @param serverPrincipal the server KerberosPrincipal
197         */
198        public void setServerPrincipal( KerberosPrincipal serverPrincipal ) throws InvalidTicketException
199        {
200            try
201            {
202                sName = new PrincipalName( serverPrincipal.getName(), serverPrincipal.getNameType() );
203                realm = serverPrincipal.getRealm();
204            }
205            catch ( ParseException pe )
206            {
207                LOG.error( "Cannot create a ticket for the {} KerberosPrincipal, error : {}", serverPrincipal, pe.getMessage() );
208                throw new InvalidTicketException( ErrorType.KRB_ERR_GENERIC, "Cannot create a ticket : " + pe.getMessage() );
209            }
210        }
211        
212    
213        /**
214         * Returns the server realm.
215         *
216         * @return The server realm.
217         */
218        public String getRealm()
219        {
220            return realm;
221        }
222    
223    
224        /**
225         * Set the server realm
226         * @param realm the server realm
227         */
228        public void setRealm( String realm )
229        {
230            this.realm = realm;
231        }
232        
233       
234        /**
235         * Returns the {@link EncryptedData}.
236         *
237         * @return The {@link EncryptedData}.
238         */
239        public EncryptedData getEncPart()
240        {
241            return encPart;
242        }
243    
244        
245        /**
246         * Set the encrypted ticket part
247         * @param encPart the encrypted ticket part
248         */
249        public void setEncPart( EncryptedData encPart )
250        {
251            this.encPart = encPart; 
252        }
253        
254    
255        /**
256         * Returns the {@link EncTicketPart}.
257         *
258         * @return The {@link EncTicketPart}.
259         */
260        public EncTicketPart getEncTicketPart()
261        {
262            return encTicketPart;
263        }
264    
265    
266        /**
267         * Returns the {@link AuthorizationData}.
268         *
269         * @return The {@link AuthorizationData}.
270         *
271        public AuthorizationData getAuthorizationData()
272        {
273            return encTicketPart.getAuthorizationData();
274        }
275        */
276    
277        /**
278         * Returns the auth {@link KerberosTime}.
279         *
280         * @return The auth {@link KerberosTime}.
281         *
282        public KerberosTime getAuthTime()
283        {
284            return encTicketPart.getAuthTime();
285        }
286        */
287    
288        /**
289         * Returns the client {@link HostAddresses}.
290         *
291         * @return The client {@link HostAddresses}.
292         *
293        public HostAddresses getClientAddresses()
294        {
295            return encTicketPart.getClientAddresses();
296        }
297        */
298    
299        /**
300         * Returns the client {@link KerberosPrincipal}.
301         *
302         * @return The client {@link KerberosPrincipal}.
303         *
304        public KerberosPrincipal getClientPrincipal()
305        {
306            return encTicketPart.getClientPrincipal();
307        }
308        */
309        
310        /**
311         * Returns the client {@link PrincipalName}.
312         *
313         * @return The client {@link PrincipalName}.
314         *
315        public PrincipalName getClientPrincipalName()
316        {
317            return encTicketPart.getClientPrincipalName();
318        }
319        */
320    
321        /**
322         * Returns the client realm.
323         *
324         * @return The client realm.
325         *
326        public String getClientRealm()
327        {
328            return encTicketPart.getClientRealm();
329        }
330        */
331    
332        /**
333         * Returns the end {@link KerberosTime}.
334         *
335         * @return The end {@link KerberosTime}.
336         *
337        public KerberosTime getEndTime()
338        {
339            return encTicketPart.getEndTime();
340        }
341        */
342    
343        /**
344         * Returns the {@link TicketFlags}.
345         *
346         * @return The {@link TicketFlags}.
347         *
348        public TicketFlags getFlags()
349        {
350            return encTicketPart.getFlags();
351        }
352        */
353        
354        /**
355         * Returns the integer value for the {@link TicketFlags}.
356         *
357         * @return The {@link TicketFlags}.
358         *
359        public int getFlagsIntValue()
360        {
361            return encTicketPart.getFlags().getIntValue();
362        }
363        */
364    
365        /**
366         * Returns the renew till {@link KerberosTime}.
367         *
368         * @return The renew till {@link KerberosTime}.
369         *
370        public KerberosTime getRenewTill()
371        {
372            return encTicketPart.getRenewTill();
373        }
374        */
375    
376        /**
377         * Returns the session {@link EncryptionKey}.
378         *
379         * @return The session {@link EncryptionKey}.
380         *
381        public EncryptionKey getSessionKey()
382        {
383            return encTicketPart.getSessionKey();
384        }
385        */
386    
387        /**
388         * Returns the start {@link KerberosTime}.
389         *
390         * @return The start {@link KerberosTime}.
391         *
392        public KerberosTime getStartTime()
393        {
394            return encTicketPart.getStartTime();
395        }
396        */
397    
398        /**
399         * Returns the {@link TransitedEncoding}.
400         *
401         * @return The {@link TransitedEncoding}.
402         *
403        public TransitedEncoding getTransitedEncoding()
404        {
405            return encTicketPart.getTransitedEncoding();
406        }
407        */
408    
409        /**
410         * Returns the flag at the given index.
411         *
412         * @param flag
413         * @return true if the flag at the given index is set.
414         *
415        public boolean getFlag( int flag )
416        {
417            return encTicketPart.getFlags().isFlagSet( flag );
418        }
419        */
420        
421        /**
422         * Compute the Ticket length
423         * 
424         * Ticket :
425         * 
426         * 0x61 L1 Ticket [APPLICATION 1]
427         *  |
428         *  +--> 0x30 L2 Ticket SEQUENCE
429         *        |
430         *        +--> 0xA0 L3 tkt-vno tag
431         *        |     |
432         *        |     +--> 0x02 L3-1 tkt-vno (int, 5)
433         *        |
434         *        +--> 0xA1 L4 realm tag
435         *        |     |
436         *        |     +--> 0x1B L4-1 realm (KerberosString)
437         *        |
438         *        +--> 0xA2 L5 sname (PrincipalName)
439         *        |
440         *        +--> 0xA3 L6 enc-part (EncryptedData)
441         */
442        public int computeLength()
443        {
444            // Compute the Ticket version length.
445            tktvnoLength = 1 + TLV.getNbBytes( tktvno ) + Value.getNbBytes( tktvno );
446    
447            // Compute the Ticket realm length.
448            realmBytes = StringTools.getBytesUtf8( realm );
449            realmLength = 1 + TLV.getNbBytes( realmBytes.length ) + realmBytes.length;
450    
451            // Compute the principal length
452            sNameLength = sName.computeLength();
453            
454            // Compute the encrypted data
455            encPartLength = encPart.computeLength();
456    
457            // Compute the sequence size
458            ticketSeqLength = 
459                1 + TLV.getNbBytes( tktvnoLength ) + tktvnoLength +
460                1 + TLV.getNbBytes( realmLength ) + realmLength +
461                1 + TLV.getNbBytes( sNameLength ) + sNameLength + 
462                1 + TLV.getNbBytes( encPartLength ) + encPartLength;
463            
464            // compute the global size
465            ticketLength = 1 + TLV.getNbBytes( ticketSeqLength ) + ticketSeqLength;
466            
467            return 1 + TLV.getNbBytes( ticketLength ) + ticketLength;
468        }
469        
470        /**
471         * Encode the Ticket message to a PDU. 
472         * 
473         * Ticket :
474         * 
475         * 0x61 LL
476         *   0x30 LL
477         *     0xA0 LL tktvno 
478         *     0xA1 LL realm
479         *     0xA2 LL
480         *       sname (PrincipalName)
481         *     0xA3 LL
482         *       enc-part (EncryptedData)
483         * 
484         * @param buffer The buffer where to put the PDU. It should have been allocated
485         * before, with the right size.
486         * @return The constructed PDU.
487         */
488        public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
489        {
490            if ( buffer == null )
491            {
492                buffer = ByteBuffer.allocate( computeLength() );
493            }
494    
495            try
496            {
497                // The Ticket APPLICATION Tag
498                buffer.put( (byte)0x61 );
499                buffer.put( TLV.getBytes( ticketLength ) );
500    
501                // The Ticket SEQUENCE Tag
502                buffer.put( UniversalTag.SEQUENCE_TAG );
503                buffer.put( TLV.getBytes( ticketSeqLength ) );
504    
505                // The tkt-vno Tag and value
506                buffer.put( ( byte ) 0xA0 );
507                buffer.put( TLV.getBytes( tktvnoLength ) );
508                Value.encode( buffer, tktvno );
509    
510                // The realm Tag and value
511                buffer.put( ( byte ) 0xA1 );
512                buffer.put( TLV.getBytes( realmLength ) );
513                buffer.put( UniversalTag.GENERALIZED_STRING_TAG );
514                buffer.put( TLV.getBytes( realmBytes.length ) );
515                buffer.put( realmBytes );
516    
517                // The sname Tag and value
518                buffer.put( ( byte ) 0xA2 );
519                buffer.put( TLV.getBytes( sNameLength ) );
520                sName.encode( buffer );
521                
522                // The encPartLength Tag and value
523                buffer.put( ( byte ) 0xA3 );
524                buffer.put( TLV.getBytes( encPartLength ) );
525                encPart.encode( buffer );
526            }
527            catch ( BufferOverflowException boe )
528            {
529                LOG.error( "Cannot encode the Ticket object, the PDU size is {} when only {} bytes has been allocated", 1
530                    + TLV.getNbBytes( ticketLength ) + ticketLength, buffer.capacity() );
531                throw new EncoderException( "The PDU buffer size is too small !" );
532            }
533    
534            if ( IS_DEBUG )
535            {
536                LOG.debug( "Ticket encoding : {}", StringTools.dumpBytes( buffer.array() ) );
537                LOG.debug( "Ticket initial value : {}", toString() );
538            }
539    
540            return buffer;
541        }
542    }