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 = 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 = 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 }