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