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