1 /*
   2  * Copyright (c) 2000, 2019, 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  * <pre>{@code
  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  * }</pre>
  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     // The instance fields not statically typed as Serializable are
  89     // asn1 encoded and written by the writeObject method.
  90     @SuppressWarnings("serial") // Not statically typed as Serializable
  91     private KerberosTime cTime; //optional
  92     private Integer cuSec; //optional
  93     @SuppressWarnings("serial") // Not statically typed as Serializable
  94     private KerberosTime sTime;
  95     private Integer suSec;
  96     private int errorCode;
  97     @SuppressWarnings("serial") // Not statically typed as Serializable
  98     private Realm crealm; //optional
  99     @SuppressWarnings("serial") // Not statically typed as Serializable
 100     private PrincipalName cname; //optional
 101     @SuppressWarnings("serial") // Not statically typed as Serializable
 102     private PrincipalName sname;
 103     private String eText; //optional
 104     private byte[] eData; //optional
 105     @SuppressWarnings("serial") // Not statically typed as Serializable
 106     private Checksum eCksum; //optional
 107 
 108     @SuppressWarnings("serial") // Not statically typed as Serializable
 109     private PAData[] pa;    // PA-DATA in eData
 110 
 111     private static boolean DEBUG = Krb5.DEBUG;
 112 
 113     private void readObject(ObjectInputStream is)
 114             throws IOException, ClassNotFoundException {
 115         try {
 116             init(new DerValue((byte[])is.readObject()));
 117             parseEData(eData);
 118         } catch (Exception e) {
 119             throw new IOException(e);
 120         }
 121     }
 122 
 123     private void writeObject(ObjectOutputStream os)
 124             throws IOException {
 125         try {
 126             os.writeObject(asn1Encode());
 127         } catch (Exception e) {
 128             throw new IOException(e);
 129         }
 130     }
 131 
 132     public KRBError(
 133                     APOptions new_apOptions,
 134                     KerberosTime new_cTime,
 135                     Integer new_cuSec,
 136                     KerberosTime new_sTime,
 137                     Integer new_suSec,
 138                     int new_errorCode,
 139                     PrincipalName new_cname,
 140                     PrincipalName new_sname,
 141                     String new_eText,
 142                     byte[] new_eData
 143                         ) throws IOException, Asn1Exception {
 144         pvno = Krb5.PVNO;
 145         msgType = Krb5.KRB_ERROR;
 146         cTime = new_cTime;
 147         cuSec = new_cuSec;
 148         sTime = new_sTime;
 149         suSec = new_suSec;
 150         errorCode = new_errorCode;
 151         crealm = new_cname != null ? new_cname.getRealm() : null;
 152         cname = new_cname;
 153         sname = new_sname;
 154         eText = new_eText;
 155         eData = new_eData;
 156 
 157         parseEData(eData);
 158     }
 159 
 160     public KRBError(
 161                     APOptions new_apOptions,
 162                     KerberosTime new_cTime,
 163                     Integer new_cuSec,
 164                     KerberosTime new_sTime,
 165                     Integer new_suSec,
 166                     int new_errorCode,
 167                     PrincipalName new_cname,
 168                     PrincipalName new_sname,
 169                     String new_eText,
 170                     byte[] new_eData,
 171                     Checksum new_eCksum
 172                         ) throws IOException, Asn1Exception {
 173         pvno = Krb5.PVNO;
 174         msgType = Krb5.KRB_ERROR;
 175         cTime = new_cTime;
 176         cuSec = new_cuSec;
 177         sTime = new_sTime;
 178         suSec = new_suSec;
 179         errorCode = new_errorCode;
 180         crealm = new_cname != null ? new_cname.getRealm() : null;
 181         cname = new_cname;
 182         sname = new_sname;
 183         eText = new_eText;
 184         eData = new_eData;
 185         eCksum = new_eCksum;
 186 
 187         parseEData(eData);
 188     }
 189 
 190     public KRBError(byte[] data) throws Asn1Exception,
 191             RealmException, KrbApErrException, IOException {
 192         init(new DerValue(data));
 193         parseEData(eData);
 194     }
 195 
 196     public KRBError(DerValue encoding) throws Asn1Exception,
 197             RealmException, KrbApErrException, IOException {
 198         init(encoding);
 199         showDebug();
 200         parseEData(eData);
 201     }
 202 
 203     /*
 204      * Attention:
 205      *
 206      * According to RFC 4120, e-data field in a KRB-ERROR message is
 207      * a METHOD-DATA when errorCode is KDC_ERR_PREAUTH_REQUIRED,
 208      * and application-specific otherwise (The RFC suggests using
 209      * TYPED-DATA).
 210      *
 211      * Hence, the ideal procedure to parse e-data should look like:
 212      *
 213      * if (errorCode is KDC_ERR_PREAUTH_REQUIRED) {
 214      *    parse as METHOD-DATA
 215      * } else {
 216      *    try parsing as TYPED-DATA
 217      * }
 218      *
 219      * Unfortunately, we know that some implementations also use the
 220      * METHOD-DATA format for errorcode KDC_ERR_PREAUTH_FAILED, and
 221      * do not use the TYPED-DATA for other errorcodes (say,
 222      * KDC_ERR_CLIENT_REVOKED).
 223      */
 224 
 225     // parse the edata field
 226     private void parseEData(byte[] data) throws IOException {
 227         if (data == null) {
 228             return;
 229         }
 230 
 231         // We need to parse eData as METHOD-DATA for both errorcodes.
 232         if (errorCode == Krb5.KDC_ERR_PREAUTH_REQUIRED
 233                 || errorCode == Krb5.KDC_ERR_PREAUTH_FAILED) {
 234             try {
 235                 // RFC 4120 does not guarantee that eData is METHOD-DATA when
 236                 // errorCode is KDC_ERR_PREAUTH_FAILED. Therefore, the parse
 237                 // may fail.
 238                 parsePAData(data);
 239             } catch (Exception e) {
 240                 if (DEBUG) {
 241                     System.out.println("Unable to parse eData field of KRB-ERROR:\n" +
 242                             new sun.security.util.HexDumpEncoder().encodeBuffer(data));
 243                 }
 244                 IOException ioe = new IOException(
 245                         "Unable to parse eData field of KRB-ERROR");
 246                 ioe.initCause(e);
 247                 throw ioe;
 248             }
 249         } else {
 250             if (DEBUG) {
 251                 System.out.println("Unknown eData field of KRB-ERROR:\n" +
 252                         new sun.security.util.HexDumpEncoder().encodeBuffer(data));
 253             }
 254         }
 255     }
 256 
 257     /**
 258      * Try parsing the data as a sequence of PA-DATA.
 259      * @param data the data block
 260      */
 261     private void parsePAData(byte[] data)
 262             throws IOException, Asn1Exception {
 263         DerValue derPA = new DerValue(data);
 264         List<PAData> paList = new ArrayList<>();
 265         while (derPA.data.available() > 0) {
 266             // read the PA-DATA
 267             DerValue tmp = derPA.data.getDerValue();
 268             PAData pa_data = new PAData(tmp);
 269             paList.add(pa_data);
 270             if (DEBUG) {
 271                 System.out.println(pa_data);
 272             }
 273         }
 274         pa = paList.toArray(new PAData[paList.size()]);
 275     }
 276 
 277     public final Realm getClientRealm() {
 278         return crealm;
 279     }
 280 
 281     public final KerberosTime getServerTime() {
 282         return sTime;
 283     }
 284 
 285     public final KerberosTime getClientTime() {
 286         return cTime;
 287     }
 288 
 289     public final Integer getServerMicroSeconds() {
 290         return suSec;
 291     }
 292 
 293     public final Integer getClientMicroSeconds() {
 294         return cuSec;
 295     }
 296 
 297     public final int getErrorCode() {
 298         return errorCode;
 299     }
 300 
 301     // access pre-auth info
 302     public final PAData[] getPA() {
 303         return pa;
 304     }
 305 
 306     public final String getErrorString() {
 307         return eText;
 308     }
 309 
 310     /**
 311      * Initializes a KRBError object.
 312      * @param encoding a DER-encoded data.
 313      * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data.
 314      * @exception IOException if an I/O error occurs while reading encoded data.
 315      * @exception KrbApErrException if the value read from the DER-encoded data
 316      *  stream does not match the pre-defined value.
 317      * @exception RealmException if an error occurs while parsing a Realm object.
 318      */
 319     private void init(DerValue encoding) throws Asn1Exception,
 320             RealmException, KrbApErrException, IOException {
 321         DerValue der, subDer;
 322         if (((encoding.getTag() & (byte)0x1F) != (byte)0x1E)
 323                 || (encoding.isApplication() != true)
 324                 || (encoding.isConstructed() != true)) {
 325             throw new Asn1Exception(Krb5.ASN1_BAD_ID);
 326         }
 327         der = encoding.getData().getDerValue();
 328         if (der.getTag() != DerValue.tag_Sequence) {
 329             throw new Asn1Exception(Krb5.ASN1_BAD_ID);
 330         }
 331         subDer = der.getData().getDerValue();
 332         if ((subDer.getTag() & (byte)0x1F) == (byte)0x00) {
 333 
 334             pvno = subDer.getData().getBigInteger().intValue();
 335             if (pvno != Krb5.PVNO)
 336                 throw new KrbApErrException(Krb5.KRB_AP_ERR_BADVERSION);
 337         } else {
 338             throw new Asn1Exception(Krb5.ASN1_BAD_ID);
 339         }
 340 
 341         subDer = der.getData().getDerValue();
 342         if ((subDer.getTag() & (byte)0x1F) == (byte)0x01) {
 343             msgType = subDer.getData().getBigInteger().intValue();
 344             if (msgType != Krb5.KRB_ERROR) {
 345                 throw new KrbApErrException(Krb5.KRB_AP_ERR_MSG_TYPE);
 346             }
 347         } else {
 348             throw new Asn1Exception(Krb5.ASN1_BAD_ID);
 349         }
 350 
 351         cTime = KerberosTime.parse(der.getData(), (byte)0x02, true);
 352         if ((der.getData().peekByte() & 0x1F) == 0x03) {
 353             subDer = der.getData().getDerValue();
 354             cuSec = subDer.getData().getBigInteger().intValue();
 355         }
 356         else cuSec = null;
 357         sTime = KerberosTime.parse(der.getData(), (byte)0x04, false);
 358         subDer = der.getData().getDerValue();
 359         if ((subDer.getTag() & (byte)0x1F) == (byte)0x05) {
 360             suSec = subDer.getData().getBigInteger().intValue();
 361         }
 362         else  throw new Asn1Exception(Krb5.ASN1_BAD_ID);
 363         subDer = der.getData().getDerValue();
 364         if ((subDer.getTag() & (byte)0x1F) == (byte)0x06) {
 365             errorCode = subDer.getData().getBigInteger().intValue();
 366         }
 367         else  throw new Asn1Exception(Krb5.ASN1_BAD_ID);
 368         crealm = Realm.parse(der.getData(), (byte)0x07, true);
 369         cname = PrincipalName.parse(der.getData(), (byte)0x08, true, crealm);
 370         Realm realm = Realm.parse(der.getData(), (byte)0x09, false);
 371         sname = PrincipalName.parse(der.getData(), (byte)0x0A, false, realm);
 372         eText = null;
 373         eData = null;
 374         eCksum = null;
 375         if (der.getData().available() >0) {
 376             if ((der.getData().peekByte() & 0x1F) == 0x0B) {
 377                 subDer = der.getData().getDerValue();
 378                 eText = new KerberosString(subDer.getData().getDerValue())
 379                         .toString();
 380             }
 381         }
 382         if (der.getData().available() >0) {
 383             if ((der.getData().peekByte() & 0x1F) == 0x0C) {
 384                 subDer = der.getData().getDerValue();
 385                 eData = subDer.getData().getOctetString();
 386             }
 387         }
 388         if (der.getData().available() >0) {
 389             eCksum = Checksum.parse(der.getData(), (byte)0x0D, true);
 390         }
 391         if (der.getData().available() >0)
 392             throw new Asn1Exception(Krb5.ASN1_BAD_ID);
 393     }
 394 
 395     /**
 396      * For debug use only
 397      */
 398     private void showDebug() {
 399         if (DEBUG) {
 400             System.out.println(">>>KRBError:");
 401             if (cTime != null)
 402                 System.out.println("\t cTime is " + cTime.toDate().toString() + " " + cTime.toDate().getTime());
 403             if (cuSec != null) {
 404                 System.out.println("\t cuSec is " + cuSec.intValue());
 405             }
 406 
 407             System.out.println("\t sTime is " + sTime.toDate().toString
 408                                () + " " + sTime.toDate().getTime());
 409             System.out.println("\t suSec is " + suSec);
 410             System.out.println("\t error code is " + errorCode);
 411             System.out.println("\t error Message is " + Krb5.getErrorMessage(errorCode));
 412             if (crealm != null) {
 413                 System.out.println("\t crealm is " + crealm.toString());
 414             }
 415             if (cname != null) {
 416                 System.out.println("\t cname is " + cname.toString());
 417             }
 418             if (sname != null) {
 419                 System.out.println("\t sname is " + sname.toString());
 420             }
 421             if (eData != null) {
 422                 System.out.println("\t eData provided.");
 423             }
 424             if (eCksum != null) {
 425                 System.out.println("\t checksum provided.");
 426             }
 427             System.out.println("\t msgType is " + msgType);
 428         }
 429     }
 430 
 431     /**
 432      * Encodes an KRBError object.
 433      * @return the byte array of encoded KRBError object.
 434      * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data.
 435      * @exception IOException if an I/O error occurs while reading encoded data.
 436      */
 437     public byte[] asn1Encode() throws Asn1Exception, IOException {
 438         DerOutputStream temp = new DerOutputStream();
 439         DerOutputStream bytes = new DerOutputStream();
 440 
 441         temp.putInteger(BigInteger.valueOf(pvno));
 442         bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x00), temp);
 443         temp = new DerOutputStream();
 444         temp.putInteger(BigInteger.valueOf(msgType));
 445         bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x01), temp);
 446 
 447         if (cTime != null) {
 448             bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x02), cTime.asn1Encode());
 449         }
 450         if (cuSec != null) {
 451             temp = new DerOutputStream();
 452             temp.putInteger(BigInteger.valueOf(cuSec.intValue()));
 453             bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x03), temp);
 454         }
 455 
 456         bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x04), sTime.asn1Encode());
 457         temp = new DerOutputStream();
 458         temp.putInteger(BigInteger.valueOf(suSec.intValue()));
 459         bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x05), temp);
 460         temp = new DerOutputStream();
 461         temp.putInteger(BigInteger.valueOf(errorCode));
 462         bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x06), temp);
 463 
 464         if (crealm != null) {
 465             bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x07), crealm.asn1Encode());
 466         }
 467         if (cname != null) {
 468             bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x08), cname.asn1Encode());
 469         }
 470 
 471         bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x09), sname.getRealm().asn1Encode());
 472         bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x0A), sname.asn1Encode());
 473 
 474         if (eText != null) {
 475             temp = new DerOutputStream();
 476             temp.putDerValue(new KerberosString(eText).toDerValue());
 477             bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x0B), temp);
 478         }
 479         if (eData != null) {
 480             temp = new DerOutputStream();
 481             temp.putOctetString(eData);
 482             bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x0C), temp);
 483         }
 484         if (eCksum != null) {
 485             bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x0D), eCksum.asn1Encode());
 486         }
 487 
 488         temp = new DerOutputStream();
 489         temp.write(DerValue.tag_Sequence, bytes);
 490         bytes = new DerOutputStream();
 491         bytes.write(DerValue.createTag(DerValue.TAG_APPLICATION, true, (byte)0x1E), temp);
 492         return bytes.toByteArray();
 493     }
 494 
 495     @Override public boolean equals(Object obj) {
 496         if (this == obj) {
 497             return true;
 498         }
 499 
 500         if (!(obj instanceof KRBError)) {
 501             return false;
 502         }
 503 
 504         KRBError other = (KRBError)obj;
 505         return  pvno == other.pvno &&
 506                 msgType == other.msgType &&
 507                 isEqual(cTime, other.cTime) &&
 508                 isEqual(cuSec, other.cuSec) &&
 509                 isEqual(sTime, other.sTime) &&
 510                 isEqual(suSec, other.suSec) &&
 511                 errorCode == other.errorCode &&
 512                 isEqual(crealm, other.crealm) &&
 513                 isEqual(cname, other.cname) &&
 514                 isEqual(sname, other.sname) &&
 515                 isEqual(eText, other.eText) &&
 516                 java.util.Arrays.equals(eData, other.eData) &&
 517                 isEqual(eCksum, other.eCksum);
 518     }
 519 
 520     private static boolean isEqual(Object a, Object b) {
 521         return (a == null)?(b == null):(a.equals(b));
 522     }
 523 
 524     @Override public int hashCode() {
 525         int result = 17;
 526         result = 37 * result + pvno;
 527         result = 37 * result + msgType;
 528         if (cTime != null) result = 37 * result + cTime.hashCode();
 529         if (cuSec != null) result = 37 * result + cuSec.hashCode();
 530         if (sTime != null) result = 37 * result + sTime.hashCode();
 531         if (suSec != null) result = 37 * result + suSec.hashCode();
 532         result = 37 * result + errorCode;
 533         if (crealm != null) result = 37 * result + crealm.hashCode();
 534         if (cname != null) result = 37 * result + cname.hashCode();
 535         if (sname != null) result = 37 * result + sname.hashCode();
 536         if (eText != null) result = 37 * result + eText.hashCode();
 537         result = 37 * result + Arrays.hashCode(eData);
 538         if (eCksum != null) result = 37 * result + eCksum.hashCode();
 539         return result;
 540     }
 541 }