blob: febc959cb805fd27745f4052064a470514108e9f [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Portions Copyright 2000-2006 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any 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
32package sun.security.krb5.internal;
33
34import java.io.ObjectOutputStream;
35import sun.security.krb5.PrincipalName;
36import sun.security.krb5.Checksum;
37import sun.security.krb5.Asn1Exception;
38import sun.security.krb5.Realm;
39import sun.security.krb5.RealmException;
40import sun.security.util.*;
41import java.io.IOException;
42import java.io.ObjectInputStream;
43import java.math.BigInteger;
44import java.util.Arrays;
45/**
46 * Implements the ASN.1 KRBError type.
47 *
48 * <xmp>
49 * KRB-ERROR ::= [APPLICATION 30] SEQUENCE {
50 * pvno [0] INTEGER (5),
51 * msg-type [1] INTEGER (30),
52 * ctime [2] KerberosTime OPTIONAL,
53 * cusec [3] Microseconds OPTIONAL,
54 * stime [4] KerberosTime,
55 * susec [5] Microseconds,
56 * error-code [6] Int32,
57 * crealm [7] Realm OPTIONAL,
58 * cname [8] PrincipalName OPTIONAL,
59 * realm [9] Realm -- service realm --,
60 * sname [10] PrincipalName -- service name --,
61 * e-text [11] KerberosString OPTIONAL,
62 * e-data [12] OCTET STRING OPTIONAL
63 * }
64 *
65 * METHOD-DATA ::= SEQUENCE OF PA-DATA
66 *
67 * TYPED-DATA ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE {
68 * data-type [0] Int32,
69 * data-value [1] OCTET STRING OPTIONAL
70 * }
71 * </xmp>
72 *
73 * <p>
74 * This definition reflects the Network Working Group RFC 4120
75 * specification available at
76 * <a href="http://www.ietf.org/rfc/rfc4120.txt">
77 * http://www.ietf.org/rfc/rfc4120.txt</a>.
78 */
79
80public class KRBError implements java.io.Serializable {
81 static final long serialVersionUID = 3643809337475284503L;
82
83 private int pvno;
84 private int msgType;
85 private KerberosTime cTime; //optional
86 private Integer cuSec; //optional
87 private KerberosTime sTime;
88 private Integer suSec;
89 private int errorCode;
90 private Realm crealm; //optional
91 private PrincipalName cname; //optional
92 private Realm realm;
93 private PrincipalName sname;
94 private String eText; //optional
95 private byte[] eData; //optional
96 private Checksum eCksum; //optional
97
98 // pre-auth info
99 private int etype = 0;
100 private byte[] salt = null;
101 private byte[] s2kparams = null;
102
103 private static boolean DEBUG = Krb5.DEBUG;
104
105 private void readObject(ObjectInputStream is)
106 throws IOException, ClassNotFoundException {
107 try {
108 init(new DerValue((byte[])is.readObject()));
109 parseEData(eData);
110 } catch (Exception e) {
111 throw new IOException(e);
112 }
113 }
114
115 private void writeObject(ObjectOutputStream os)
116 throws IOException {
117 try {
118 os.writeObject(asn1Encode());
119 } catch (Exception e) {
120 throw new IOException(e);
121 }
122 }
123
124 public KRBError(
125 APOptions new_apOptions,
126 KerberosTime new_cTime,
127 Integer new_cuSec,
128 KerberosTime new_sTime,
129 Integer new_suSec,
130 int new_errorCode,
131 Realm new_crealm,
132 PrincipalName new_cname,
133 Realm new_realm,
134 PrincipalName new_sname,
135 String new_eText,
136 byte[] new_eData
137 ) throws IOException, Asn1Exception {
138 pvno = Krb5.PVNO;
139 msgType = Krb5.KRB_ERROR;
140 cTime = new_cTime;
141 cuSec = new_cuSec;
142 sTime = new_sTime;
143 suSec = new_suSec;
144 errorCode = new_errorCode;
145 crealm = new_crealm;
146 cname = new_cname;
147 realm = new_realm;
148 sname = new_sname;
149 eText = new_eText;
150 eData = new_eData;
151
152 parseEData(eData);
153 }
154
155 public KRBError(
156 APOptions new_apOptions,
157 KerberosTime new_cTime,
158 Integer new_cuSec,
159 KerberosTime new_sTime,
160 Integer new_suSec,
161 int new_errorCode,
162 Realm new_crealm,
163 PrincipalName new_cname,
164 Realm new_realm,
165 PrincipalName new_sname,
166 String new_eText,
167 byte[] new_eData,
168 Checksum new_eCksum
169 ) throws IOException, Asn1Exception {
170 pvno = Krb5.PVNO;
171 msgType = Krb5.KRB_ERROR;
172 cTime = new_cTime;
173 cuSec = new_cuSec;
174 sTime = new_sTime;
175 suSec = new_suSec;
176 errorCode = new_errorCode;
177 crealm = new_crealm;
178 cname = new_cname;
179 realm = new_realm;
180 sname = new_sname;
181 eText = new_eText;
182 eData = new_eData;
183 eCksum = new_eCksum;
184
185 parseEData(eData);
186 }
187
188 public KRBError(byte[] data) throws Asn1Exception,
189 RealmException, KrbApErrException, IOException {
190 init(new DerValue(data));
191 parseEData(eData);
192 }
193
194 public KRBError(DerValue encoding) throws Asn1Exception,
195 RealmException, KrbApErrException, IOException {
196 init(encoding);
197 showDebug();
198 parseEData(eData);
199 }
200
201 /*
202 * Attention:
203 *
204 * According to RFC 4120, e-data field in a KRB-ERROR message is
205 * a METHOD-DATA when errorCode is KDC_ERR_PREAUTH_REQUIRED,
206 * and application-specific otherwise (The RFC suggests using
207 * TYPED-DATA).
208 *
209 * Hence, the ideal procedure to parse e-data should look like:
210 *
211 * if (errorCode is KDC_ERR_PREAUTH_REQUIRED) {
212 * parse as METHOD-DATA
213 * } else {
214 * try parsing as TYPED-DATA
215 * }
216 *
217 * Unfortunately, we know that some implementations also use the
218 * METHOD-DATA format for errorcode KDC_ERR_PREAUTH_FAILED, and
219 * do not use the TYPED-DATA for other errorcodes (say,
220 * KDC_ERR_CLIENT_REVOKED).
221 */
222
223 // parse the edata field
224 private void parseEData(byte[] data) throws IOException {
225 if (data == null) {
226 return;
227 }
228
229 // We need to parse eData as METHOD-DATA for both errorcodes.
230 if (errorCode == Krb5.KDC_ERR_PREAUTH_REQUIRED
231 || errorCode == Krb5.KDC_ERR_PREAUTH_FAILED) {
232 try {
233 // RFC 4120 does not guarantee that eData is METHOD-DATA when
234 // errorCode is KDC_ERR_PREAUTH_FAILED. Therefore, the parse
235 // may fail.
236 parsePAData(data);
237 } catch (Exception e) {
238 if (DEBUG) {
239 System.out.println("Unable to parse eData field of KRB-ERROR:\n" +
240 new sun.misc.HexDumpEncoder().encodeBuffer(data));
241 }
242 IOException ioe = new IOException(
243 "Unable to parse eData field of KRB-ERROR");
244 ioe.initCause(e);
245 throw ioe;
246 }
247 } else {
248 if (DEBUG) {
249 System.out.println("Unknown eData field of KRB-ERROR:\n" +
250 new sun.misc.HexDumpEncoder().encodeBuffer(data));
251 }
252 }
253 }
254
255 /**
256 * Try parsing the data as a sequence of PA-DATA.
257 * @param data the data block
258 */
259 private void parsePAData(byte[] data)
260 throws IOException, Asn1Exception {
261 DerValue derPA = new DerValue(data);
262 while (derPA.data.available() > 0) {
263 // read the PA-DATA
264 DerValue tmp = derPA.data.getDerValue();
265 PAData pa_data = new PAData(tmp);
266 int pa_type = pa_data.getType();
267 byte[] pa_value = pa_data.getValue();
268 if (DEBUG) {
269 System.out.println(">>>Pre-Authentication Data:");
270 System.out.println("\t PA-DATA type = " + pa_type);
271 }
272
273 switch(pa_type) {
274 case Krb5.PA_ENC_TIMESTAMP:
275 if (DEBUG) {
276 System.out.println("\t PA-ENC-TIMESTAMP");
277 }
278 break;
279 case Krb5.PA_ETYPE_INFO:
280 if (pa_value != null) {
281 DerValue der = new DerValue(pa_value);
282 DerValue value = der.data.getDerValue();
283 ETypeInfo info = new ETypeInfo(value);
284 etype = info.getEType();
285 salt = info.getSalt();
286 if (DEBUG) {
287 System.out.println("\t PA-ETYPE-INFO etype = " + etype);
288 }
289 }
290 break;
291 case Krb5.PA_ETYPE_INFO2:
292 if (pa_value != null) {
293 DerValue der = new DerValue(pa_value);
294 DerValue value = der.data.getDerValue();
295 ETypeInfo2 info2 = new ETypeInfo2(value);
296 etype = info2.getEType();
297 salt = info2.getSalt();
298 s2kparams = info2.getParams();
299 if (DEBUG) {
300 System.out.println("\t PA-ETYPE-INFO2 etype = " + etype);
301 }
302 }
303 break;
304 default:
305 // Unknown Pre-auth type
306 break;
307 }
308 }
309 }
310
311 public final KerberosTime getServerTime() {
312 return sTime;
313 }
314
315 public final KerberosTime getClientTime() {
316 return cTime;
317 }
318
319 public final Integer getServerMicroSeconds() {
320 return suSec;
321 }
322
323 public final Integer getClientMicroSeconds() {
324 return cuSec;
325 }
326
327 public final int getErrorCode() {
328 return errorCode;
329 }
330
331 // access pre-auth info
332 public final int getEType() {
333 return etype;
334 }
335
336 // access pre-auth info
337 public final byte[] getSalt() {
338 return ((salt == null) ? null : salt.clone());
339 }
340
341 // access pre-auth info
342 public final byte[] getParams() {
343 return ((s2kparams == null) ? null : s2kparams.clone());
344 }
345
346 public final String getErrorString() {
347 return eText;
348 }
349
350 /**
351 * Initializes a KRBError object.
352 * @param encoding a DER-encoded data.
353 * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data.
354 * @exception IOException if an I/O error occurs while reading encoded data.
355 * @exception KrbApErrException if the value read from the DER-encoded data
356 * stream does not match the pre-defined value.
357 * @exception RealmException if an error occurs while parsing a Realm object.
358 */
359 private void init(DerValue encoding) throws Asn1Exception,
360 RealmException, KrbApErrException, IOException {
361 DerValue der, subDer;
362 if (((encoding.getTag() & (byte)0x1F) != (byte)0x1E)
363 || (encoding.isApplication() != true)
364 || (encoding.isConstructed() != true)) {
365 throw new Asn1Exception(Krb5.ASN1_BAD_ID);
366 }
367 der = encoding.getData().getDerValue();
368 if (der.getTag() != DerValue.tag_Sequence) {
369 throw new Asn1Exception(Krb5.ASN1_BAD_ID);
370 }
371 subDer = der.getData().getDerValue();
372 if ((subDer.getTag() & (byte)0x1F) == (byte)0x00) {
373
374 pvno = subDer.getData().getBigInteger().intValue();
375 if (pvno != Krb5.PVNO)
376 throw new KrbApErrException(Krb5.KRB_AP_ERR_BADVERSION);
377 } else {
378 throw new Asn1Exception(Krb5.ASN1_BAD_ID);
379 }
380
381 subDer = der.getData().getDerValue();
382 if ((subDer.getTag() & (byte)0x1F) == (byte)0x01) {
383 msgType = subDer.getData().getBigInteger().intValue();
384 if (msgType != Krb5.KRB_ERROR) {
385 throw new KrbApErrException(Krb5.KRB_AP_ERR_MSG_TYPE);
386 }
387 } else {
388 throw new Asn1Exception(Krb5.ASN1_BAD_ID);
389 }
390
391 cTime = KerberosTime.parse(der.getData(), (byte)0x02, true);
392 if ((der.getData().peekByte() & 0x1F) == 0x03) {
393 subDer = der.getData().getDerValue();
394 cuSec = new Integer(subDer.getData().getBigInteger().intValue());
395 }
396 else cuSec = null;
397 sTime = KerberosTime.parse(der.getData(), (byte)0x04, false);
398 subDer = der.getData().getDerValue();
399 if ((subDer.getTag() & (byte)0x1F) == (byte)0x05) {
400 suSec = new Integer (subDer.getData().getBigInteger().intValue());
401 }
402 else throw new Asn1Exception(Krb5.ASN1_BAD_ID);
403 subDer = der.getData().getDerValue();
404 if ((subDer.getTag() & (byte)0x1F) == (byte)0x06) {
405 errorCode = subDer.getData().getBigInteger().intValue();
406 }
407 else throw new Asn1Exception(Krb5.ASN1_BAD_ID);
408 crealm = Realm.parse(der.getData(), (byte)0x07, true);
409 cname = PrincipalName.parse(der.getData(), (byte)0x08, true);
410 realm = Realm.parse(der.getData(), (byte)0x09, false);
411 sname = PrincipalName.parse(der.getData(), (byte)0x0A, false);
412 eText = null;
413 eData = null;
414 eCksum = null;
415 if (der.getData().available() >0) {
416 if ((der.getData().peekByte() & 0x1F) == 0x0B) {
417 subDer = der.getData().getDerValue();
418 eText = subDer.getData().getGeneralString();
419 }
420 }
421 if (der.getData().available() >0) {
422 if ((der.getData().peekByte() & 0x1F) == 0x0C) {
423 subDer = der.getData().getDerValue();
424 eData = subDer.getData().getOctetString();
425 }
426 }
427 if (der.getData().available() >0) {
428 eCksum = Checksum.parse(der.getData(), (byte)0x0D, true);
429 }
430 if (der.getData().available() >0)
431 throw new Asn1Exception(Krb5.ASN1_BAD_ID);
432 }
433
434 /**
435 * For debug use only
436 */
437 private void showDebug() {
438 if (DEBUG) {
439 System.out.println(">>>KRBError:");
440 if (cTime != null)
441 System.out.println("\t cTime is " + cTime.toDate().toString() + " " + cTime.toDate().getTime());
442 if (cuSec != null) {
443 System.out.println("\t cuSec is " + cuSec.intValue());
444 }
445
446 System.out.println("\t sTime is " + sTime.toDate().toString
447 () + " " + sTime.toDate().getTime());
448 System.out.println("\t suSec is " + suSec);
449 System.out.println("\t error code is " + errorCode);
450 System.out.println("\t error Message is " + Krb5.getErrorMessage(errorCode));
451 if (crealm != null) {
452 System.out.println("\t crealm is " + crealm.toString());
453 }
454 if (cname != null) {
455 System.out.println("\t cname is " + cname.toString());
456 }
457 if (realm != null) {
458 System.out.println("\t realm is " + realm.toString());
459 }
460 if (sname != null) {
461 System.out.println("\t sname is " + sname.toString());
462 }
463 if (eData != null) {
464 System.out.println("\t eData provided.");
465 }
466 if (eCksum != null) {
467 System.out.println("\t checksum provided.");
468 }
469 System.out.println("\t msgType is " + msgType);
470 }
471 }
472
473 /**
474 * Encodes an KRBError object.
475 * @return the byte array of encoded KRBError object.
476 * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data.
477 * @exception IOException if an I/O error occurs while reading encoded data.
478 */
479 public byte[] asn1Encode() throws Asn1Exception, IOException {
480 DerOutputStream temp = new DerOutputStream();
481 DerOutputStream bytes = new DerOutputStream();
482
483 temp.putInteger(BigInteger.valueOf(pvno));
484 bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x00), temp);
485 temp = new DerOutputStream();
486 temp.putInteger(BigInteger.valueOf(msgType));
487 bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x01), temp);
488
489 if (cTime != null) {
490 bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x02), cTime.asn1Encode());
491 }
492 if (cuSec != null) {
493 temp = new DerOutputStream();
494 temp.putInteger(BigInteger.valueOf(cuSec.intValue()));
495 bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x03), temp);
496 }
497
498 bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x04), sTime.asn1Encode());
499 temp = new DerOutputStream();
500 temp.putInteger(BigInteger.valueOf(suSec.intValue()));
501 bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x05), temp);
502 temp = new DerOutputStream();
503 temp.putInteger(BigInteger.valueOf(errorCode));
504 bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x06), temp);
505
506 if (crealm != null) {
507 bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x07), crealm.asn1Encode());
508 }
509 if (cname != null) {
510 bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x08), cname.asn1Encode());
511 }
512
513 bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x09), realm.asn1Encode());
514 bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x0A), sname.asn1Encode());
515
516 if (eText != null) {
517 temp = new DerOutputStream();
518 temp.putGeneralString(eText);
519 bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x0B), temp);
520 }
521 if (eData != null) {
522 temp = new DerOutputStream();
523 temp.putOctetString(eData);
524 bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x0C), temp);
525 }
526 if (eCksum != null) {
527 bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x0D), eCksum.asn1Encode());
528 }
529
530 temp = new DerOutputStream();
531 temp.write(DerValue.tag_Sequence, bytes);
532 bytes = new DerOutputStream();
533 bytes.write(DerValue.createTag(DerValue.TAG_APPLICATION, true, (byte)0x1E), temp);
534 return bytes.toByteArray();
535 }
536
537 @Override public boolean equals(Object obj) {
538 if (this == obj) {
539 return true;
540 }
541
542 if (!(obj instanceof KRBError)) {
543 return false;
544 }
545
546 KRBError other = (KRBError)obj;
547 return pvno == other.pvno &&
548 msgType == other.msgType &&
549 isEqual(cTime, other.cTime) &&
550 isEqual(cuSec, other.cuSec) &&
551 isEqual(sTime, other.sTime) &&
552 isEqual(suSec, other.suSec) &&
553 errorCode == other.errorCode &&
554 isEqual(crealm, other.crealm) &&
555 isEqual(cname, other.cname) &&
556 isEqual(realm, other.realm) &&
557 isEqual(sname, other.sname) &&
558 isEqual(eText, other.eText) &&
559 java.util.Arrays.equals(eData, other.eData) &&
560 isEqual(eCksum, other.eCksum);
561 }
562
563 private static boolean isEqual(Object a, Object b) {
564 return (a == null)?(b == null):(a.equals(b));
565 }
566
567 @Override public int hashCode() {
568 int result = 17;
569 result = 37 * result + pvno;
570 result = 37 * result + msgType;
571 if (cTime != null) result = 37 * result + cTime.hashCode();
572 if (cuSec != null) result = 37 * result + cuSec.hashCode();
573 if (sTime != null) result = 37 * result + sTime.hashCode();
574 if (suSec != null) result = 37 * result + suSec.hashCode();
575 result = 37 * result + errorCode;
576 if (crealm != null) result = 37 * result + crealm.hashCode();
577 if (cname != null) result = 37 * result + cname.hashCode();
578 if (realm != null) result = 37 * result + realm.hashCode();
579 if (sname != null) result = 37 * result + sname.hashCode();
580 if (eText != null) result = 37 * result + eText.hashCode();
581 result = 37 * result + Arrays.hashCode(eData);
582 if (eCksum != null) result = 37 * result + eCksum.hashCode();
583 return result;
584 }
585}