Sun, 31 May 2020 10:13:04 +0800
8246193: Possible NPE in ENC-PA-REP search in AS-REQ
Reviewed-by: zgu, andrew
1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3 *
4 * This code is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 only, as
6 * published by the Free Software Foundation. Oracle designates this
7 * particular file as subject to the "Classpath" exception as provided
8 * by Oracle in the LICENSE file that accompanied this code.
9 *
10 * This code is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * version 2 for more details (a copy is included in the LICENSE file that
14 * accompanied this code).
15 *
16 * You should have received a copy of the GNU General Public License version
17 * 2 along with this work; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19 *
20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21 * or visit www.oracle.com if you need additional information or have any
22 * questions.
23 */
25 /*
26 *
27 * (C) Copyright IBM Corp. 1999 All Rights Reserved.
28 * Copyright 1997 The Open Group Research Institute. All rights reserved.
29 */
31 package sun.security.krb5;
33 import sun.security.krb5.internal.*;
34 import sun.security.krb5.internal.crypto.KeyUsage;
35 import sun.security.util.DerInputStream;
37 abstract class KrbKdcRep {
39 static void check(
40 boolean isAsReq,
41 KDCReq req,
42 KDCRep rep,
43 EncryptionKey replyKey
44 ) throws KrbApErrException {
46 // cname change in AS-REP is allowed only if the client
47 // sent CANONICALIZE or an NT-ENTERPRISE cname in the request, and the
48 // server supports RFC 6806 - Section 11 FAST scheme (ENC-PA-REP flag).
49 if (isAsReq && !req.reqBody.cname.equals(rep.cname) &&
50 ((!req.reqBody.kdcOptions.get(KDCOptions.CANONICALIZE) &&
51 req.reqBody.cname.getNameType() !=
52 PrincipalName.KRB_NT_ENTERPRISE) ||
53 !rep.encKDCRepPart.flags.get(Krb5.TKT_OPTS_ENC_PA_REP))) {
54 rep.encKDCRepPart.key.destroy();
55 throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
56 }
58 // sname change in TGS-REP is allowed only if client
59 // sent CANONICALIZE and new sname is a referral of
60 // the form krbtgt/TO-REALM.COM@FROM-REALM.COM.
61 if (!req.reqBody.sname.equals(rep.encKDCRepPart.sname)) {
62 String[] snameStrings = rep.encKDCRepPart.sname.getNameStrings();
63 if (isAsReq || !req.reqBody.kdcOptions.get(KDCOptions.CANONICALIZE) ||
64 snameStrings == null || snameStrings.length != 2 ||
65 !snameStrings[0].equals(PrincipalName.TGS_DEFAULT_SRV_NAME) ||
66 !rep.encKDCRepPart.sname.getRealmString().equals(
67 req.reqBody.sname.getRealmString())) {
68 rep.encKDCRepPart.key.destroy();
69 throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
70 }
71 }
73 if (req.reqBody.getNonce() != rep.encKDCRepPart.nonce) {
74 rep.encKDCRepPart.key.destroy();
75 throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
76 }
78 if (
79 ((req.reqBody.addresses != null && rep.encKDCRepPart.caddr != null) &&
80 !req.reqBody.addresses.equals(rep.encKDCRepPart.caddr))) {
81 rep.encKDCRepPart.key.destroy();
82 throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
83 }
85 // We allow KDC to return a non-forwardable ticket if request has -f
86 for (int i = 2; i < 6; i++) {
87 if (req.reqBody.kdcOptions.get(i) !=
88 rep.encKDCRepPart.flags.get(i)) {
89 if (Krb5.DEBUG) {
90 System.out.println("> KrbKdcRep.check: at #" + i
91 + ". request for " + req.reqBody.kdcOptions.get(i)
92 + ", received " + rep.encKDCRepPart.flags.get(i));
93 }
94 throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
95 }
96 }
98 // Reply to a renewable request should be renewable, but if request does
99 // not contain renewable, KDC is free to issue a renewable ticket (for
100 // example, if ticket_lifetime is too big).
101 if (req.reqBody.kdcOptions.get(KDCOptions.RENEWABLE) &&
102 !rep.encKDCRepPart.flags.get(KDCOptions.RENEWABLE)) {
103 throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
104 }
106 if ((req.reqBody.from == null) || req.reqBody.from.isZero()) {
107 // verify this is allowed
108 if ((rep.encKDCRepPart.starttime != null) &&
109 !rep.encKDCRepPart.starttime.inClockSkew()) {
110 rep.encKDCRepPart.key.destroy();
111 throw new KrbApErrException(Krb5.KRB_AP_ERR_SKEW);
112 }
113 }
115 if ((req.reqBody.from != null) && !req.reqBody.from.isZero()) {
116 // verify this is allowed
117 if ((rep.encKDCRepPart.starttime != null) &&
118 !req.reqBody.from.equals(rep.encKDCRepPart.starttime)) {
119 rep.encKDCRepPart.key.destroy();
120 throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
121 }
122 }
124 if (!req.reqBody.till.isZero() &&
125 rep.encKDCRepPart.endtime.greaterThan(req.reqBody.till)) {
126 rep.encKDCRepPart.key.destroy();
127 throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
128 }
130 // RFC 6806 - Section 11 mechanism check
131 // The availability of the ENC-PA-REP flag in the KDC response is
132 // mandatory on some cases (see Krb5.TKT_OPTS_ENC_PA_REP check above).
133 if (rep.encKDCRepPart.flags.get(Krb5.TKT_OPTS_ENC_PA_REP)) {
134 boolean reqPaReqEncPaRep = false;
135 boolean repPaReqEncPaRepValid = false;
137 if (req.pAData != null) {
138 for (PAData pa : req.pAData) {
139 if (pa.getType() == Krb5.PA_REQ_ENC_PA_REP) {
140 // The KDC supports RFC 6806 and ENC-PA-REP was sent in
141 // the request (AS-REQ). A valid checksum is now required.
142 reqPaReqEncPaRep = true;
143 break;
144 }
145 }
146 }
148 if (rep.encKDCRepPart.pAData != null) {
149 for (PAData pa : rep.encKDCRepPart.pAData) {
150 if (pa.getType() == Krb5.PA_REQ_ENC_PA_REP) {
151 try {
152 Checksum repCksum = new Checksum(
153 new DerInputStream(
154 pa.getValue()).getDerValue());
155 // The checksum is inside encKDCRepPart so we don't
156 // care if it's keyed or not.
157 repPaReqEncPaRepValid =
158 repCksum.verifyAnyChecksum(
159 req.asn1Encode(), replyKey,
160 KeyUsage.KU_AS_REQ);
161 } catch (Exception e) {
162 if (Krb5.DEBUG) {
163 e.printStackTrace();
164 }
165 }
166 break;
167 }
168 }
169 }
171 if (reqPaReqEncPaRep && !repPaReqEncPaRepValid) {
172 throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
173 }
174 }
176 if (req.reqBody.kdcOptions.get(KDCOptions.RENEWABLE)) {
177 if (req.reqBody.rtime != null && !req.reqBody.rtime.isZero()) {
178 // verify this is required
179 if ((rep.encKDCRepPart.renewTill == null) ||
180 rep.encKDCRepPart.renewTill.greaterThan(req.reqBody.rtime)
181 ) {
182 rep.encKDCRepPart.key.destroy();
183 throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
184 }
186 }
187 }
188 }
189 }