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; 33 34 import sun.security.krb5.internal.*; 35 import sun.security.krb5.internal.crypto.*; 36 import java.io.IOException; 37 import java.net.UnknownHostException; 38 import java.time.Instant; 39 import java.util.Arrays; 40 41 /** 42 * This class encapsulates a Kerberos TGS-REQ that is sent from the 43 * client to the KDC. 44 */ 45 public class KrbTgsReq { 46 47 private PrincipalName princName; 48 private PrincipalName clientAlias; 49 private PrincipalName servName; 50 private PrincipalName serverAlias; 51 private TGSReq tgsReqMessg; 52 private KerberosTime ctime; 53 private Ticket secondTicket = null; 54 private boolean useSubkey = false; 55 EncryptionKey tgsReqKey; 56 57 private static final boolean DEBUG = Krb5.DEBUG; 58 59 private byte[] obuf; 60 private byte[] ibuf; 61 62 // Used in CredentialsUtil KrbTgsReq(KDCOptions options, Credentials asCreds, PrincipalName cname, PrincipalName clientAlias, PrincipalName sname, PrincipalName serverAlias, Ticket[] additionalTickets, PAData[] extraPAs)63 public KrbTgsReq(KDCOptions options, Credentials asCreds, 64 PrincipalName cname, PrincipalName clientAlias, 65 PrincipalName sname, PrincipalName serverAlias, 66 Ticket[] additionalTickets, PAData[] extraPAs) 67 throws KrbException, IOException { 68 this(options, 69 asCreds, 70 cname, 71 clientAlias, 72 sname, 73 serverAlias, 74 null, // KerberosTime from 75 null, // KerberosTime till 76 null, // KerberosTime rtime 77 null, // int[] eTypes 78 null, // HostAddresses addresses 79 null, // AuthorizationData authorizationData 80 additionalTickets, 81 null, // EncryptionKey subKey 82 extraPAs); 83 } 84 85 // Called by Credentials, KrbCred KrbTgsReq( KDCOptions options, Credentials asCreds, PrincipalName sname, PrincipalName serverAlias, KerberosTime from, KerberosTime till, KerberosTime rtime, int[] eTypes, HostAddresses addresses, AuthorizationData authorizationData, Ticket[] additionalTickets, EncryptionKey subKey)86 KrbTgsReq( 87 KDCOptions options, 88 Credentials asCreds, 89 PrincipalName sname, 90 PrincipalName serverAlias, 91 KerberosTime from, 92 KerberosTime till, 93 KerberosTime rtime, 94 int[] eTypes, 95 HostAddresses addresses, 96 AuthorizationData authorizationData, 97 Ticket[] additionalTickets, 98 EncryptionKey subKey) throws KrbException, IOException { 99 this(options, asCreds, asCreds.getClient(), asCreds.getClientAlias(), 100 sname, serverAlias, from, till, rtime, eTypes, 101 addresses, authorizationData, additionalTickets, subKey, null); 102 } 103 KrbTgsReq( KDCOptions options, Credentials asCreds, PrincipalName cname, PrincipalName clientAlias, PrincipalName sname, PrincipalName serverAlias, KerberosTime from, KerberosTime till, KerberosTime rtime, int[] eTypes, HostAddresses addresses, AuthorizationData authorizationData, Ticket[] additionalTickets, EncryptionKey subKey, PAData[] extraPAs)104 private KrbTgsReq( 105 KDCOptions options, 106 Credentials asCreds, 107 PrincipalName cname, 108 PrincipalName clientAlias, 109 PrincipalName sname, 110 PrincipalName serverAlias, 111 KerberosTime from, 112 KerberosTime till, 113 KerberosTime rtime, 114 int[] eTypes, 115 HostAddresses addresses, 116 AuthorizationData authorizationData, 117 Ticket[] additionalTickets, 118 EncryptionKey subKey, 119 PAData[] extraPAs) throws KrbException, IOException { 120 121 princName = cname; 122 this.clientAlias = clientAlias; 123 servName = sname; 124 this.serverAlias = serverAlias; 125 ctime = KerberosTime.now(); 126 127 // check if they are valid arguments. The optional fields 128 // should be consistent with settings in KDCOptions. 129 130 if (options.get(KDCOptions.FORWARDABLE) && 131 (!(asCreds.flags.get(Krb5.TKT_OPTS_FORWARDABLE)))) { 132 options.set(KDCOptions.FORWARDABLE, false); 133 } 134 if (options.get(KDCOptions.FORWARDED)) { 135 if (!(asCreds.flags.get(KDCOptions.FORWARDABLE))) 136 throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS); 137 } 138 if (options.get(KDCOptions.PROXIABLE) && 139 (!(asCreds.flags.get(Krb5.TKT_OPTS_PROXIABLE)))) { 140 throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS); 141 } 142 if (options.get(KDCOptions.PROXY)) { 143 if (!(asCreds.flags.get(KDCOptions.PROXIABLE))) 144 throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS); 145 } 146 if (options.get(KDCOptions.ALLOW_POSTDATE) && 147 (!(asCreds.flags.get(Krb5.TKT_OPTS_MAY_POSTDATE)))) { 148 throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS); 149 } 150 if (options.get(KDCOptions.RENEWABLE) && 151 (!(asCreds.flags.get(Krb5.TKT_OPTS_RENEWABLE)))) { 152 throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS); 153 } 154 155 if (options.get(KDCOptions.POSTDATED)) { 156 if (!(asCreds.flags.get(KDCOptions.POSTDATED))) 157 throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS); 158 } else { 159 if (from != null) from = null; 160 } 161 if (options.get(KDCOptions.RENEWABLE)) { 162 if (!(asCreds.flags.get(KDCOptions.RENEWABLE))) 163 throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS); 164 } else { 165 if (rtime != null) rtime = null; 166 } 167 if (options.get(KDCOptions.ENC_TKT_IN_SKEY) || options.get(KDCOptions.CNAME_IN_ADDL_TKT)) { 168 if (additionalTickets == null) 169 throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS); 170 // in TGS_REQ there could be more than one additional 171 // tickets, but in file-based credential cache, 172 // there is only one additional ticket field. 173 secondTicket = additionalTickets[0]; 174 } else { 175 if (additionalTickets != null) 176 additionalTickets = null; 177 } 178 179 tgsReqMessg = createRequest( 180 options, 181 asCreds.ticket, 182 asCreds.key, 183 ctime, 184 princName, 185 servName, 186 from, 187 till, 188 rtime, 189 eTypes, 190 addresses, 191 authorizationData, 192 additionalTickets, 193 subKey, 194 extraPAs); 195 obuf = tgsReqMessg.asn1Encode(); 196 197 // XXX We need to revisit this to see if can't move it 198 // up such that FORWARDED flag set in the options 199 // is included in the marshaled request. 200 /* 201 * If this is based on a forwarded ticket, record that in the 202 * options, because the returned TgsRep will contain the 203 * FORWARDED flag set. 204 */ 205 if (asCreds.flags.get(KDCOptions.FORWARDED)) 206 options.set(KDCOptions.FORWARDED, true); 207 208 209 } 210 211 /** 212 * Sends a TGS request to the realm of the target. 213 * @throws KrbException 214 * @throws IOException 215 */ send()216 public void send() throws IOException, KrbException { 217 String realmStr = null; 218 if (servName != null) 219 realmStr = servName.getRealmString(); 220 KdcComm comm = new KdcComm(realmStr); 221 ibuf = comm.send(obuf); 222 } 223 getReply()224 public KrbTgsRep getReply() 225 throws KrbException, IOException { 226 return new KrbTgsRep(ibuf, this); 227 } 228 229 /** 230 * Sends the request, waits for a reply, and returns the Credentials. 231 * Used in Credentials, KrbCred, and internal/CredentialsUtil. 232 */ sendAndGetCreds()233 public Credentials sendAndGetCreds() throws IOException, KrbException { 234 KrbTgsRep tgs_rep = null; 235 String kdc = null; 236 send(); 237 tgs_rep = getReply(); 238 return tgs_rep.getCreds(); 239 } 240 getCtime()241 KerberosTime getCtime() { 242 return ctime; 243 } 244 createRequest( KDCOptions kdc_options, Ticket ticket, EncryptionKey key, KerberosTime ctime, PrincipalName cname, PrincipalName sname, KerberosTime from, KerberosTime till, KerberosTime rtime, int[] eTypes, HostAddresses addresses, AuthorizationData authorizationData, Ticket[] additionalTickets, EncryptionKey subKey, PAData[] extraPAs)245 private TGSReq createRequest( 246 KDCOptions kdc_options, 247 Ticket ticket, 248 EncryptionKey key, 249 KerberosTime ctime, 250 PrincipalName cname, 251 PrincipalName sname, 252 KerberosTime from, 253 KerberosTime till, 254 KerberosTime rtime, 255 int[] eTypes, 256 HostAddresses addresses, 257 AuthorizationData authorizationData, 258 Ticket[] additionalTickets, 259 EncryptionKey subKey, 260 PAData[] extraPAs) 261 throws IOException, KrbException, UnknownHostException { 262 KerberosTime req_till = null; 263 if (till == null) { 264 String d = Config.getInstance().get("libdefaults", "ticket_lifetime"); 265 if (d != null) { 266 req_till = new KerberosTime(Instant.now().plusSeconds(Config.duration(d))); 267 } else { 268 req_till = new KerberosTime(0); // Choose KDC maximum allowed 269 } 270 } else { 271 req_till = till; 272 } 273 274 /* 275 * RFC 4120, Section 5.4.2. 276 * For KRB_TGS_REP, the ciphertext is encrypted in the 277 * sub-session key from the Authenticator, or if absent, 278 * the session key from the ticket-granting ticket used 279 * in the request. 280 * 281 * To support this, use tgsReqKey to remember which key to use. 282 */ 283 tgsReqKey = key; 284 285 int[] req_eTypes = null; 286 if (eTypes == null) { 287 req_eTypes = EType.getDefaults("default_tgs_enctypes"); 288 } else { 289 req_eTypes = eTypes; 290 } 291 292 EncryptionKey reqKey = null; 293 EncryptedData encAuthorizationData = null; 294 if (authorizationData != null) { 295 byte[] ad = authorizationData.asn1Encode(); 296 if (subKey != null) { 297 reqKey = subKey; 298 tgsReqKey = subKey; // Key to use to decrypt reply 299 useSubkey = true; 300 encAuthorizationData = new EncryptedData(reqKey, ad, 301 KeyUsage.KU_TGS_REQ_AUTH_DATA_SUBKEY); 302 } else 303 encAuthorizationData = new EncryptedData(key, ad, 304 KeyUsage.KU_TGS_REQ_AUTH_DATA_SESSKEY); 305 } 306 307 KDCReqBody reqBody = new KDCReqBody( 308 kdc_options, 309 cname, 310 sname, 311 from, 312 req_till, 313 rtime, 314 Nonce.value(), 315 req_eTypes, 316 addresses, 317 encAuthorizationData, 318 additionalTickets); 319 320 byte[] temp = reqBody.asn1Encode(Krb5.KRB_TGS_REQ); 321 // if the checksum type is one of the keyed checksum types, 322 // use session key. 323 Checksum cksum = new Checksum(Checksum.CKSUMTYPE_DEFAULT, temp, key, 324 KeyUsage.KU_PA_TGS_REQ_CKSUM); 325 326 // Usage will be KeyUsage.KU_PA_TGS_REQ_AUTHENTICATOR 327 328 byte[] tgs_ap_req = new KrbApReq( 329 new APOptions(), 330 ticket, 331 key, 332 cname, 333 cksum, 334 ctime, 335 reqKey, 336 null, 337 null).getMessage(); 338 339 PAData tgsPAData = new PAData(Krb5.PA_TGS_REQ, tgs_ap_req); 340 PAData[] pa; 341 if (extraPAs != null) { 342 pa = Arrays.copyOf(extraPAs, extraPAs.length + 1); 343 pa[extraPAs.length] = tgsPAData; 344 } else { 345 pa = new PAData[] {tgsPAData}; 346 } 347 return new TGSReq(pa, reqBody); 348 } 349 getMessage()350 TGSReq getMessage() { 351 return tgsReqMessg; 352 } 353 getSecondTicket()354 Ticket getSecondTicket() { 355 return secondTicket; 356 } 357 getClientAlias()358 PrincipalName getClientAlias() { 359 return clientAlias; 360 } 361 getServerAlias()362 PrincipalName getServerAlias() { 363 return serverAlias; 364 } 365 debug(String message)366 private static void debug(String message) { 367 // System.err.println(">>> KrbTgsReq: " + message); 368 } 369 usedSubkey()370 boolean usedSubkey() { 371 return useSubkey; 372 } 373 374 } 375