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