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 }