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