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 32 package sun.security.krb5; 33 34 import sun.security.krb5.internal.*; 35 import sun.security.krb5.internal.crypto.*; 36 import sun.security.jgss.krb5.Krb5AcceptCredential; 37 import java.net.InetAddress; 38 import sun.security.util.*; 39 import java.io.IOException; 40 import java.util.Arrays; 41 import java.security.MessageDigest; 42 import java.security.NoSuchAlgorithmException; 43 import sun.security.krb5.internal.rcache.AuthTimeWithHash; 44 45 /** 46 * This class encapsulates a KRB-AP-REQ that a client sends to a 47 * server for authentication. 48 */ 49 public class KrbApReq { 50 51 private byte[] obuf; 52 private KerberosTime ctime; 53 private int cusec; 54 private Authenticator authenticator; 55 private Credentials creds; 56 private APReq apReqMessg; 57 58 // Used by acceptor side 59 private static ReplayCache rcache = ReplayCache.getInstance(); 60 private static boolean DEBUG = Krb5.DEBUG; 61 private static final char[] hexConst = "0123456789ABCDEF".toCharArray(); 62 63 /** 64 * Constructs an AP-REQ message to send to the peer. 65 * @param tgsCred the <code>Credentials</code> to be used to construct the 66 * AP Request protocol message. 67 * @param mutualRequired Whether mutual authentication is required 68 * @param useSubkey Whether the subkey is to be used to protect this 69 * specific application session. If this is not set then the 70 * session key from the ticket will be used. 71 * @throws KrbException for any Kerberos protocol specific error 72 * @throws IOException for any IO related errors 73 * (e.g. socket operations) 74 */ 75 /* 76 // Not Used 77 public KrbApReq(Credentials tgsCred, 78 boolean mutualRequired, 79 boolean useSubKey, 80 boolean useSeqNumber) throws Asn1Exception, 81 KrbCryptoException, KrbException, IOException { 82 83 this(tgsCred, mutualRequired, useSubKey, useSeqNumber, null); 84 } 85 */ 86 87 /** 88 * Constructs an AP-REQ message to send to the peer. 89 * @param tgsCred the <code>Credentials</code> to be used to construct the 90 * AP Request protocol message. 91 * @param mutualRequired Whether mutual authentication is required 92 * @param useSubKey Whether the subkey is to be used to protect this 93 * specific application session. If this is not set then the 94 * session key from the ticket will be used. 95 * @param cksum checksum of the application data that accompanies 96 * the KRB_AP_REQ. 97 * @throws KrbException for any Kerberos protocol specific error 98 * @throws IOException for any IO related errors 99 * (e.g. socket operations) 100 */ 101 // Used in InitSecContextToken 102 public KrbApReq(Credentials tgsCred, 103 boolean mutualRequired, 104 boolean useSubKey, 105 boolean useSeqNumber, 106 Checksum cksum) throws Asn1Exception, 107 KrbCryptoException, KrbException, IOException { 108 109 APOptions apOptions = (mutualRequired? 110 new APOptions(Krb5.AP_OPTS_MUTUAL_REQUIRED): 111 new APOptions()); 112 if (DEBUG) 113 System.out.println(">>> KrbApReq: APOptions are " + apOptions); 114 115 EncryptionKey subKey = (useSubKey? 116 new EncryptionKey(tgsCred.getSessionKey()): 117 null); 118 119 SeqNumber seqNum = new LocalSeqNumber(); 120 121 init(apOptions, 122 tgsCred, 123 cksum, 124 subKey, 125 seqNum, 126 null, // AuthorizationData authzData 127 KeyUsage.KU_AP_REQ_AUTHENTICATOR); 128 129 } 130 131 /** 132 * Constructs an AP-REQ message from the bytes received from the 133 * peer. 134 * @param message The message received from the peer 135 * @param cred <code>KrbAcceptCredential</code> containing keys to decrypt 136 * the message; key selected will depend on etype used to encrypt data 137 * @throws KrbException for any Kerberos protocol specific error 138 * @throws IOException for any IO related errors 139 * (e.g. socket operations) 140 */ 141 // Used in InitSecContextToken (for AP_REQ and not TGS REQ) 142 public KrbApReq(byte[] message, 143 Krb5AcceptCredential cred, 144 InetAddress initiator) 145 throws KrbException, IOException { 146 obuf = message; 147 if (apReqMessg == null) 148 decode(); 149 authenticate(cred, initiator); 150 } 151 152 /** 153 * Constructs an AP-REQ message from the bytes received from the 154 * peer. 155 * @param value The <code>DerValue</code> that contains the 156 * DER enoded AP-REQ protocol message 157 * @param keys <code>EncrtyptionKey</code>s to decrypt the message; 158 * 159 * @throws KrbException for any Kerberos protocol specific error 160 * @throws IOException for any IO related errors 161 * (e.g. socket operations) 162 */ 163 /* 164 public KrbApReq(DerValue value, EncryptionKey[] key, InetAddress initiator) 165 throws KrbException, IOException { 166 obuf = value.toByteArray(); 167 if (apReqMessg == null) 168 decode(value); 169 authenticate(keys, initiator); 170 } 171 172 KrbApReq(APOptions options, 173 Credentials tgs_creds, 174 Checksum cksum, 175 EncryptionKey subKey, 176 SeqNumber seqNumber, 177 AuthorizationData authorizationData) 178 throws KrbException, IOException { 179 init(options, tgs_creds, cksum, subKey, seqNumber, authorizationData); 180 } 181 */ 182 183 /** used by KrbTgsReq **/ 184 KrbApReq(APOptions apOptions, 185 Ticket ticket, 186 EncryptionKey key, 187 PrincipalName cname, 188 Checksum cksum, 189 KerberosTime ctime, 190 EncryptionKey subKey, 191 SeqNumber seqNumber, 192 AuthorizationData authorizationData) 193 throws Asn1Exception, IOException, 194 KdcErrException, KrbCryptoException { 195 196 init(apOptions, ticket, key, cname, 197 cksum, ctime, subKey, seqNumber, authorizationData, 198 KeyUsage.KU_PA_TGS_REQ_AUTHENTICATOR); 199 200 } 201 202 private void init(APOptions options, 203 Credentials tgs_creds, 204 Checksum cksum, 205 EncryptionKey subKey, 206 SeqNumber seqNumber, 207 AuthorizationData authorizationData, 208 int usage) 209 throws KrbException, IOException { 210 211 ctime = KerberosTime.now(); 212 init(options, 213 tgs_creds.ticket, 214 tgs_creds.key, 215 tgs_creds.client, 216 cksum, 217 ctime, 218 subKey, 219 seqNumber, 220 authorizationData, 221 usage); 222 } 223 224 private void init(APOptions apOptions, 225 Ticket ticket, 226 EncryptionKey key, 227 PrincipalName cname, 228 Checksum cksum, 229 KerberosTime ctime, 230 EncryptionKey subKey, 231 SeqNumber seqNumber, 232 AuthorizationData authorizationData, 233 int usage) 234 throws Asn1Exception, IOException, 235 KdcErrException, KrbCryptoException { 236 237 createMessage(apOptions, ticket, key, cname, 238 cksum, ctime, subKey, seqNumber, authorizationData, 239 usage); 240 obuf = apReqMessg.asn1Encode(); 241 } 242 243 244 void decode() throws KrbException, IOException { 245 DerValue encoding = new DerValue(obuf); 246 decode(encoding); 247 } 248 249 void decode(DerValue encoding) throws KrbException, IOException { 250 apReqMessg = null; 251 try { 252 apReqMessg = new APReq(encoding); 253 } catch (Asn1Exception e) { 254 apReqMessg = null; 255 KRBError err = new KRBError(encoding); 256 String errStr = err.getErrorString(); 257 String eText; 258 if (errStr.charAt(errStr.length() - 1) == 0) 259 eText = errStr.substring(0, errStr.length() - 1); 260 else 261 eText = errStr; 262 KrbException ke = new KrbException(err.getErrorCode(), eText); 263 ke.initCause(e); 264 throw ke; 265 } 266 } 267 268 private void authenticate(Krb5AcceptCredential cred, InetAddress initiator) 269 throws KrbException, IOException { 270 int encPartKeyType = apReqMessg.ticket.encPart.getEType(); 271 Integer kvno = apReqMessg.ticket.encPart.getKeyVersionNumber(); 272 EncryptionKey[] keys = cred.getKrb5EncryptionKeys(apReqMessg.ticket.sname); 273 EncryptionKey dkey = EncryptionKey.findKey(encPartKeyType, kvno, keys); 274 275 if (dkey == null) { 276 throw new KrbException(Krb5.API_INVALID_ARG, 277 "Cannot find key of appropriate type to decrypt AP-REQ - " + 278 EType.toString(encPartKeyType)); 279 } 280 281 byte[] bytes = apReqMessg.ticket.encPart.decrypt(dkey, 282 KeyUsage.KU_TICKET); 283 byte[] temp = apReqMessg.ticket.encPart.reset(bytes); 284 EncTicketPart enc_ticketPart = new EncTicketPart(temp); 285 286 checkPermittedEType(enc_ticketPart.key.getEType()); 287 288 byte[] bytes2 = apReqMessg.authenticator.decrypt(enc_ticketPart.key, 289 KeyUsage.KU_AP_REQ_AUTHENTICATOR); 290 byte[] temp2 = apReqMessg.authenticator.reset(bytes2); 291 authenticator = new Authenticator(temp2); 292 ctime = authenticator.ctime; 293 cusec = authenticator.cusec; 294 authenticator.ctime = 295 authenticator.ctime.withMicroSeconds(authenticator.cusec); 296 297 if (!authenticator.cname.equals(enc_ticketPart.cname)) { 298 throw new KrbApErrException(Krb5.KRB_AP_ERR_BADMATCH); 299 } 300 301 if (!authenticator.ctime.inClockSkew()) 302 throw new KrbApErrException(Krb5.KRB_AP_ERR_SKEW); 303 304 String alg = AuthTimeWithHash.DEFAULT_HASH_ALG; 305 byte[] hash; 306 try { 307 hash = MessageDigest.getInstance(AuthTimeWithHash.realAlg(alg)) 308 .digest(apReqMessg.authenticator.cipher); 309 } catch (NoSuchAlgorithmException ex) { 310 throw new AssertionError("Impossible"); 311 } 312 313 char[] h = new char[hash.length * 2]; 314 for (int i=0; i<hash.length; i++) { 315 h[2*i] = hexConst[(hash[i]&0xff)>>4]; 316 h[2*i+1] = hexConst[hash[i]&0xf]; 317 } 318 AuthTimeWithHash time = new AuthTimeWithHash( 319 authenticator.cname.toString(), 320 apReqMessg.ticket.sname.toString(), 321 authenticator.ctime.getSeconds(), 322 authenticator.cusec, 323 alg, 324 new String(h)); 325 rcache.checkAndStore(KerberosTime.now(), time); 326 327 if (initiator != null) { 328 // sender host address 329 HostAddress sender = new HostAddress(initiator); 330 if (enc_ticketPart.caddr != null 331 && !enc_ticketPart.caddr.inList(sender)) { 332 if (DEBUG) { 333 System.out.println(">>> KrbApReq: initiator is " 334 + sender.getInetAddress() 335 + ", but caddr is " 336 + Arrays.toString( 337 enc_ticketPart.caddr.getInetAddresses())); 338 } 339 throw new KrbApErrException(Krb5.KRB_AP_ERR_BADADDR); 340 } 341 } 342 343 // XXX check for repeated authenticator 344 // if found 345 // throw new KrbApErrException(Krb5.KRB_AP_ERR_REPEAT); 346 // else 347 // save authenticator to check for later 348 349 KerberosTime now = KerberosTime.now(); 350 351 if ((enc_ticketPart.starttime != null && 352 enc_ticketPart.starttime.greaterThanWRTClockSkew(now)) || 353 enc_ticketPart.flags.get(Krb5.TKT_OPTS_INVALID)) 354 throw new KrbApErrException(Krb5.KRB_AP_ERR_TKT_NYV); 355 356 // if the current time is later than end time by more 357 // than the allowable clock skew, throws ticket expired exception. 358 if (enc_ticketPart.endtime != null && 359 now.greaterThanWRTClockSkew(enc_ticketPart.endtime)) { 360 throw new KrbApErrException(Krb5.KRB_AP_ERR_TKT_EXPIRED); 361 } 362 363 creds = new Credentials( 364 apReqMessg.ticket, 365 authenticator.cname, 366 apReqMessg.ticket.sname, 367 enc_ticketPart.key, 368 enc_ticketPart.flags, 369 enc_ticketPart.authtime, 370 enc_ticketPart.starttime, 371 enc_ticketPart.endtime, 372 enc_ticketPart.renewTill, 373 enc_ticketPart.caddr, 374 enc_ticketPart.authorizationData); 375 if (DEBUG) { 376 System.out.println(">>> KrbApReq: authenticate succeed."); 377 } 378 } 379 380 /** 381 * Returns the credentials that are contained in the ticket that 382 * is part of this AP-REQ. 383 */ 384 public Credentials getCreds() { 385 return creds; 386 } 387 388 KerberosTime getCtime() { 389 if (ctime != null) 390 return ctime; 391 return authenticator.ctime; 392 } 393 394 int cusec() { 395 return cusec; 396 } 397 398 APOptions getAPOptions() throws KrbException, IOException { 399 if (apReqMessg == null) 400 decode(); 401 if (apReqMessg != null) 402 return apReqMessg.apOptions; 403 return null; 404 } 405 406 /** 407 * Returns true if mutual authentication is required and hence an 408 * AP-REP will need to be generated. 409 * @throws KrbException 410 * @throws IOException 411 */ 412 public boolean getMutualAuthRequired() throws KrbException, IOException { 413 if (apReqMessg == null) 414 decode(); 415 if (apReqMessg != null) 416 return apReqMessg.apOptions.get(Krb5.AP_OPTS_MUTUAL_REQUIRED); 417 return false; 418 } 419 420 boolean useSessionKey() throws KrbException, IOException { 421 if (apReqMessg == null) 422 decode(); 423 if (apReqMessg != null) 424 return apReqMessg.apOptions.get(Krb5.AP_OPTS_USE_SESSION_KEY); 425 return false; 426 } 427 428 /** 429 * Returns the optional subkey stored in the Authenticator for 430 * this message. Returns null if none is stored. 431 */ 432 public EncryptionKey getSubKey() { 433 // XXX Can authenticator be null 434 return authenticator.getSubKey(); 435 } 436 437 /** 438 * Returns the optional sequence number stored in the 439 * Authenticator for this message. Returns null if none is 440 * stored. 441 */ 442 public Integer getSeqNumber() { 443 // XXX Can authenticator be null 444 return authenticator.getSeqNumber(); 445 } 446 447 /** 448 * Returns the optional Checksum stored in the 449 * Authenticator for this message. Returns null if none is 450 * stored. 451 */ 452 public Checksum getChecksum() { 453 return authenticator.getChecksum(); 454 } 455 456 /** 457 * Returns the ASN.1 encoding that should be sent to the peer. 458 */ 459 public byte[] getMessage() { 460 return obuf; 461 } 462 463 /** 464 * Returns the principal name of the client that generated this 465 * message. 466 */ 467 public PrincipalName getClient() { 468 return creds.getClient(); 469 } 470 471 private void createMessage(APOptions apOptions, 472 Ticket ticket, 473 EncryptionKey key, 474 PrincipalName cname, 475 Checksum cksum, 476 KerberosTime ctime, 477 EncryptionKey subKey, 478 SeqNumber seqNumber, 479 AuthorizationData authorizationData, 480 int usage) 481 throws Asn1Exception, IOException, 482 KdcErrException, KrbCryptoException { 483 484 Integer seqno = null; 485 486 if (seqNumber != null) 487 seqno = seqNumber.current(); 488 489 authenticator = 490 new Authenticator(cname, 491 cksum, 492 ctime.getMicroSeconds(), 493 ctime, 494 subKey, 495 seqno, 496 authorizationData); 497 498 byte[] temp = authenticator.asn1Encode(); 499 500 EncryptedData encAuthenticator = 501 new EncryptedData(key, temp, usage); 502 503 apReqMessg = 504 new APReq(apOptions, ticket, encAuthenticator); 505 } 506 507 // Check that key is one of the permitted types 508 private static void checkPermittedEType(int target) throws KrbException { 509 int[] etypes = EType.getDefaults("permitted_enctypes"); 510 if (!EType.isSupported(target, etypes)) { 511 throw new KrbException(EType.toString(target) + 512 " encryption type not in permitted_enctypes list"); 513 } 514 } 515 }