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 * 30 * Copyright 1997 The Open Group Research Institute. All rights reserved. 31 * =========================================================================== 32 * 33 */ 34package sun.security.krb5.internal.ccache; 35 36import sun.security.krb5.*; 37import sun.security.krb5.internal.*; 38import java.util.StringTokenizer; 39import java.util.Vector; 40import java.io.IOException; 41import java.io.File; 42import java.io.FileInputStream; 43import java.io.FileOutputStream; 44import java.io.BufferedReader; 45import java.io.InputStreamReader; 46 47/** 48 * CredentialsCache stores credentials(tickets, session keys, etc) in a 49 * semi-permanent store 50 * for later use by different program. 51 * 52 * @author Yanni Zhang 53 * @author Ram Marti 54 */ 55 56public class FileCredentialsCache extends CredentialsCache 57 implements FileCCacheConstants { 58 public int version; 59 public Tag tag; // optional 60 public PrincipalName primaryPrincipal; 61 private Vector<Credentials> credentialsList; 62 private static String dir; 63 private static boolean DEBUG = Krb5.DEBUG; 64 65 public static synchronized FileCredentialsCache acquireInstance( 66 PrincipalName principal, String cache) { 67 try { 68 FileCredentialsCache fcc = new FileCredentialsCache(); 69 if (cache == null) { 70 cacheName = FileCredentialsCache.getDefaultCacheName(); 71 } else { 72 cacheName = FileCredentialsCache.checkValidation(cache); 73 } 74 if ((cacheName == null) || !(new File(cacheName)).exists()) { 75 // invalid cache name or the file doesn't exist 76 return null; 77 } 78 if (principal != null) { 79 fcc.primaryPrincipal = principal; 80 } 81 fcc.load(cacheName); 82 return fcc; 83 } catch (IOException e) { 84 // we don't handle it now, instead we return a null at the end. 85 if (DEBUG) { 86 e.printStackTrace(); 87 } 88 } catch (KrbException e) { 89 // we don't handle it now, instead we return a null at the end. 90 if (DEBUG) { 91 e.printStackTrace(); 92 } 93 } 94 return null; 95 } 96 97 public static FileCredentialsCache acquireInstance() { 98 return acquireInstance(null, null); 99 } 100 101 static synchronized FileCredentialsCache New(PrincipalName principal, 102 String name) { 103 try { 104 FileCredentialsCache fcc = new FileCredentialsCache(); 105 cacheName = FileCredentialsCache.checkValidation(name); 106 if (cacheName == null) { 107 // invalid cache name or the file doesn't exist 108 return null; 109 } 110 fcc.init(principal, cacheName); 111 return fcc; 112 } 113 catch (IOException e) { 114 } 115 catch (KrbException e) { 116 } 117 return null; 118 } 119 120 static synchronized FileCredentialsCache New(PrincipalName principal) { 121 try { 122 FileCredentialsCache fcc = new FileCredentialsCache(); 123 cacheName = FileCredentialsCache.getDefaultCacheName(); 124 fcc.init(principal, cacheName); 125 return fcc; 126 } 127 catch (IOException e) { 128 if (DEBUG) { 129 e.printStackTrace(); 130 } 131 } catch (KrbException e) { 132 if (DEBUG) { 133 e.printStackTrace(); 134 } 135 136 } 137 return null; 138 } 139 140 private FileCredentialsCache() { 141 } 142 143 boolean exists(String cache) { 144 File file = new File(cache); 145 if (file.exists()) { 146 return true; 147 } else return false; 148 } 149 150 synchronized void init(PrincipalName principal, String name) 151 throws IOException, KrbException { 152 primaryPrincipal = principal; 153 try (FileOutputStream fos = new FileOutputStream(name); 154 CCacheOutputStream cos = new CCacheOutputStream(fos)) { 155 version = KRB5_FCC_FVNO_3; 156 cos.writeHeader(primaryPrincipal, version); 157 } 158 load(name); 159 } 160 161 synchronized void load(String name) throws IOException, KrbException { 162 PrincipalName p; 163 try (FileInputStream fis = new FileInputStream(name); 164 CCacheInputStream cis = new CCacheInputStream(fis)) { 165 version = cis.readVersion(); 166 if (version == KRB5_FCC_FVNO_4) { 167 tag = cis.readTag(); 168 } else { 169 tag = null; 170 if (version == KRB5_FCC_FVNO_1 || version == KRB5_FCC_FVNO_2) { 171 cis.setNativeByteOrder(); 172 } 173 } 174 p = cis.readPrincipal(version); 175 176 if (primaryPrincipal != null) { 177 if (!(primaryPrincipal.match(p))) { 178 throw new IOException("Primary principals don't match."); 179 } 180 } else 181 primaryPrincipal = p; 182 credentialsList = new Vector<Credentials>(); 183 while (cis.available() > 0) { 184 Credentials cred = cis.readCred(version); 185 if (cred != null) { 186 credentialsList.addElement(cred); 187 } 188 } 189 } 190 } 191 192 193 /** 194 * Updates the credentials list. If the specified credentials for the 195 * service is new, add it to the list. If there is an entry in the list, 196 * replace the old credentials with the new one. 197 * @param c the credentials. 198 */ 199 200 public synchronized void update(Credentials c) { 201 if (credentialsList != null) { 202 if (credentialsList.isEmpty()) { 203 credentialsList.addElement(c); 204 } else { 205 Credentials tmp = null; 206 boolean matched = false; 207 208 for (int i = 0; i < credentialsList.size(); i++) { 209 tmp = credentialsList.elementAt(i); 210 if (match(c.sname.getNameStrings(), 211 tmp.sname.getNameStrings()) && 212 ((c.sname.getRealmString()).equalsIgnoreCase( 213 tmp.sname.getRealmString()))) { 214 matched = true; 215 if (c.endtime.getTime() >= tmp.endtime.getTime()) { 216 if (DEBUG) { 217 System.out.println(" >>> FileCredentialsCache " 218 + "Ticket matched, overwrite " 219 + "the old one."); 220 } 221 credentialsList.removeElementAt(i); 222 credentialsList.addElement(c); 223 } 224 } 225 } 226 if (matched == false) { 227 if (DEBUG) { 228 System.out.println(" >>> FileCredentialsCache Ticket " 229 + "not exactly matched, " 230 + "add new one into cache."); 231 } 232 233 credentialsList.addElement(c); 234 } 235 } 236 } 237 } 238 239 public synchronized PrincipalName getPrimaryPrincipal() { 240 return primaryPrincipal; 241 } 242 243 244 /** 245 * Saves the credentials cache file to the disk. 246 */ 247 public synchronized void save() throws IOException, Asn1Exception { 248 try (FileOutputStream fos = new FileOutputStream(cacheName); 249 CCacheOutputStream cos = new CCacheOutputStream(fos)) { 250 cos.writeHeader(primaryPrincipal, version); 251 Credentials[] tmp = null; 252 if ((tmp = getCredsList()) != null) { 253 for (int i = 0; i < tmp.length; i++) { 254 cos.addCreds(tmp[i]); 255 } 256 } 257 } 258 } 259 260 boolean match(String[] s1, String[] s2) { 261 if (s1.length != s2.length) { 262 return false; 263 } else { 264 for (int i = 0; i < s1.length; i++) { 265 if (!(s1[i].equalsIgnoreCase(s2[i]))) { 266 return false; 267 } 268 } 269 } 270 return true; 271 } 272 273 /** 274 * Returns the list of credentials entries in the cache file. 275 */ 276 public synchronized Credentials[] getCredsList() { 277 if ((credentialsList == null) || (credentialsList.isEmpty())) { 278 return null; 279 } else { 280 Credentials[] tmp = new Credentials[credentialsList.size()]; 281 for (int i = 0; i < credentialsList.size(); i++) { 282 tmp[i] = credentialsList.elementAt(i); 283 } 284 return tmp; 285 } 286 287 } 288 289 public Credentials getCreds(LoginOptions options, PrincipalName sname) { 290 if (options == null) { 291 return getCreds(sname); 292 } else { 293 Credentials[] list = getCredsList(); 294 if (list == null) { 295 return null; 296 } else { 297 for (int i = 0; i < list.length; i++) { 298 if (sname.match(list[i].sname)) { 299 if (list[i].flags.match(options)) { 300 return list[i]; 301 } 302 } 303 } 304 } 305 return null; 306 } 307 } 308 309 310 /** 311 * Gets a credentials for a specified service. 312 * @param sname service principal name. 313 */ 314 public Credentials getCreds(PrincipalName sname) { 315 Credentials[] list = getCredsList(); 316 if (list == null) { 317 return null; 318 } else { 319 for (int i = 0; i < list.length; i++) { 320 if (sname.match(list[i].sname)) { 321 return list[i]; 322 } 323 } 324 } 325 return null; 326 } 327 328 public Credentials getDefaultCreds() { 329 Credentials[] list = getCredsList(); 330 if (list == null) { 331 return null; 332 } else { 333 for (int i = list.length-1; i >= 0; i--) { 334 if (list[i].sname.toString().startsWith("krbtgt")) { 335 String[] nameStrings = list[i].sname.getNameStrings(); 336 // find the TGT for the current realm krbtgt/realm@realm 337 if (nameStrings[1].equals(list[i].sname.getRealm().toString())) { 338 return list[i]; 339 } 340 } 341 } 342 } 343 return null; 344 } 345 346 /* 347 * Returns path name of the credentials cache file. 348 * The path name is searched in the following order: 349 * 350 * 1. KRB5CCNAME (bare file name without FILE:) 351 * 2. /tmp/krb5cc_<uid> on unix systems 352 * 3. <user.home>/krb5cc_<user.name> 353 * 4. <user.home>/krb5cc (if can't get <user.name>) 354 */ 355 356 public static String getDefaultCacheName() { 357 358 String stdCacheNameComponent = "krb5cc"; 359 String name; 360 361 // The env var can start with TYPE:, we only support FILE: here. 362 // http://docs.oracle.com/cd/E19082-01/819-2252/6n4i8rtr3/index.html 363 name = java.security.AccessController.doPrivileged( 364 new java.security.PrivilegedAction<String>() { 365 @Override 366 public String run() { 367 String cache = System.getenv("KRB5CCNAME"); 368 if (cache != null && 369 (cache.length() >= 5) && 370 cache.regionMatches(true, 0, "FILE:", 0, 5)) { 371 cache = cache.substring(5); 372 } 373 return cache; 374 } 375 }); 376 if (name != null) { 377 if (DEBUG) { 378 System.out.println(">>>KinitOptions cache name is " + name); 379 } 380 return name; 381 } 382 383 // get cache name from system.property 384 String osname = 385 java.security.AccessController.doPrivileged( 386 new sun.security.action.GetPropertyAction("os.name")); 387 388 /* 389 * For Unix platforms we use the default cache name to be 390 * /tmp/krb5cc_uid ; for all other platforms we use 391 * {user_home}/krb5cc_{user_name} 392 * Please note that for Windows we will use LSA to get 393 * the TGT from the default cache even before we come here; 394 * however when we create cache we will create a cache under 395 * {user_home}/krb5cc_{user_name} for non-Unix platforms including 396 * Windows. 397 */ 398 399 if (osname != null && !osname.startsWith("Windows")) { 400 long uid = jdk.internal.misc.VM.getuid(); 401 if (uid != -1) { 402 name = File.separator + "tmp" + 403 File.separator + stdCacheNameComponent + "_" + uid; 404 if (DEBUG) { 405 System.out.println(">>>KinitOptions cache name is " + 406 name); 407 } 408 return name; 409 } else { 410 if (DEBUG) { 411 System.out.println("Error in obtaining uid " + 412 "for Unix platforms " + 413 "Using user's home directory"); 414 } 415 } 416 } 417 418 // we did not get the uid; 419 420 String user_name = 421 java.security.AccessController.doPrivileged( 422 new sun.security.action.GetPropertyAction("user.name")); 423 424 String user_home = 425 java.security.AccessController.doPrivileged( 426 new sun.security.action.GetPropertyAction("user.home")); 427 428 if (user_home == null) { 429 user_home = 430 java.security.AccessController.doPrivileged( 431 new sun.security.action.GetPropertyAction("user.dir")); 432 } 433 434 if (user_name != null) { 435 name = user_home + File.separator + 436 stdCacheNameComponent + "_" + user_name; 437 } else { 438 name = user_home + File.separator + stdCacheNameComponent; 439 } 440 441 if (DEBUG) { 442 System.out.println(">>>KinitOptions cache name is " + name); 443 } 444 445 return name; 446 } 447 448 public static String checkValidation(String name) { 449 String fullname = null; 450 if (name == null) { 451 return null; 452 } 453 try { 454 // get full path name 455 fullname = (new File(name)).getCanonicalPath(); 456 File fCheck = new File(fullname); 457 if (!(fCheck.exists())) { 458 // get absolute directory 459 File temp = new File(fCheck.getParent()); 460 // test if the directory exists 461 if (!(temp.isDirectory())) 462 fullname = null; 463 temp = null; 464 } 465 fCheck = null; 466 467 } catch (IOException e) { 468 fullname = null; // invalid name 469 } 470 return fullname; 471 } 472 473 474 private static String exec(String c) { 475 StringTokenizer st = new StringTokenizer(c); 476 Vector<String> v = new Vector<>(); 477 while (st.hasMoreTokens()) { 478 v.addElement(st.nextToken()); 479 } 480 final String[] command = new String[v.size()]; 481 v.copyInto(command); 482 try { 483 484 Process p = 485 java.security.AccessController.doPrivileged 486 (new java.security.PrivilegedAction<Process> () { 487 public Process run() { 488 try { 489 return (Runtime.getRuntime().exec(command)); 490 } catch (java.io.IOException e) { 491 if (DEBUG) { 492 e.printStackTrace(); 493 } 494 return null; 495 } 496 } 497 }); 498 if (p == null) { 499 // exception occurred during executing the command 500 return null; 501 } 502 503 BufferedReader commandResult = 504 new BufferedReader 505 (new InputStreamReader(p.getInputStream(), "8859_1")); 506 String s1 = null; 507 if ((command.length == 1) && 508 (command[0].equals("/usr/bin/env"))) { 509 while ((s1 = commandResult.readLine()) != null) { 510 if (s1.length() >= 11) { 511 if ((s1.substring(0, 11)).equalsIgnoreCase 512 ("KRB5CCNAME=")) { 513 s1 = s1.substring(11); 514 break; 515 } 516 } 517 } 518 } else s1 = commandResult.readLine(); 519 commandResult.close(); 520 return s1; 521 } catch (Exception e) { 522 if (DEBUG) { 523 e.printStackTrace(); 524 } 525 } 526 return null; 527 } 528} 529