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.List;
026
027import org.apache.directory.api.asn1.EncoderException;
028import org.apache.directory.api.asn1.ber.tlv.BerValue;
029import org.apache.directory.api.asn1.ber.tlv.TLV;
030import org.apache.directory.api.asn1.ber.tlv.UniversalTag;
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.messages.KerberosMessage;
035
036
037/**
038 * The KDC-REQ data structure. It will store the object described by the ASN.1 grammar :
039 * <pre>
040 * KDC-REQ    ::= SEQUENCE {
041 *      -- NOTE: first tag is [1], not [0]
042 *      pvno            [1] INTEGER (5) ,
043 *      msg-type        [2] INTEGER (10 -- AS -- | 12 -- TGS --),
044 *      padata          [3] SEQUENCE OF &lt;PA-DATA&gt; OPTIONAL
045                            -- NOTE: not empty --,
046 *      req-body        [4] &lt;KDC-REQ-BODY&gt;
047 * }
048 * </pre>
049 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
050 */
051public abstract class KdcReq extends KerberosMessage
052{
053    /** The PA-DATAs */
054    private List<PaData> paData;
055
056    /** The KDC-REQ-BODY */
057    private KdcReqBody kdcReqBody;
058
059    // Storage for computed lengths
060    private int pvnoLength;
061    private int msgTypeLength;
062    private int paDataLength;
063    private int paDataSeqLength;
064    private int[] paDataLengths;
065    private int kdcReqBodyLength;
066    private int kdcReqSeqLength;
067    private int kdcReqLength;
068
069
070    /**
071     * Creates a new instance of KDC-REQ.
072     */
073    public KdcReq( KerberosMessageType msgType )
074    {
075        super( msgType );
076        paData = new ArrayList<>();
077    }
078
079
080    /**
081     * @return the pvno
082     */
083    public int getPvno()
084    {
085        return getProtocolVersionNumber();
086    }
087
088
089    /**
090     * @param pvno the pvno to set
091     */
092    public void setPvno( int pvno )
093    {
094        setProtocolVersionNumber( pvno );
095    }
096
097
098    /**
099     * @return the paData
100     */
101    public List<PaData> getPaData()
102    {
103        return paData;
104    }
105
106
107    /**
108     * @param paData the paData to set
109     */
110    public void addPaData( PaData paData )
111    {
112        this.paData.add( paData );
113    }
114
115
116    /**
117     * @return the kdcReqBody
118     */
119    public KdcReqBody getKdcReqBody()
120    {
121        return kdcReqBody;
122    }
123
124
125    /**
126     * @param kdcReqBody the kdcReqBody to set
127     */
128    public void setKdcReqBody( KdcReqBody kdcReqBody )
129    {
130        this.kdcReqBody = kdcReqBody;
131    }
132
133
134    /**
135     * Compute the KDC-REQ length
136     * <pre>
137     * KDC-REQ :
138     * 
139     * 0x30 L1 KDC-REQ sequence
140     *  |
141     *  +--&gt; 0xA1 0x03 pvno tag
142     *  |     |
143     *  |     +--&gt; 0x02 0x01 0x05 pvno (5)
144     *  |
145     *  +--&gt; 0xA2 0x03 msg-type tag
146     *  |     |
147     *  |     +--&gt; 0x02 0x01 0x0A/0x0C msg-type : either AS-REQ (0x0A) or TGS-REQ (0x0C)
148     *  |     
149     *  +--&gt; 0xA3 L2 pa-data tag
150     *  |     |
151     *  |     +--&gt; 0x30 L2-1 pa-data SEQ
152     *  |           |
153     *  |           +--&gt; 0x30 L2-1-1 pa-data
154     *  |           |
155     *  |           +--&gt; 0x30 L2-1-2 pa-data
156     *  |           :
157     *  |     
158     *  +--&gt; 0xA4 L3 req-body tag
159     *  |     |
160     *  |     +--&gt; 0x30 L3-1 req-body (KDC-REQ-BODY)
161     * </pre>       
162     */
163    public int computeLength()
164    {
165        // The pvno length
166        pvnoLength = 1 + 1 + 1;
167        kdcReqSeqLength = 1 + TLV.getNbBytes( pvnoLength ) + pvnoLength;
168
169        // The msg-type length
170        msgTypeLength = 1 + 1 + 1;
171        kdcReqSeqLength += 1 + TLV.getNbBytes( msgTypeLength ) + msgTypeLength;
172
173        // Compute the pa-data length.
174        if ( !paData.isEmpty() )
175        {
176            paDataLengths = new int[paData.size()];
177            int pos = 0;
178            paDataSeqLength = 0;
179
180            for ( PaData paDataElem : paData )
181            {
182                paDataLengths[pos] = paDataElem.computeLength();
183                paDataSeqLength += paDataLengths[pos];
184                pos++;
185            }
186
187            paDataLength = 1 + TLV.getNbBytes( paDataSeqLength ) + paDataSeqLength;
188            kdcReqSeqLength += 1 + TLV.getNbBytes( paDataLength ) + paDataLength;
189        }
190
191        // The KDC-REQ-BODY length
192        kdcReqBodyLength = kdcReqBody.computeLength();
193        kdcReqSeqLength += 1 + TLV.getNbBytes( kdcReqBodyLength ) + kdcReqBodyLength;
194
195        // compute the global size
196        kdcReqLength = 1 + TLV.getNbBytes( kdcReqSeqLength ) + kdcReqSeqLength;
197
198        return kdcReqLength;
199    }
200
201
202    /**
203     * Encode the KDC-REQ component
204     * 
205     * @param buffer The buffer containing the encoded result
206     * @return The encoded component
207     * @throws EncoderException If the encoding failed
208     */
209    public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
210    {
211        if ( buffer == null )
212        {
213            throw new EncoderException( I18n.err( I18n.ERR_148 ) );
214        }
215
216        // The KDC-REQ SEQ Tag
217        buffer.put( UniversalTag.SEQUENCE.getValue() );
218        buffer.put( TLV.getBytes( kdcReqSeqLength ) );
219
220        // The PVNO -----------------------------------------------------------
221        // The tag
222        buffer.put( ( byte ) KerberosConstants.KDC_REQ_PVNO_TAG );
223        buffer.put( TLV.getBytes( pvnoLength ) );
224
225        // The value
226        BerValue.encode( buffer, getProtocolVersionNumber() );
227
228        // The msg-type if any ------------------------------------------------
229        // The tag
230        buffer.put( ( byte ) KerberosConstants.KDC_REQ_MSG_TYPE_TAG );
231        buffer.put( TLV.getBytes( msgTypeLength ) );
232
233        // The value
234        BerValue.encode( buffer, getMessageType().getValue() );
235
236        // The PD-DATA if any -------------------------------------------------
237        if ( !paData.isEmpty() )
238        {
239            // The tag
240            buffer.put( ( byte ) KerberosConstants.KDC_REQ_PA_DATA_TAG );
241            buffer.put( TLV.getBytes( paDataLength ) );
242
243            // The sequence
244            buffer.put( UniversalTag.SEQUENCE.getValue() );
245            buffer.put( TLV.getBytes( paDataSeqLength ) );
246
247            // The values
248            for ( PaData paDataElem : paData )
249            {
250                paDataElem.encode( buffer );
251            }
252        }
253
254        // The KDC-REQ-BODY ---------------------------------------------------
255        // The tag
256        buffer.put( ( byte ) KerberosConstants.KDC_REQ_KDC_REQ_BODY_TAG );
257        buffer.put( TLV.getBytes( kdcReqBodyLength ) );
258
259        // The value
260        kdcReqBody.encode( buffer );
261
262        return buffer;
263    }
264
265
266    /**
267     * Pretty print the instance
268     */
269    public String toString( String tabs )
270    {
271        StringBuilder sb = new StringBuilder();
272
273        sb.append( tabs ).append(
274            "\n>-------------------------------------------------------------------------------\n" );
275        if ( getMessageType() == KerberosMessageType.AS_REQ )
276        {
277            sb.append( tabs ).append( "AS-REQ" ).append( '\n' );
278        }
279        else if ( getMessageType() == KerberosMessageType.TGS_REQ )
280        {
281            sb.append( tabs ).append( "TGS-REQ" ).append( '\n' );
282        }
283        else
284        {
285            sb.append( tabs ).append( "Unknown" ).append( '\n' );
286        }
287
288        sb.append( tabs ).append( "pvno : " ).append( getProtocolVersionNumber() ).append( '\n' );
289
290        sb.append( tabs ).append( "msg-type : " ).append( getMessageType() ).append( "\n" );
291
292        for ( PaData paDataElem : paData )
293        {
294            sb.append( tabs ).append( "padata :\n" );
295            sb.append( paDataElem.toString( tabs + "    " ) ).append( '\n' );
296        }
297
298        sb.append( tabs ).append( "kdc-req-body : \n" );
299        sb.append( kdcReqBody.toString( tabs + "    " ) ).append( '\n' );
300        sb.append( tabs ).append( "\n-------------------------------------------------------------------------------<\n" );
301
302        return sb.toString();
303    }
304
305
306    /**
307     * @see Object#toString()
308     */
309    public String toString()
310    {
311        return toString( "" );
312    }
313}