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 byte[] hash; 305 try { 306 hash = MessageDigest.getInstance("MD5") 307 .digest(apReqMessg.authenticator.cipher); 308 } catch (NoSuchAlgorithmException ex) { 309 throw new AssertionError("Impossible"); 310 } 311 312 char[] h = new char[hash.length * 2]; 313 for (int i=0; i<hash.length; i++) { 314 h[2*i] = hexConst[(hash[i]&0xff)>>4]; 315 h[2*i+1] = hexConst[hash[i]&0xf]; 316 } 317 AuthTimeWithHash time = new AuthTimeWithHash( 318 authenticator.cname.toString(), 319 apReqMessg.ticket.sname.toString(), 320 authenticator.ctime.getSeconds(), 321 authenticator.cusec, 322 new String(h)); 323 rcache.checkAndStore(KerberosTime.now(), time); 324 325 if (initiator != null) { 326 // sender host address 327 HostAddress sender = new HostAddress(initiator); 328 if (enc_ticketPart.caddr != null 329 && !enc_ticketPart.caddr.inList(sender)) { 330 if (DEBUG) { 331 System.out.println(">>> KrbApReq: initiator is " 332 + sender.getInetAddress() 333 + ", but caddr is " 334 + Arrays.toString( 335 enc_ticketPart.caddr.getInetAddresses())); 336 } 337 throw new KrbApErrException(Krb5.KRB_AP_ERR_BADADDR); 338 } 339 } 340 341 // XXX check for repeated authenticator 342 // if found 343 // throw new KrbApErrException(Krb5.KRB_AP_ERR_REPEAT); 344 // else 345 // save authenticator to check for later 346 347 KerberosTime now = KerberosTime.now(); 348 349 if ((enc_ticketPart.starttime != null && 350 enc_ticketPart.starttime.greaterThanWRTClockSkew(now)) || 351 enc_ticketPart.flags.get(Krb5.TKT_OPTS_INVALID)) 352 throw new KrbApErrException(Krb5.KRB_AP_ERR_TKT_NYV); 353 354 // if the current time is later than end time by more 355 // than the allowable clock skew, throws ticket expired exception. 356 if (enc_ticketPart.endtime != null && 357 now.greaterThanWRTClockSkew(enc_ticketPart.endtime)) { 358 throw new KrbApErrException(Krb5.KRB_AP_ERR_TKT_EXPIRED); 359 } 360 361 creds = new Credentials( 362 apReqMessg.ticket, 363 authenticator.cname, 364 apReqMessg.ticket.sname, 365 enc_ticketPart.key, 366 enc_ticketPart.flags, 367 enc_ticketPart.authtime, 368 enc_ticketPart.starttime, 369 enc_ticketPart.endtime, 370 enc_ticketPart.renewTill, 371 enc_ticketPart.caddr, 372 enc_ticketPart.authorizationData); 373 if (DEBUG) { 374 System.out.println(">>> KrbApReq: authenticate succeed."); 375 } 376 } 377 378 /** 379 * Returns the credentials that are contained in the ticket that 380 * is part of this AP-REQ. 381 */ 382 public Credentials getCreds() { 383 return creds; 384 } 385 386 KerberosTime getCtime() { 387 if (ctime != null) 388 return ctime; 389 return authenticator.ctime; 390 } 391 392 int cusec() { 393 return cusec; 394 } 395 396 APOptions getAPOptions() throws KrbException, IOException { 397 if (apReqMessg == null) 398 decode(); 399 if (apReqMessg != null) 400 return apReqMessg.apOptions; 401 return null; 402 } 403 404 /** 405 * Returns true if mutual authentication is required and hence an 406 * AP-REP will need to be generated. 407 * @throws KrbException 408 * @throws IOException 409 */ 410 public boolean getMutualAuthRequired() throws KrbException, IOException { 411 if (apReqMessg == null) 412 decode(); 413 if (apReqMessg != null) 414 return apReqMessg.apOptions.get(Krb5.AP_OPTS_MUTUAL_REQUIRED); 415 return false; 416 } 417 418 boolean useSessionKey() throws KrbException, IOException { 419 if (apReqMessg == null) 420 decode(); 421 if (apReqMessg != null) 422 return apReqMessg.apOptions.get(Krb5.AP_OPTS_USE_SESSION_KEY); 423 return false; 424 } 425 426 /** 427 * Returns the optional subkey stored in the Authenticator for 428 * this message. Returns null if none is stored. 429 */ 430 public EncryptionKey getSubKey() { 431 // XXX Can authenticator be null 432 return authenticator.getSubKey(); 433 } 434 435 /** 436 * Returns the optional sequence number stored in the 437 * Authenticator for this message. Returns null if none is 438 * stored. 439 */ 440 public Integer getSeqNumber() { 441 // XXX Can authenticator be null 442 return authenticator.getSeqNumber(); 443 } 444 445 /** 446 * Returns the optional Checksum stored in the 447 * Authenticator for this message. Returns null if none is 448 * stored. 449 */ 450 public Checksum getChecksum() { 451 return authenticator.getChecksum(); 452 } 453 454 /** 455 * Returns the ASN.1 encoding that should be sent to the peer. 456 */ 457 public byte[] getMessage() { 458 return obuf; 459 } 460 461 /** 462 * Returns the principal name of the client that generated this 463 * message. 464 */ 465 public PrincipalName getClient() { 466 return creds.getClient(); 467 } 468 469 private void createMessage(APOptions apOptions, 470 Ticket ticket, 471 EncryptionKey key, 472 PrincipalName cname, 473 Checksum cksum, 474 KerberosTime ctime, 475 EncryptionKey subKey, 476 SeqNumber seqNumber, 477 AuthorizationData authorizationData, 478 int usage) 479 throws Asn1Exception, IOException, 480 KdcErrException, KrbCryptoException { 481 482 Integer seqno = null; 483 484 if (seqNumber != null) 485 seqno = seqNumber.current(); 486 487 authenticator = 488 new Authenticator(cname, 489 cksum, 490 ctime.getMicroSeconds(), 491 ctime, 492 subKey, 493 seqno, 494 authorizationData); 495 496 byte[] temp = authenticator.asn1Encode(); 497 498 EncryptedData encAuthenticator = 499 new EncryptedData(key, temp, usage); 500 501 apReqMessg = 502 new APReq(apOptions, ticket, encAuthenticator); 503 } 504 505 // Check that key is one of the permitted types 506 private static void checkPermittedEType(int target) throws KrbException { 507 int[] etypes = EType.getDefaults("permitted_enctypes"); 508 if (!EType.isSupported(target, etypes)) { 509 throw new KrbException(EType.toString(target) + 510 " encryption type not in permitted_enctypes list"); 511 } 512 } 513 }