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.ccache.CredentialsCache; 36import sun.security.krb5.internal.crypto.EType; 37import java.io.IOException; 38import java.util.Date; 39import java.util.Locale; 40import java.net.InetAddress; 41 42/** 43 * This class encapsulates the concept of a Kerberos service 44 * credential. That includes a Kerberos ticket and an associated 45 * session key. 46 */ 47public class Credentials { 48 49 Ticket ticket; 50 PrincipalName client; 51 PrincipalName server; 52 EncryptionKey key; 53 TicketFlags flags; 54 KerberosTime authTime; 55 KerberosTime startTime; 56 KerberosTime endTime; 57 KerberosTime renewTill; 58 HostAddresses cAddr; 59 EncryptionKey serviceKey; 60 AuthorizationData authzData; 61 private static boolean DEBUG = Krb5.DEBUG; 62 private static CredentialsCache cache; 63 static boolean alreadyLoaded = false; 64 private static boolean alreadyTried = false; 65 66 // Read native ticket with session key type in the given list 67 private static native Credentials acquireDefaultNativeCreds(int[] eTypes); 68 69 public Credentials(Ticket new_ticket, 70 PrincipalName new_client, 71 PrincipalName new_server, 72 EncryptionKey new_key, 73 TicketFlags new_flags, 74 KerberosTime authTime, 75 KerberosTime new_startTime, 76 KerberosTime new_endTime, 77 KerberosTime renewTill, 78 HostAddresses cAddr, 79 AuthorizationData authzData) { 80 this(new_ticket, new_client, new_server, new_key, new_flags, 81 authTime, new_startTime, new_endTime, renewTill, cAddr); 82 this.authzData = authzData; 83 } 84 85 public Credentials(Ticket new_ticket, 86 PrincipalName new_client, 87 PrincipalName new_server, 88 EncryptionKey new_key, 89 TicketFlags new_flags, 90 KerberosTime authTime, 91 KerberosTime new_startTime, 92 KerberosTime new_endTime, 93 KerberosTime renewTill, 94 HostAddresses cAddr) { 95 ticket = new_ticket; 96 client = new_client; 97 server = new_server; 98 key = new_key; 99 flags = new_flags; 100 this.authTime = authTime; 101 startTime = new_startTime; 102 endTime = new_endTime; 103 this.renewTill = renewTill; 104 this.cAddr = cAddr; 105 } 106 107 public Credentials(byte[] encoding, 108 String client, 109 String server, 110 byte[] keyBytes, 111 int keyType, 112 boolean[] flags, 113 Date authTime, 114 Date startTime, 115 Date endTime, 116 Date renewTill, 117 InetAddress[] cAddrs) throws KrbException, IOException { 118 this(new Ticket(encoding), 119 new PrincipalName(client, PrincipalName.KRB_NT_PRINCIPAL), 120 new PrincipalName(server, PrincipalName.KRB_NT_SRV_INST), 121 new EncryptionKey(keyType, keyBytes), 122 (flags == null? null: new TicketFlags(flags)), 123 (authTime == null? null: new KerberosTime(authTime)), 124 (startTime == null? null: new KerberosTime(startTime)), 125 (endTime == null? null: new KerberosTime(endTime)), 126 (renewTill == null? null: new KerberosTime(renewTill)), 127 null); // caddrs are in the encoding at this point 128 } 129 130 /** 131 * Acquires a service ticket for the specified service 132 * principal. If the service ticket is not already available, it 133 * obtains a new one from the KDC. 134 */ 135 /* 136 public Credentials(Credentials tgt, PrincipalName service) 137 throws KrbException { 138 } 139 */ 140 141 public final PrincipalName getClient() { 142 return client; 143 } 144 145 public final PrincipalName getServer() { 146 return server; 147 } 148 149 public final EncryptionKey getSessionKey() { 150 return key; 151 } 152 153 public final Date getAuthTime() { 154 if (authTime != null) { 155 return authTime.toDate(); 156 } else { 157 return null; 158 } 159 } 160 161 public final Date getStartTime() { 162 if (startTime != null) 163 { 164 return startTime.toDate(); 165 } 166 return null; 167 } 168 169 public final Date getEndTime() { 170 if (endTime != null) 171 { 172 return endTime.toDate(); 173 } 174 return null; 175 } 176 177 public final Date getRenewTill() { 178 if (renewTill != null) 179 { 180 return renewTill.toDate(); 181 } 182 return null; 183 } 184 185 public final boolean[] getFlags() { 186 if (flags == null) // Can be in a KRB-CRED 187 return null; 188 return flags.toBooleanArray(); 189 } 190 191 public final InetAddress[] getClientAddresses() { 192 193 if (cAddr == null) 194 return null; 195 196 return cAddr.getInetAddresses(); 197 } 198 199 public final byte[] getEncoded() { 200 byte[] retVal = null; 201 try { 202 retVal = ticket.asn1Encode(); 203 } catch (Asn1Exception e) { 204 if (DEBUG) 205 System.out.println(e); 206 } catch (IOException ioe) { 207 if (DEBUG) 208 System.out.println(ioe); 209 } 210 return retVal; 211 } 212 213 public boolean isForwardable() { 214 return flags.get(Krb5.TKT_OPTS_FORWARDABLE); 215 } 216 217 public boolean isRenewable() { 218 return flags.get(Krb5.TKT_OPTS_RENEWABLE); 219 } 220 221 public Ticket getTicket() { 222 return ticket; 223 } 224 225 public TicketFlags getTicketFlags() { 226 return flags; 227 } 228 229 public AuthorizationData getAuthzData() { 230 return authzData; 231 } 232 /** 233 * Checks if the service ticket returned by the KDC has the OK-AS-DELEGATE 234 * flag set 235 * @return true if OK-AS_DELEGATE flag is set, otherwise, return false. 236 */ 237 public boolean checkDelegate() { 238 return flags.get(Krb5.TKT_OPTS_DELEGATE); 239 } 240 241 /** 242 * Reset TKT_OPTS_DELEGATE to false, called at credentials acquirement 243 * when one of the cross-realm TGTs does not have the OK-AS-DELEGATE 244 * flag set. This info must be preservable and restorable through 245 * the Krb5Util.credsToTicket/ticketToCreds() methods so that even if 246 * the service ticket is cached it still remembers the cross-realm 247 * authentication result. 248 */ 249 public void resetDelegate() { 250 flags.set(Krb5.TKT_OPTS_DELEGATE, false); 251 } 252 253 public Credentials renew() throws KrbException, IOException { 254 KDCOptions options = new KDCOptions(); 255 options.set(KDCOptions.RENEW, true); 256 /* 257 * Added here to pass KrbKdcRep.check:73 258 */ 259 options.set(KDCOptions.RENEWABLE, true); 260 261 return new KrbTgsReq(options, 262 this, 263 server, 264 null, // from 265 null, // till 266 null, // rtime 267 null, // eTypes 268 cAddr, 269 null, 270 null, 271 null).sendAndGetCreds(); 272 } 273 274 /** 275 * Returns a TGT for the given client principal from a ticket cache. 276 * 277 * @param princ the client principal. A value of null means that the 278 * default principal name in the credentials cache will be used. 279 * @param ticketCache the path to the tickets file. A value 280 * of null will be accepted to indicate that the default 281 * path should be searched 282 * @return the TGT credentials or null if none were found. If the tgt 283 * expired, it is the responsibility of the caller to determine this. 284 */ 285 public static Credentials acquireTGTFromCache(PrincipalName princ, 286 String ticketCache) 287 throws KrbException, IOException { 288 289 if (ticketCache == null) { 290 // The default ticket cache on Windows and Mac is not a file. 291 String os = java.security.AccessController.doPrivileged( 292 new sun.security.action.GetPropertyAction("os.name")); 293 if (os.toUpperCase(Locale.ENGLISH).startsWith("WINDOWS") || 294 os.toUpperCase(Locale.ENGLISH).contains("OS X")) { 295 Credentials creds = acquireDefaultCreds(); 296 if (creds == null) { 297 if (DEBUG) { 298 System.out.println(">>> Found no TGT's in LSA"); 299 } 300 return null; 301 } 302 if (princ != null) { 303 if (creds.getClient().equals(princ)) { 304 if (DEBUG) { 305 System.out.println(">>> Obtained TGT from LSA: " 306 + creds); 307 } 308 return creds; 309 } else { 310 if (DEBUG) { 311 System.out.println(">>> LSA contains TGT for " 312 + creds.getClient() 313 + " not " 314 + princ); 315 } 316 return null; 317 } 318 } else { 319 if (DEBUG) { 320 System.out.println(">>> Obtained TGT from LSA: " 321 + creds); 322 } 323 return creds; 324 } 325 } 326 } 327 328 /* 329 * Returns the appropriate cache. If ticketCache is null, it is the 330 * default cache otherwise it is the cache filename contained in it. 331 */ 332 CredentialsCache ccache = 333 CredentialsCache.getInstance(princ, ticketCache); 334 335 if (ccache == null) { 336 return null; 337 } 338 339 sun.security.krb5.internal.ccache.Credentials tgtCred = 340 ccache.getDefaultCreds(); 341 342 if (tgtCred == null) { 343 return null; 344 } 345 346 if (EType.isSupported(tgtCred.getEType())) { 347 return tgtCred.setKrbCreds(); 348 } else { 349 if (DEBUG) { 350 System.out.println( 351 ">>> unsupported key type found the default TGT: " + 352 tgtCred.getEType()); 353 } 354 return null; 355 } 356 } 357 358 /** 359 * Acquires default credentials. 360 * <br>The possible locations for default credentials cache is searched in 361 * the following order: 362 * <ol> 363 * <li> The directory and cache file name specified by "KRB5CCNAME" system. 364 * property. 365 * <li> The directory and cache file name specified by "KRB5CCNAME" 366 * environment variable. 367 * <li> A cache file named krb5cc_{user.name} at {user.home} directory. 368 * </ol> 369 * @return a <code>KrbCreds</code> object if the credential is found, 370 * otherwise return null. 371 */ 372 373 // this method is intentionally changed to not check if the caller's 374 // principal name matches cache file's principal name. 375 // It assumes that the GSS call has 376 // the privilege to access the default cache file. 377 378 // This method is only called on Windows and Mac OS X, the native 379 // acquireDefaultNativeCreds is also available on these platforms. 380 public static synchronized Credentials acquireDefaultCreds() { 381 Credentials result = null; 382 383 if (cache == null) { 384 cache = CredentialsCache.getInstance(); 385 } 386 if (cache != null) { 387 sun.security.krb5.internal.ccache.Credentials temp = 388 cache.getDefaultCreds(); 389 if (temp != null) { 390 if (DEBUG) { 391 System.out.println(">>> KrbCreds found the default ticket" 392 + " granting ticket in credential cache."); 393 } 394 if (EType.isSupported(temp.getEType())) { 395 result = temp.setKrbCreds(); 396 } else { 397 if (DEBUG) { 398 System.out.println( 399 ">>> unsupported key type found the default TGT: " + 400 temp.getEType()); 401 } 402 } 403 } 404 } 405 if (result == null) { 406 // Doesn't seem to be a default cache on this system or 407 // TGT has unsupported encryption type 408 409 if (!alreadyTried) { 410 // See if there's any native code to load 411 try { 412 ensureLoaded(); 413 } catch (Exception e) { 414 if (DEBUG) { 415 System.out.println("Can not load credentials cache"); 416 e.printStackTrace(); 417 } 418 alreadyTried = true; 419 } 420 } 421 if (alreadyLoaded) { 422 // There is some native code 423 if (DEBUG) { 424 System.out.println(">> Acquire default native Credentials"); 425 } 426 try { 427 result = acquireDefaultNativeCreds( 428 EType.getDefaults("default_tkt_enctypes")); 429 } catch (KrbException ke) { 430 // when there is no default_tkt_enctypes. 431 } 432 } 433 } 434 return result; 435 } 436 437 /** 438 * Acquires credentials for a specified service using initial credential. 439 * When the service has a different realm 440 * from the initial credential, we do cross-realm authentication 441 * - first, we use the current credential to get 442 * a cross-realm credential from the local KDC, then use that 443 * cross-realm credential to request service credential 444 * from the foreigh KDC. 445 * 446 * @param service the name of service principal using format 447 * components@realm 448 * @param ccreds client's initial credential. 449 * @exception IOException if an error occurs in reading the credentials 450 * cache 451 * @exception KrbException if an error occurs specific to Kerberos 452 * @return a <code>Credentials</code> object. 453 */ 454 455 public static Credentials acquireServiceCreds(String service, 456 Credentials ccreds) 457 throws KrbException, IOException { 458 return CredentialsUtil.acquireServiceCreds(service, ccreds); 459 } 460 461 public static Credentials acquireS4U2selfCreds(PrincipalName user, 462 Credentials ccreds) throws KrbException, IOException { 463 return CredentialsUtil.acquireS4U2selfCreds(user, ccreds); 464 } 465 466 public static Credentials acquireS4U2proxyCreds(String service, 467 Ticket second, PrincipalName client, Credentials ccreds) 468 throws KrbException, IOException { 469 return CredentialsUtil.acquireS4U2proxyCreds( 470 service, second, client, ccreds); 471 } 472 473 public CredentialsCache getCache() { 474 return cache; 475 } 476 477 public EncryptionKey getServiceKey() { 478 return serviceKey; 479 } 480 481 /* 482 * Prints out debug info. 483 */ 484 public static void printDebug(Credentials c) { 485 System.out.println(">>> DEBUG: ----Credentials----"); 486 System.out.println("\tclient: " + c.client.toString()); 487 System.out.println("\tserver: " + c.server.toString()); 488 System.out.println("\tticket: sname: " + c.ticket.sname.toString()); 489 if (c.startTime != null) { 490 System.out.println("\tstartTime: " + c.startTime.getTime()); 491 } 492 System.out.println("\tendTime: " + c.endTime.getTime()); 493 System.out.println(" ----Credentials end----"); 494 } 495 496 497 static void ensureLoaded() { 498 java.security.AccessController.doPrivileged( 499 new java.security.PrivilegedAction<Void> () { 500 public Void run() { 501 if (System.getProperty("os.name").contains("OS X")) { 502 System.loadLibrary("osxkrb5"); 503 } else { 504 System.loadLibrary("w2k_lsa_auth"); 505 } 506 return null; 507 } 508 }); 509 alreadyLoaded = true; 510 } 511 512 public String toString() { 513 StringBuilder sb = new StringBuilder("Credentials:"); 514 sb.append( "\n client=").append(client); 515 sb.append( "\n server=").append(server); 516 if (authTime != null) { 517 sb.append("\n authTime=").append(authTime); 518 } 519 if (startTime != null) { 520 sb.append("\n startTime=").append(startTime); 521 } 522 sb.append( "\n endTime=").append(endTime); 523 sb.append( "\n renewTill=").append(renewTill); 524 sb.append( "\n flags=").append(flags); 525 sb.append( "\nEType (skey)=").append(key.getEType()); 526 sb.append( "\n (tkt key)=").append(ticket.encPart.eType); 527 return sb.toString(); 528 } 529 530 public sun.security.krb5.internal.ccache.Credentials toCCacheCreds() { 531 return new sun.security.krb5.internal.ccache.Credentials( 532 getClient(), getServer(), 533 getSessionKey(), 534 date2kt(getAuthTime()), 535 date2kt(getStartTime()), 536 date2kt(getEndTime()), 537 date2kt(getRenewTill()), 538 false, 539 flags, 540 new HostAddresses(getClientAddresses()), 541 getAuthzData(), 542 getTicket(), 543 null); 544 } 545 546 private static KerberosTime date2kt(Date d) { 547 return d == null ? null : new KerberosTime(d); 548 } 549} 550