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