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