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.KerberosTime;
035import org.apache.directory.shared.kerberos.components.PrincipalName;
036import org.apache.directory.shared.kerberos.exceptions.ErrorType;
037import org.slf4j.Logger;
038import org.slf4j.LoggerFactory;
039
040
041/**
042 * Class representing KRB-ERROR message
043 * 
044 * <pre>
045 * KRB-ERROR       ::= [APPLICATION 30] SEQUENCE {
046 *      pvno            [0] INTEGER (5),
047 *      msg-type        [1] INTEGER (30),
048 *      ctime           [2] KerberosTime OPTIONAL,
049 *      cusec           [3] Microseconds OPTIONAL,
050 *      stime           [4] KerberosTime,
051 *      susec           [5] Microseconds,
052 *      error-code      [6] Int32,
053 *      crealm          [7] Realm OPTIONAL,
054 *      cname           [8] PrincipalName OPTIONAL,
055 *      realm           [9] Realm -- service realm --,
056 *      sname           [10] PrincipalName -- service name --,
057 *      e-text          [11] KerberosString OPTIONAL,
058 *      e-data          [12] OCTET STRING OPTIONAL
059 * }
060 * </pre>
061 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
062 */
063public class KrbError extends KerberosMessage
064{
065
066    /** The logger */
067    private static final Logger log = LoggerFactory.getLogger( KrbError.class );
068
069    /** Speedup for logs */
070    private static final boolean IS_DEBUG = log.isDebugEnabled();
071
072    /** the current time of client */
073    private KerberosTime cTime;
074
075    /** microseconds of the client's current time */
076    private Integer cusec;
077
078    /** current time on the server */
079    private KerberosTime sTime;
080
081    /** microseconds of the server's time */
082    private int susec;
083
084    /** the error code */
085    private ErrorType errorCode;
086
087    /** the name of the realm to which the requesting client belongs */
088    private String cRealm;
089
090    /** the client's principal */
091    private PrincipalName cName;
092
093    /** the realm that issued the ticket */
094    private String realm;
095
096    /** the server's principal */
097    private PrincipalName sName;
098
099    /** the error text */
100    private String eText;
101
102    /** the error data */
103    private byte[] eData;
104
105    // Storage for computed lengths
106    private int pvnoLength;
107    private int msgTypeLength;
108    private int cTimeLength;
109    private int cusecLength;
110    private int sTimeLength;
111    private int susecLength;
112    private int errorCodeLength;
113    private int cRealmLength;
114    private byte[] crealmBytes;
115    private int cNameLength;
116    private int realmLength;
117    private byte[] realmBytes;
118    private int sNameLength;
119    private int eTextLength;
120    private byte[] eTextBytes;
121    private int eDataLength;
122    private int krbErrorSeqLength;
123    private int krbErrorLength;
124
125
126    /**
127     * Creates a new instance of KrbError.
128     */
129    public KrbError()
130    {
131        super( KerberosMessageType.KRB_ERROR );
132    }
133
134
135    /**
136     * @return the cTime
137     */
138    public KerberosTime getCTime()
139    {
140        return cTime;
141    }
142
143
144    /**
145     * @param cTime the cTime to set
146     */
147    public void setCTime( KerberosTime cTime )
148    {
149        this.cTime = cTime;
150    }
151
152
153    /**
154     * @return the cusec
155     */
156    public int getCusec()
157    {
158        if ( cusec == null )
159        {
160            return 0;
161        }
162
163        return cusec;
164    }
165
166
167    /**
168     * @param cusec the cusec to set
169     */
170    public void setCusec( int cusec )
171    {
172        this.cusec = cusec;
173    }
174
175
176    /**
177     * @return the sTime
178     */
179    public KerberosTime getSTime()
180    {
181        return sTime;
182    }
183
184
185    /**
186     * @param sTime the sTime to set
187     */
188    public void setSTime( KerberosTime sTime )
189    {
190        this.sTime = sTime;
191    }
192
193
194    /**
195     * @return the susec
196     */
197    public int getSusec()
198    {
199        return susec;
200    }
201
202
203    /**
204     * @param susec the susec to set
205     */
206    public void setSusec( int susec )
207    {
208        this.susec = susec;
209    }
210
211
212    /**
213     * @return the errorCode
214     */
215    public ErrorType getErrorCode()
216    {
217        return errorCode;
218    }
219
220
221    /**
222     * @param errorCode the errorCode to set
223     */
224    public void setErrorCode( ErrorType errorCode )
225    {
226        this.errorCode = errorCode;
227    }
228
229
230    /**
231     * @return the cRealm
232     */
233    public String getCRealm()
234    {
235        return cRealm;
236    }
237
238
239    /**
240     * @param cRealm the cRealm to set
241     */
242    public void setCRealm( String cRealm )
243    {
244        this.cRealm = cRealm;
245    }
246
247
248    /**
249     * @return the cName
250     */
251    public PrincipalName getCName()
252    {
253        return cName;
254    }
255
256
257    /**
258     * @param cName the cName to set
259     */
260    public void setCName( PrincipalName cName )
261    {
262        this.cName = cName;
263    }
264
265
266    /**
267     * @return the realm
268     */
269    public String getRealm()
270    {
271        return realm;
272    }
273
274
275    /**
276     * @param realm the realm to set
277     */
278    public void setRealm( String realm )
279    {
280        this.realm = realm;
281    }
282
283
284    /**
285     * @return the sName
286     */
287    public PrincipalName getSName()
288    {
289        return sName;
290    }
291
292
293    /**
294     * @param sName the sName to set
295     */
296    public void setSName( PrincipalName sName )
297    {
298        this.sName = sName;
299    }
300
301
302    /**
303     * @return the eText
304     */
305    public String getEText()
306    {
307        return eText;
308    }
309
310
311    /**
312     * @param eText the eText to set
313     */
314    public void setEText( String eText )
315    {
316        this.eText = eText;
317    }
318
319
320    /**
321     * @return the eData
322     */
323    public byte[] getEData()
324    {
325        return eData;
326    }
327
328
329    /**
330     * @param eData the eData to set
331     */
332    public void setEData( byte[] eData )
333    {
334        this.eData = eData;
335    }
336
337
338    /**
339     * Compute the KRB-ERROR length
340     * <pre>
341     * KRB-ERROR :
342     * 
343     * 0x7E L1 KRB-ERROR APPLICATION[30]
344     *  |
345     *  +--&gt; 0x30 L2 KRB-ERROR sequence
346     *        |
347     *        +--&gt; 0xA0 0x03 pvno tag
348     *        |     |
349     *        |     +--&gt; 0x02 0x01 0x05 pvno (5)
350     *        |
351     *        +--&gt; 0xA1 0x03 msg-type tag
352     *        |     |
353     *        |     +--&gt; 0x02 0x01 0x1E msg-type (30)
354     *        |     
355     *        +--&gt; 0xA2 0x11 ctime tag
356     *        |     |
357     *        |     +--&gt; 0x18 0x0F ttt ctime (KerberosTime)
358     *        |     
359     *        +--&gt; 0xA3 L3 cusec tag
360     *        |     |
361     *        |     +--&gt; 0x02 L3-1 cusec
362     *        |     
363     *        +--&gt; 0xA4 0x11 stime tag
364     *        |     |
365     *        |     +--&gt; 0x18 0x0F ttt stime (KerberosTime)
366     *        |     
367     *        +--&gt; 0xA5 L4 susec tag
368     *        |     |
369     *        |     +--&gt; 0x02 L4-1 susec (KerberosTime)
370     *        |     
371     *        +--&gt; 0xA6 L5 error-code tag
372     *        |     |
373     *        |     +--&gt; 0x02 L5-1 nnn error-code
374     *        |     
375     *        +--&gt; 0xA7 L6 crealm tag
376     *        |     |
377     *        |     +--&gt; 0x1B L6-1 crealm (KerberosString)
378     *        |     
379     *        +--&gt; 0xA8 L7 cname tag
380     *        |     |
381     *        |     +--&gt; 0x30 L7-1 cname (PrincipalName)
382     *        |
383     *        +--&gt; 0xA9 L8 realm tag
384     *        |     |
385     *        |     +--&gt; 0x1B L8-1 realm (KerberosString)
386     *        |     
387     *        +--&gt; 0xAA L9 sname tag
388     *        |     |
389     *        |     +--&gt; 0x30 L9-1 sname (PrincipalName)
390     *        |     
391     *        +--&gt; 0xAB L10 e-text tag
392     *        |     |
393     *        |     +--&gt; 0x1B L10-1 e-text (KerberosString)
394     *        |
395     *        +--&gt; 0xAC L11 e-data
396     *              |
397     *              +--&gt; 0x04 L11-1 e-data (Octet String)
398     * </pre>       
399     */
400    public int computeLength()
401    {
402        // The PVNO
403        pvnoLength = 1 + 1 + 1;
404        krbErrorSeqLength = 1 + TLV.getNbBytes( pvnoLength ) + pvnoLength;
405
406        // The message type
407        msgTypeLength = 1 + 1 + BerValue.getNbBytes( getMessageType().getValue() );
408        krbErrorSeqLength += 1 + TLV.getNbBytes( msgTypeLength ) + msgTypeLength;
409
410        // The ctime, if any
411        if ( cTime != null )
412        {
413            cTimeLength = 1 + 1 + 0x0F;
414            krbErrorSeqLength += 1 + TLV.getNbBytes( cTimeLength ) + cTimeLength;
415        }
416
417        // The cusec, if any
418        if ( cusec != null )
419        {
420            int cusecLen = BerValue.getNbBytes( cusec );
421            cusecLength = 1 + TLV.getNbBytes( cusecLen ) + cusecLen;
422            krbErrorSeqLength += 1 + TLV.getNbBytes( cusecLength ) + cusecLength;
423        }
424
425        // The stime
426        sTimeLength = 1 + 1 + 0x0F;
427        krbErrorSeqLength += 1 + TLV.getNbBytes( sTimeLength ) + sTimeLength;
428
429        // The susec
430        int susecLen = BerValue.getNbBytes( susec );
431        susecLength = 1 + TLV.getNbBytes( susecLen ) + susecLen;
432        krbErrorSeqLength += 1 + TLV.getNbBytes( susecLength ) + susecLength;
433
434        // The error-code
435        errorCodeLength = 1 + 1 + BerValue.getNbBytes( errorCode.getValue() );
436        krbErrorSeqLength += 1 + TLV.getNbBytes( errorCodeLength ) + errorCodeLength;
437
438        // The crealm, if any
439        if ( cRealm != null )
440        {
441            crealmBytes = Strings.getBytesUtf8( cRealm );
442            cRealmLength = 1 + TLV.getNbBytes( crealmBytes.length ) + crealmBytes.length;
443            krbErrorSeqLength += 1 + TLV.getNbBytes( cRealmLength ) + cRealmLength;
444        }
445
446        // The cname if any
447        if ( cName != null )
448        {
449            cNameLength = cName.computeLength();
450            krbErrorSeqLength += 1 + TLV.getNbBytes( cNameLength ) + cNameLength;
451        }
452
453        // The realm
454        realmBytes = Strings.getBytesUtf8( realm );
455        realmLength = 1 + TLV.getNbBytes( realmBytes.length ) + realmBytes.length;
456        krbErrorSeqLength += 1 + TLV.getNbBytes( realmLength ) + realmLength;
457
458        // The sname
459        sNameLength = sName.computeLength();
460        krbErrorSeqLength += 1 + TLV.getNbBytes( sNameLength ) + sNameLength;
461
462        // The e-text, if any
463        if ( eText != null )
464        {
465            eTextBytes = Strings.getBytesUtf8( eText );
466            eTextLength = 1 + TLV.getNbBytes( eTextBytes.length ) + eTextBytes.length;
467            krbErrorSeqLength += 1 + TLV.getNbBytes( eTextLength ) + eTextLength;
468        }
469
470        // The e-data, if any
471        if ( eData != null )
472        {
473            eDataLength = 1 + TLV.getNbBytes( eData.length ) + eData.length;
474            krbErrorSeqLength += 1 + TLV.getNbBytes( eDataLength ) + eDataLength;
475        }
476
477        // The global sequence length
478        krbErrorLength = 1 + TLV.getNbBytes( krbErrorSeqLength ) + krbErrorSeqLength;
479
480        return 1 + TLV.getNbBytes( krbErrorLength ) + krbErrorLength;
481    }
482
483
484    /**
485     * Encode the KRB-ERROR message to a PDU. 
486     * <pre>
487     * KRB-ERROR :
488     * 
489     * 0x7E LL
490     *   0x30 LL
491     *     0xA0 0x03 
492     *       0x02 0x01 0x05  pvno 
493     *     0xA1 0x03 
494     *       0x02 0x01 0x1E msg-type
495     *    [0xA2 0x11
496     *       0x18 0x0F ttt] ctime
497     *    [0xA3 LL
498     *       0x02 LL nnn] cusec
499     *     0xA4 0x11
500     *       0x18 0x0F ttt  stime
501     *     0xA5 LL
502     *       0x02 LL nnn susec
503     *     0xA6 LL
504     *       0x02 LL nnn error-code
505     *    [0xA7 LL
506     *       0x1B LL abcd] crealm
507     *    [0xA8 LL
508     *       0x30 LL abcd] cname
509     *     0xA9 LL
510     *       0x1B LL abcd realm
511     *     0xAA LL
512     *       0x30 LL abcd sname
513     *    [0xAB LL
514     *       0x1B LL abcd] e-text
515     *    [0xAC LL
516     *       0x04 LL abcd] e-data
517     * </pre>
518     * @return The constructed PDU.
519     */
520    public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
521    {
522        if ( buffer == null )
523        {
524            throw new EncoderException( I18n.err( I18n.ERR_148 ) );
525        }
526
527        try
528        {
529            // The KRB-ERROR APPLICATION tag
530            buffer.put( ( byte ) KerberosConstants.KRB_ERROR_TAG );
531            buffer.put( TLV.getBytes( krbErrorLength ) );
532
533            // The KRB_ERROR sequence
534            buffer.put( UniversalTag.SEQUENCE.getValue() );
535            buffer.put( TLV.getBytes( krbErrorSeqLength ) );
536
537            // pvno tag and value
538            buffer.put( ( byte ) KerberosConstants.KRB_ERROR_PVNO_TAG );
539            buffer.put( TLV.getBytes( pvnoLength ) );
540            BerValue.encode( buffer, getProtocolVersionNumber() );
541
542            // msg-type tag and value
543            buffer.put( ( byte ) KerberosConstants.KRB_ERROR_MSGTYPE_TAG );
544            buffer.put( TLV.getBytes( msgTypeLength ) );
545            BerValue.encode( buffer, getMessageType().getValue() );
546
547            // ctime tag and value if any
548            if ( cTimeLength > 0 )
549            {
550                // The tag
551                buffer.put( ( byte ) KerberosConstants.KRB_ERROR_CTIME_TAG );
552                buffer.put( TLV.getBytes( cTimeLength ) );
553
554                // The value
555                buffer.put( UniversalTag.GENERALIZED_TIME.getValue() );
556                buffer.put( ( byte ) 0x0F );
557                buffer.put( cTime.getBytes() );
558            }
559
560            // cusec tag and value if any
561            if ( cusec != null )
562            {
563                buffer.put( ( byte ) KerberosConstants.KRB_ERROR_CUSEC_TAG );
564                buffer.put( TLV.getBytes( cusecLength ) );
565                BerValue.encode( buffer, cusec );
566            }
567
568            // stime tag and value
569            // The tag
570            buffer.put( ( byte ) KerberosConstants.KRB_ERROR_STIME_TAG );
571            buffer.put( TLV.getBytes( sTimeLength ) );
572
573            // The value
574            buffer.put( UniversalTag.GENERALIZED_TIME.getValue() );
575            buffer.put( ( byte ) 0x0F );
576            buffer.put( sTime.getBytes() );
577
578            // susec tag and value
579            buffer.put( ( byte ) KerberosConstants.KRB_ERROR_SUSEC_TAG );
580            buffer.put( TLV.getBytes( susecLength ) );
581            BerValue.encode( buffer, susec );
582
583            // error-code tag and value
584            buffer.put( ( byte ) KerberosConstants.KRB_ERROR_ERROR_CODE_TAG );
585            buffer.put( TLV.getBytes( errorCodeLength ) );
586            BerValue.encode( buffer, errorCode.getValue() );
587
588            // crealm tage and value, if any
589            if ( cRealm != null )
590            {
591                buffer.put( ( byte ) KerberosConstants.KRB_ERROR_CREALM_TAG );
592                buffer.put( TLV.getBytes( cRealmLength ) );
593
594                buffer.put( UniversalTag.GENERAL_STRING.getValue() );
595                buffer.put( TLV.getBytes( crealmBytes.length ) );
596                buffer.put( crealmBytes );
597            }
598
599            // cname tag and value, if any
600            if ( cName != null )
601            {
602                buffer.put( ( byte ) KerberosConstants.KRB_ERROR_CNAME_TAG );
603                buffer.put( TLV.getBytes( cNameLength ) );
604                cName.encode( buffer );
605            }
606
607            // realm tag and value
608            // the tag
609            buffer.put( ( byte ) KerberosConstants.KRB_ERROR_REALM_TAG );
610            buffer.put( TLV.getBytes( realmLength ) );
611
612            // The value
613            buffer.put( UniversalTag.GENERAL_STRING.getValue() );
614            buffer.put( TLV.getBytes( realmBytes.length ) );
615            buffer.put( realmBytes );
616
617            // sname tag and value
618            buffer.put( ( byte ) KerberosConstants.KRB_ERROR_SNAME_TAG );
619            buffer.put( TLV.getBytes( sNameLength ) );
620            sName.encode( buffer );
621
622            // etext tag and value, if any
623            if ( eText != null )
624            {
625                buffer.put( ( byte ) KerberosConstants.KRB_ERROR_ETEXT_TAG );
626                buffer.put( TLV.getBytes( eTextLength ) );
627
628                buffer.put( UniversalTag.GENERAL_STRING.getValue() );
629                buffer.put( TLV.getBytes( eTextBytes.length ) );
630                buffer.put( eTextBytes );
631            }
632
633            // edata tag and value, if any
634            if ( eData != null )
635            {
636                buffer.put( ( byte ) KerberosConstants.KRB_ERROR_EDATA_TAG );
637                buffer.put( TLV.getBytes( eDataLength ) );
638                BerValue.encode( buffer, eData );
639            }
640        }
641        catch ( BufferOverflowException boe )
642        {
643            log.error( I18n.err( I18n.ERR_734_CANNOT_ENCODE_KRBERROR, 1 + TLV.getNbBytes( krbErrorLength )
644                + krbErrorLength, buffer.capacity() ) );
645            throw new EncoderException( I18n.err( I18n.ERR_138 ), boe );
646        }
647
648        if ( IS_DEBUG )
649        {
650            log.debug( "KrbError encoding : {}", Strings.dumpBytes( buffer.array() ) );
651            log.debug( "KrbError initial value : {}", this );
652        }
653
654        return buffer;
655    }
656
657
658    /**
659     * @see Object#toString()
660     */
661    public String toString()
662    {
663        StringBuilder sb = new StringBuilder();
664
665        sb.append( "\nKRB-ERROR : {\n" );
666        sb.append( "    pvno: " ).append( getProtocolVersionNumber() ).append( '\n' );
667        sb.append( "    msgType: " ).append( getMessageType() ).append( '\n' );
668
669        if ( cTime != null )
670        {
671            sb.append( "    cTime: " ).append( cTime ).append( '\n' );
672        }
673
674        if ( cusec != null )
675        {
676            sb.append( "    cusec: " ).append( cusec ).append( '\n' );
677        }
678
679        sb.append( "    sTime: " ).append( sTime ).append( '\n' );
680        sb.append( "    susec: " ).append( susec ).append( '\n' );
681        sb.append( "    errorCode: " ).append( errorCode ).append( '\n' );
682
683        if ( cRealm != null )
684        {
685            sb.append( "    cRealm: " ).append( cRealm ).append( '\n' );
686        }
687
688        if ( cName != null )
689        {
690            sb.append( "    cName: " ).append( cName ).append( '\n' );
691        }
692
693        sb.append( "    realm: " ).append( realm ).append( '\n' );
694
695        sb.append( "    sName: " ).append( sName ).append( '\n' );
696
697        if ( eText != null )
698        {
699            sb.append( "    eText: " ).append( eText ).append( '\n' );
700        }
701
702        if ( eData != null )
703        {
704            sb.append( "    eData: " ).append( Strings.dumpBytes( eData ) ).append( '\n' );
705        }
706
707        sb.append( "}\n" );
708
709        return sb.toString();
710    }
711}