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