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