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.codec.options.ApOptions;
035import org.apache.directory.shared.kerberos.components.EncryptedData;
036import org.slf4j.Logger;
037import org.slf4j.LoggerFactory;
038
039
040/**
041 * AP-REQ message component . It will store the object described by the ASN.1 grammar :
042 * <pre>
043 * AP-REQ          ::= [APPLICATION 14] SEQUENCE {
044 *         pvno            [0] INTEGER (5),
045 *         msg-type        [1] INTEGER (14),
046 *         ap-options      [2] APOptions,
047 *         ticket          [3] Ticket,
048 *         authenticator   [4] EncryptedData -- Authenticator
049 * }
050 * </pre>
051 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
052 */
053public class ApReq extends KerberosMessage
054{
055    /** The logger */
056    private static final Logger LOG = LoggerFactory.getLogger( ApReq.class );
057
058    /** Speedup for logs */
059    private static final boolean IS_DEBUG = LOG.isDebugEnabled();
060
061    /** The AP options */
062    private ApOptions apOptions;
063
064    /** The Ticket */
065    private Ticket ticket;
066
067    /** The encryptedData, an encrypted Authenticator */
068    private EncryptedData authenticator;
069
070    // Storage for computed lengths
071    private int pvnoLength;
072    private int msgTypeLength;
073    private int apOptionsLength;
074    private int ticketLength;
075    private int authenticatorLength;
076    private int apReqLength;
077    private int apReqSeqLength;
078
079
080    /**
081     * Creates a new instance of ApplicationRequest.
082     */
083    public ApReq()
084    {
085        super( KerberosMessageType.AP_REQ );
086    }
087
088
089    /**
090     * Returns the {@link ApOptions}.
091     *
092     * @return The {@link ApOptions}.
093     */
094    public ApOptions getApOptions()
095    {
096        return apOptions;
097    }
098
099
100    /**
101     * Returns the {@link Ticket}.
102     *
103     * @return The {@link Ticket}.
104     */
105    public Ticket getTicket()
106    {
107        return ticket;
108    }
109
110
111    /**
112     * Returns the option at a specified index.
113     *
114     * @param option
115     * @return The option.
116     */
117    public boolean getOption( int option )
118    {
119        return apOptions.get( option );
120    }
121
122
123    /**
124     * Sets the options
125     *
126     * @param apOptions The options to set
127     */
128    public void setOption( ApOptions apOptions )
129    {
130        this.apOptions = apOptions;
131    }
132
133
134    /**
135     * Clears the option at a specified index.
136     *
137     * @param option
138     */
139    public void clearOption( int option )
140    {
141        apOptions.clear( option );
142    }
143
144
145    /**
146     * Returns the {@link EncryptedData}.
147     *
148     * @return The {@link EncryptedData}.
149     */
150    public EncryptedData getAuthenticator()
151    {
152        return authenticator;
153    }
154
155
156    /**
157     * Sets the {@link EncryptedData}.
158     *
159     * @param authenticator The encrypted authenticator
160     */
161    public void setAuthenticator( EncryptedData authenticator )
162    {
163        this.authenticator = authenticator;
164    }
165
166
167    /**
168     * Sets the {@link ApOptions}.
169     *
170     * @param options
171     */
172    public void setApOptions( ApOptions options )
173    {
174        apOptions = options;
175    }
176
177
178    /**
179     * Sets the {@link Ticket}.
180     *
181     * @param ticket
182     */
183    public void setTicket( Ticket ticket )
184    {
185        this.ticket = ticket;
186    }
187
188
189    /**
190     * Compute the AP-REQ length
191     * <pre>
192     * AP-REQ :
193     * 
194     * 0x6E L1 AP-REQ [APPLICATION 14]
195     *  |
196     *  +--&gt; 0x30 L2
197     *        |
198     *        +--&gt; 0xA0 0x03 pvno tag
199     *        |     |
200     *        |     +--&gt; 0x02 0x01 0x05 pvno (5)
201     *        |
202     *        +--&gt; 0xA1 0x03 msg-type tag
203     *        |     |
204     *        |     +--&gt; 0x02 0x01 0x0E msg-type (14)
205     *        |
206     *        +--&gt; 0xA2 0x03 APOptions tag
207     *        |     |
208     *        |     +--&gt; 0x03 0x05 0x00 b1 b2 b3 b4 APOtions
209     *        |
210     *        +--&gt; 0xA3 L3 ticket tag
211     *        |     |
212     *        |     +--&gt; 0x61 L3-1 ticket
213     *        |
214     *        +--&gt; 0xA4 L4 authenticator tag
215     *              |
216     *              +--&gt; 0x30 L4-1 authenticator (encrypted)
217     * </pre>
218     */
219    public int computeLength()
220    {
221        reset();
222
223        // Compute the PVNO length.
224        pvnoLength = 1 + 1 + BerValue.getNbBytes( getProtocolVersionNumber() );
225
226        // Compute the msg-type length
227        msgTypeLength = 1 + 1 + BerValue.getNbBytes( getMessageType().getValue() );
228
229        // Compute the APOptions length
230        apOptionsLength = 1 + 1 + apOptions.getBytes().length;
231
232        // Compute the ticket length
233        ticketLength = ticket.computeLength();
234
235        // Compute the authenticator length
236        authenticatorLength = authenticator.computeLength();
237
238        // Compute the sequence size
239        apReqLength =
240            1 + TLV.getNbBytes( pvnoLength ) + pvnoLength +
241                1 + TLV.getNbBytes( msgTypeLength ) + msgTypeLength +
242                1 + TLV.getNbBytes( apOptionsLength ) + apOptionsLength +
243                1 + TLV.getNbBytes( ticketLength ) + ticketLength +
244                1 + TLV.getNbBytes( authenticatorLength ) + authenticatorLength;
245
246        apReqSeqLength = 1 + TLV.getNbBytes( apReqLength ) + apReqLength;
247
248        return 1 + TLV.getNbBytes( apReqSeqLength ) + apReqSeqLength;
249    }
250
251
252    /**
253     * Encode the AP-REQ component
254     * 
255     * @param buffer The buffer containing the encoded result
256     * @return The encoded component
257     * @throws EncoderException If the encoding failed
258     */
259    public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
260    {
261        if ( buffer == null )
262        {
263            buffer = ByteBuffer.allocate( computeLength() );
264        }
265
266        try
267        {
268            // The AP-REP Tag
269            buffer.put( ( byte ) KerberosConstants.AP_REQ_TAG );
270            buffer.put( TLV.getBytes( apReqSeqLength ) );
271
272            // The AP-REP SEQ Tag
273            buffer.put( UniversalTag.SEQUENCE.getValue() );
274            buffer.put( TLV.getBytes( apReqLength ) );
275
276            // The PVNO -------------------------------------------------------
277            // The tag
278            buffer.put( ( byte ) KerberosConstants.AP_REQ_PVNO_TAG );
279            buffer.put( TLV.getBytes( pvnoLength ) );
280
281            // The value
282            BerValue.encode( buffer, getProtocolVersionNumber() );
283
284            // The msg-type ---------------------------------------------------
285            // The tag
286            buffer.put( ( byte ) KerberosConstants.AP_REQ_MSG_TYPE_TAG );
287            buffer.put( TLV.getBytes( msgTypeLength ) );
288
289            // The value
290            BerValue.encode( buffer, getMessageType().getValue() );
291
292            // The ap-options -------------------------------------------------
293            // The tag
294            buffer.put( ( byte ) KerberosConstants.AP_REQ_AP_OPTIONS_TAG );
295            buffer.put( TLV.getBytes( apOptionsLength ) );
296
297            // The value
298            BerValue.encode( buffer, apOptions );
299
300            // The ticket -----------------------------------------------------
301            // The tag
302            buffer.put( ( byte ) KerberosConstants.AP_REQ_TICKET_TAG );
303            buffer.put( TLV.getBytes( ticketLength ) );
304
305            // The value
306            ticket.encode( buffer );
307
308            // The authenticator ----------------------------------------------
309            // The tag
310            buffer.put( ( byte ) KerberosConstants.AP_REQ_AUTHENTICATOR_TAG );
311            buffer.put( TLV.getBytes( authenticatorLength ) );
312
313            // The value
314            authenticator.encode( buffer );
315        }
316        catch ( BufferOverflowException boe )
317        {
318            LOG.error( I18n.err( I18n.ERR_137, 1 + TLV.getNbBytes( apReqLength ) + apReqLength,
319                buffer.capacity() ) );
320            throw new EncoderException( I18n.err( I18n.ERR_138 ), boe );
321        }
322
323        if ( IS_DEBUG )
324        {
325            LOG.debug( "AP-REQ encoding : {}", Strings.dumpBytes( buffer.array() ) );
326            LOG.debug( "AP-REQ initial value : {}", this );
327        }
328
329        return buffer;
330    }
331
332
333    /**
334     * reset the fields used while computing length
335     */
336    private void reset()
337    {
338        pvnoLength = 0;
339        msgTypeLength = 0;
340        apOptionsLength = 0;
341        ticketLength = 0;
342        authenticatorLength = 0;
343        apReqLength = 0;
344        apReqSeqLength = 0;
345    }
346
347
348    /**
349     * @see Object#toString()
350     */
351    public String toString()
352    {
353        StringBuilder sb = new StringBuilder();
354
355        sb.append( "AP-REQ :\n" );
356        sb.append( "  pvno : " ).append( getProtocolVersionNumber() ).append( "\n" );
357        sb.append( "  msg-type : " ).append( getMessageType() ).append( "\n" );
358        sb.append( "  ap-options : " ).append( apOptions ).append( "\n" );
359        sb.append( "  ticket : " ).append( ticket ).append( "\n" );
360        sb.append( "  authenticator : " ).append( authenticator ).append( "\n" );
361
362        return sb.toString();
363    }
364}