View Javadoc
1   /*
2    * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  /*
27   *
28   *  (C) Copyright IBM Corp. 1999 All Rights Reserved.
29   *  Copyright 1997 The Open Group Research Institute.  All rights reserved.
30   */
31  
32  package sun.security.krb5.internal;
33  
34  import java.io.ObjectOutputStream;
35  import sun.security.krb5.PrincipalName;
36  import sun.security.krb5.Checksum;
37  import sun.security.krb5.Asn1Exception;
38  import sun.security.krb5.Realm;
39  import sun.security.krb5.RealmException;
40  import sun.security.util.*;
41  import java.io.IOException;
42  import java.io.ObjectInputStream;
43  import java.math.BigInteger;
44  import java.util.ArrayList;
45  import java.util.Arrays;
46  import java.util.List;
47  import sun.security.krb5.internal.util.KerberosString;
48  /**
49   * Implements the ASN.1 KRBError type.
50   *
51   * <xmp>
52   * KRB-ERROR       ::= [APPLICATION 30] SEQUENCE {
53   *         pvno            [0] INTEGER (5),
54   *         msg-type        [1] INTEGER (30),
55   *         ctime           [2] KerberosTime OPTIONAL,
56   *         cusec           [3] Microseconds OPTIONAL,
57   *         stime           [4] KerberosTime,
58   *         susec           [5] Microseconds,
59   *         error-code      [6] Int32,
60   *         crealm          [7] Realm OPTIONAL,
61   *         cname           [8] PrincipalName OPTIONAL,
62   *         realm           [9] Realm -- service realm --,
63   *         sname           [10] PrincipalName -- service name --,
64   *         e-text          [11] KerberosString OPTIONAL,
65   *         e-data          [12] OCTET STRING OPTIONAL
66   * }
67   *
68   * METHOD-DATA     ::= SEQUENCE OF PA-DATA
69   *
70   * TYPED-DATA      ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE {
71   *         data-type       [0] Int32,
72   *         data-value      [1] OCTET STRING OPTIONAL
73   * }
74   * </xmp>
75   *
76   * <p>
77   * This definition reflects the Network Working Group RFC 4120
78   * specification available at
79   * <a href="http://www.ietf.org/rfc/rfc4120.txt">
80   * http://www.ietf.org/rfc/rfc4120.txt</a>.
81   */
82  
83  public class KRBError implements java.io.Serializable {
84      static final long serialVersionUID = 3643809337475284503L;
85  
86      private int pvno;
87      private int msgType;
88      private KerberosTime cTime; //optional
89      private Integer cuSec; //optional
90      private KerberosTime sTime;
91      private Integer suSec;
92      private int errorCode;
93      private PrincipalName cname; //optional
94      private PrincipalName sname;
95      private String eText; //optional
96      private byte[] eData; //optional
97      private Checksum eCksum; //optional
98  
99      private PAData[] pa;    // PA-DATA in eData
100 
101     private static boolean DEBUG = Krb5.DEBUG;
102 
103     private void readObject(ObjectInputStream is)
104             throws IOException, ClassNotFoundException {
105         try {
106             init(new DerValue((byte[])is.readObject()));
107             parseEData(eData);
108         } catch (Exception e) {
109             throw new IOException(e);
110         }
111     }
112 
113     private void writeObject(ObjectOutputStream os)
114             throws IOException {
115         try {
116             os.writeObject(asn1Encode());
117         } catch (Exception e) {
118             throw new IOException(e);
119         }
120     }
121 
122     public KRBError(
123                     APOptions new_apOptions,
124                     KerberosTime new_cTime,
125                     Integer new_cuSec,
126                     KerberosTime new_sTime,
127                     Integer new_suSec,
128                     int new_errorCode,
129                     PrincipalName new_cname,
130                     PrincipalName new_sname,
131                     String new_eText,
132                     byte[] new_eData
133                         ) throws IOException, Asn1Exception {
134         pvno = Krb5.PVNO;
135         msgType = Krb5.KRB_ERROR;
136         cTime = new_cTime;
137         cuSec = new_cuSec;
138         sTime = new_sTime;
139         suSec = new_suSec;
140         errorCode = new_errorCode;
141         cname = new_cname;
142         sname = new_sname;
143         eText = new_eText;
144         eData = new_eData;
145 
146         parseEData(eData);
147     }
148 
149     public KRBError(
150                     APOptions new_apOptions,
151                     KerberosTime new_cTime,
152                     Integer new_cuSec,
153                     KerberosTime new_sTime,
154                     Integer new_suSec,
155                     int new_errorCode,
156                     PrincipalName new_cname,
157                     PrincipalName new_sname,
158                     String new_eText,
159                     byte[] new_eData,
160                     Checksum new_eCksum
161                         ) throws IOException, Asn1Exception {
162         pvno = Krb5.PVNO;
163         msgType = Krb5.KRB_ERROR;
164         cTime = new_cTime;
165         cuSec = new_cuSec;
166         sTime = new_sTime;
167         suSec = new_suSec;
168         errorCode = new_errorCode;
169         cname = new_cname;
170         sname = new_sname;
171         eText = new_eText;
172         eData = new_eData;
173         eCksum = new_eCksum;
174 
175         parseEData(eData);
176     }
177 
178     public KRBError(byte[] data) throws Asn1Exception,
179             RealmException, KrbApErrException, IOException {
180         init(new DerValue(data));
181         parseEData(eData);
182     }
183 
184     public KRBError(DerValue encoding) throws Asn1Exception,
185             RealmException, KrbApErrException, IOException {
186         init(encoding);
187         showDebug();
188         parseEData(eData);
189     }
190 
191     /*
192      * Attention:
193      *
194      * According to RFC 4120, e-data field in a KRB-ERROR message is
195      * a METHOD-DATA when errorCode is KDC_ERR_PREAUTH_REQUIRED,
196      * and application-specific otherwise (The RFC suggests using
197      * TYPED-DATA).
198      *
199      * Hence, the ideal procedure to parse e-data should look like:
200      *
201      * if (errorCode is KDC_ERR_PREAUTH_REQUIRED) {
202      *    parse as METHOD-DATA
203      * } else {
204      *    try parsing as TYPED-DATA
205      * }
206      *
207      * Unfortunately, we know that some implementations also use the
208      * METHOD-DATA format for errorcode KDC_ERR_PREAUTH_FAILED, and
209      * do not use the TYPED-DATA for other errorcodes (say,
210      * KDC_ERR_CLIENT_REVOKED).
211      */
212 
213     // parse the edata field
214     private void parseEData(byte[] data) throws IOException {
215         if (data == null) {
216             return;
217         }
218 
219         // We need to parse eData as METHOD-DATA for both errorcodes.
220         if (errorCode == Krb5.KDC_ERR_PREAUTH_REQUIRED
221                 || errorCode == Krb5.KDC_ERR_PREAUTH_FAILED) {
222             try {
223                 // RFC 4120 does not guarantee that eData is METHOD-DATA when
224                 // errorCode is KDC_ERR_PREAUTH_FAILED. Therefore, the parse
225                 // may fail.
226                 parsePAData(data);
227             } catch (Exception e) {
228                 if (DEBUG) {
229                     System.out.println("Unable to parse eData field of KRB-ERROR:\n" +
230                             new sun.misc.HexDumpEncoder().encodeBuffer(data));
231                 }
232                 IOException ioe = new IOException(
233                         "Unable to parse eData field of KRB-ERROR");
234                 ioe.initCause(e);
235                 throw ioe;
236             }
237         } else {
238             if (DEBUG) {
239                 System.out.println("Unknown eData field of KRB-ERROR:\n" +
240                         new sun.misc.HexDumpEncoder().encodeBuffer(data));
241             }
242         }
243     }
244 
245     /**
246      * Try parsing the data as a sequence of PA-DATA.
247      * @param data the data block
248      */
249     private void parsePAData(byte[] data)
250             throws IOException, Asn1Exception {
251         DerValue derPA = new DerValue(data);
252         List<PAData> paList = new ArrayList<>();
253         while (derPA.data.available() > 0) {
254             // read the PA-DATA
255             DerValue tmp = derPA.data.getDerValue();
256             PAData pa_data = new PAData(tmp);
257             paList.add(pa_data);
258             if (DEBUG) {
259                 System.out.println(pa_data);
260             }
261         }
262         pa = paList.toArray(new PAData[paList.size()]);
263     }
264 
265     public final KerberosTime getServerTime() {
266         return sTime;
267     }
268 
269     public final KerberosTime getClientTime() {
270         return cTime;
271     }
272 
273     public final Integer getServerMicroSeconds() {
274         return suSec;
275     }
276 
277     public final Integer getClientMicroSeconds() {
278         return cuSec;
279     }
280 
281     public final int getErrorCode() {
282         return errorCode;
283     }
284 
285     // access pre-auth info
286     public final PAData[] getPA() {
287         return pa;
288     }
289 
290     public final String getErrorString() {
291         return eText;
292     }
293 
294     /**
295      * Initializes a KRBError object.
296      * @param encoding a DER-encoded data.
297      * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data.
298      * @exception IOException if an I/O error occurs while reading encoded data.
299      * @exception KrbApErrException if the value read from the DER-encoded data
300      *  stream does not match the pre-defined value.
301      * @exception RealmException if an error occurs while parsing a Realm object.
302      */
303     private void init(DerValue encoding) throws Asn1Exception,
304             RealmException, KrbApErrException, IOException {
305         DerValue der, subDer;
306         if (((encoding.getTag() & (byte)0x1F) != (byte)0x1E)
307                 || (encoding.isApplication() != true)
308                 || (encoding.isConstructed() != true)) {
309             throw new Asn1Exception(Krb5.ASN1_BAD_ID);
310         }
311         der = encoding.getData().getDerValue();
312         if (der.getTag() != DerValue.tag_Sequence) {
313             throw new Asn1Exception(Krb5.ASN1_BAD_ID);
314         }
315         subDer = der.getData().getDerValue();
316         if ((subDer.getTag() & (byte)0x1F) == (byte)0x00) {
317 
318             pvno = subDer.getData().getBigInteger().intValue();
319             if (pvno != Krb5.PVNO)
320                 throw new KrbApErrException(Krb5.KRB_AP_ERR_BADVERSION);
321         } else {
322             throw new Asn1Exception(Krb5.ASN1_BAD_ID);
323         }
324 
325         subDer = der.getData().getDerValue();
326         if ((subDer.getTag() & (byte)0x1F) == (byte)0x01) {
327             msgType = subDer.getData().getBigInteger().intValue();
328             if (msgType != Krb5.KRB_ERROR) {
329                 throw new KrbApErrException(Krb5.KRB_AP_ERR_MSG_TYPE);
330             }
331         } else {
332             throw new Asn1Exception(Krb5.ASN1_BAD_ID);
333         }
334 
335         cTime = KerberosTime.parse(der.getData(), (byte)0x02, true);
336         if ((der.getData().peekByte() & 0x1F) == 0x03) {
337             subDer = der.getData().getDerValue();
338             cuSec = new Integer(subDer.getData().getBigInteger().intValue());
339         }
340         else cuSec = null;
341         sTime = KerberosTime.parse(der.getData(), (byte)0x04, false);
342         subDer = der.getData().getDerValue();
343         if ((subDer.getTag() & (byte)0x1F) == (byte)0x05) {
344             suSec = new Integer (subDer.getData().getBigInteger().intValue());
345         }
346         else  throw new Asn1Exception(Krb5.ASN1_BAD_ID);
347         subDer = der.getData().getDerValue();
348         if ((subDer.getTag() & (byte)0x1F) == (byte)0x06) {
349             errorCode = subDer.getData().getBigInteger().intValue();
350         }
351         else  throw new Asn1Exception(Krb5.ASN1_BAD_ID);
352         Realm crealm = Realm.parse(der.getData(), (byte)0x07, true);
353         cname = PrincipalName.parse(der.getData(), (byte)0x08, true, crealm);
354         Realm realm = Realm.parse(der.getData(), (byte)0x09, false);
355         sname = PrincipalName.parse(der.getData(), (byte)0x0A, false, realm);
356         eText = null;
357         eData = null;
358         eCksum = null;
359         if (der.getData().available() >0) {
360             if ((der.getData().peekByte() & 0x1F) == 0x0B) {
361                 subDer = der.getData().getDerValue();
362                 eText = new KerberosString(subDer.getData().getDerValue())
363                         .toString();
364             }
365         }
366         if (der.getData().available() >0) {
367             if ((der.getData().peekByte() & 0x1F) == 0x0C) {
368                 subDer = der.getData().getDerValue();
369                 eData = subDer.getData().getOctetString();
370             }
371         }
372         if (der.getData().available() >0) {
373             eCksum = Checksum.parse(der.getData(), (byte)0x0D, true);
374         }
375         if (der.getData().available() >0)
376             throw new Asn1Exception(Krb5.ASN1_BAD_ID);
377     }
378 
379     /**
380      * For debug use only
381      */
382     private void showDebug() {
383         if (DEBUG) {
384             System.out.println(">>>KRBError:");
385             if (cTime != null)
386                 System.out.println("\t cTime is " + cTime.toDate().toString() + " " + cTime.toDate().getTime());
387             if (cuSec != null) {
388                 System.out.println("\t cuSec is " + cuSec.intValue());
389             }
390 
391             System.out.println("\t sTime is " + sTime.toDate().toString
392                                () + " " + sTime.toDate().getTime());
393             System.out.println("\t suSec is " + suSec);
394             System.out.println("\t error code is " + errorCode);
395             System.out.println("\t error Message is " + Krb5.getErrorMessage(errorCode));
396             if (cname != null) {
397                 System.out.println("\t cname is " + cname.toString());
398             }
399             if (sname != null) {
400                 System.out.println("\t sname is " + sname.toString());
401             }
402             if (eData != null) {
403                 System.out.println("\t eData provided.");
404             }
405             if (eCksum != null) {
406                 System.out.println("\t checksum provided.");
407             }
408             System.out.println("\t msgType is " + msgType);
409         }
410     }
411 
412     /**
413      * Encodes an KRBError object.
414      * @return the byte array of encoded KRBError object.
415      * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data.
416      * @exception IOException if an I/O error occurs while reading encoded data.
417      */
418     public byte[] asn1Encode() throws Asn1Exception, IOException {
419         DerOutputStream temp = new DerOutputStream();
420         DerOutputStream bytes = new DerOutputStream();
421 
422         temp.putInteger(BigInteger.valueOf(pvno));
423         bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x00), temp);
424         temp = new DerOutputStream();
425         temp.putInteger(BigInteger.valueOf(msgType));
426         bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x01), temp);
427 
428         if (cTime != null) {
429             bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x02), cTime.asn1Encode());
430         }
431         if (cuSec != null) {
432             temp = new DerOutputStream();
433             temp.putInteger(BigInteger.valueOf(cuSec.intValue()));
434             bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x03), temp);
435         }
436 
437         bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x04), sTime.asn1Encode());
438         temp = new DerOutputStream();
439         temp.putInteger(BigInteger.valueOf(suSec.intValue()));
440         bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x05), temp);
441         temp = new DerOutputStream();
442         temp.putInteger(BigInteger.valueOf(errorCode));
443         bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x06), temp);
444 
445         if (cname != null) {
446             bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x07), cname.getRealm().asn1Encode());
447             bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x08), cname.asn1Encode());
448         }
449 
450         bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x09), sname.getRealm().asn1Encode());
451         bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x0A), sname.asn1Encode());
452 
453         if (eText != null) {
454             temp = new DerOutputStream();
455             temp.putDerValue(new KerberosString(eText).toDerValue());
456             bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x0B), temp);
457         }
458         if (eData != null) {
459             temp = new DerOutputStream();
460             temp.putOctetString(eData);
461             bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x0C), temp);
462         }
463         if (eCksum != null) {
464             bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x0D), eCksum.asn1Encode());
465         }
466 
467         temp = new DerOutputStream();
468         temp.write(DerValue.tag_Sequence, bytes);
469         bytes = new DerOutputStream();
470         bytes.write(DerValue.createTag(DerValue.TAG_APPLICATION, true, (byte)0x1E), temp);
471         return bytes.toByteArray();
472     }
473 
474     @Override public boolean equals(Object obj) {
475         if (this == obj) {
476             return true;
477         }
478 
479         if (!(obj instanceof KRBError)) {
480             return false;
481         }
482 
483         KRBError other = (KRBError)obj;
484         return  pvno == other.pvno &&
485                 msgType == other.msgType &&
486                 isEqual(cTime, other.cTime) &&
487                 isEqual(cuSec, other.cuSec) &&
488                 isEqual(sTime, other.sTime) &&
489                 isEqual(suSec, other.suSec) &&
490                 errorCode == other.errorCode &&
491                 isEqual(cname, other.cname) &&
492                 isEqual(sname, other.sname) &&
493                 isEqual(eText, other.eText) &&
494                 java.util.Arrays.equals(eData, other.eData) &&
495                 isEqual(eCksum, other.eCksum);
496     }
497 
498     private static boolean isEqual(Object a, Object b) {
499         return (a == null)?(b == null):(a.equals(b));
500     }
501 
502     @Override public int hashCode() {
503         int result = 17;
504         result = 37 * result + pvno;
505         result = 37 * result + msgType;
506         if (cTime != null) result = 37 * result + cTime.hashCode();
507         if (cuSec != null) result = 37 * result + cuSec.hashCode();
508         if (sTime != null) result = 37 * result + sTime.hashCode();
509         if (suSec != null) result = 37 * result + suSec.hashCode();
510         result = 37 * result + errorCode;
511         if (cname != null) result = 37 * result + cname.hashCode();
512         if (sname != null) result = 37 * result + sname.hashCode();
513         if (eText != null) result = 37 * result + eText.hashCode();
514         result = 37 * result + Arrays.hashCode(eData);
515         if (eCksum != null) result = 37 * result + eCksum.hashCode();
516         return result;
517     }
518 }