|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
package sun.security.krb5.internal.ccache; |
|
|
|
import sun.security.krb5.*; |
|
import sun.security.krb5.internal.*; |
|
import sun.security.util.SecurityProperties; |
|
|
|
import java.nio.charset.StandardCharsets; |
|
import java.util.ArrayList; |
|
import java.util.Collections; |
|
import java.util.List; |
|
import java.util.StringTokenizer; |
|
import java.util.Vector; |
|
import java.io.IOException; |
|
import java.io.File; |
|
import java.io.FileInputStream; |
|
import java.io.FileOutputStream; |
|
import java.io.BufferedReader; |
|
import java.io.InputStreamReader; |
|
import java.lang.reflect.*; |
|
|
|
/** |
|
* CredentialsCache stores credentials(tickets, session keys, etc) in a |
|
* semi-permanent store |
|
* for later use by different program. |
|
* |
|
* @author Yanni Zhang |
|
* @author Ram Marti |
|
*/ |
|
|
|
public class FileCredentialsCache extends CredentialsCache |
|
implements FileCCacheConstants { |
|
public int version; |
|
public Tag tag; |
|
public PrincipalName primaryPrincipal; |
|
private Vector<Credentials> credentialsList; |
|
private static String dir; |
|
private static boolean DEBUG = Krb5.DEBUG; |
|
|
|
public static synchronized FileCredentialsCache acquireInstance( |
|
PrincipalName principal, String cache) { |
|
try { |
|
FileCredentialsCache fcc = new FileCredentialsCache(); |
|
if (cache == null) { |
|
cacheName = FileCredentialsCache.getDefaultCacheName(); |
|
} else { |
|
cacheName = FileCredentialsCache.checkValidation(cache); |
|
} |
|
if ((cacheName == null) || !(new File(cacheName)).exists()) { |
|
|
|
return null; |
|
} |
|
if (principal != null) { |
|
fcc.primaryPrincipal = principal; |
|
} |
|
fcc.load(cacheName); |
|
return fcc; |
|
} catch (IOException e) { |
|
|
|
if (DEBUG) { |
|
e.printStackTrace(); |
|
} |
|
} catch (KrbException e) { |
|
|
|
if (DEBUG) { |
|
e.printStackTrace(); |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
public static FileCredentialsCache acquireInstance() { |
|
return acquireInstance(null, null); |
|
} |
|
|
|
static synchronized FileCredentialsCache New(PrincipalName principal, |
|
String name) { |
|
try { |
|
FileCredentialsCache fcc = new FileCredentialsCache(); |
|
cacheName = FileCredentialsCache.checkValidation(name); |
|
if (cacheName == null) { |
|
|
|
return null; |
|
} |
|
fcc.init(principal, cacheName); |
|
return fcc; |
|
} |
|
catch (IOException e) { |
|
} |
|
catch (KrbException e) { |
|
} |
|
return null; |
|
} |
|
|
|
static synchronized FileCredentialsCache New(PrincipalName principal) { |
|
try { |
|
FileCredentialsCache fcc = new FileCredentialsCache(); |
|
cacheName = FileCredentialsCache.getDefaultCacheName(); |
|
fcc.init(principal, cacheName); |
|
return fcc; |
|
} |
|
catch (IOException e) { |
|
if (DEBUG) { |
|
e.printStackTrace(); |
|
} |
|
} catch (KrbException e) { |
|
if (DEBUG) { |
|
e.printStackTrace(); |
|
} |
|
|
|
} |
|
return null; |
|
} |
|
|
|
private FileCredentialsCache() { |
|
} |
|
|
|
boolean exists(String cache) { |
|
File file = new File(cache); |
|
if (file.exists()) { |
|
return true; |
|
} else return false; |
|
} |
|
|
|
synchronized void init(PrincipalName principal, String name) |
|
throws IOException, KrbException { |
|
primaryPrincipal = principal; |
|
try (FileOutputStream fos = new FileOutputStream(name); |
|
CCacheOutputStream cos = new CCacheOutputStream(fos)) { |
|
version = KRB5_FCC_FVNO_3; |
|
cos.writeHeader(primaryPrincipal, version); |
|
} |
|
load(name); |
|
} |
|
|
|
synchronized void load(String name) throws IOException, KrbException { |
|
PrincipalName p; |
|
try (FileInputStream fis = new FileInputStream(name); |
|
CCacheInputStream cis = new CCacheInputStream(fis)) { |
|
version = cis.readVersion(); |
|
if (version == KRB5_FCC_FVNO_4) { |
|
tag = cis.readTag(); |
|
} else { |
|
tag = null; |
|
if (version == KRB5_FCC_FVNO_1 || version == KRB5_FCC_FVNO_2) { |
|
cis.setNativeByteOrder(); |
|
} |
|
} |
|
p = cis.readPrincipal(version); |
|
|
|
if (primaryPrincipal != null) { |
|
if (!(primaryPrincipal.match(p))) { |
|
throw new IOException("Primary principals don't match."); |
|
} |
|
} else |
|
primaryPrincipal = p; |
|
credentialsList = new Vector<Credentials>(); |
|
while (cis.available() > 0) { |
|
Object cred = cis.readCred(version); |
|
if (cred != null) { |
|
if (cred instanceof Credentials) { |
|
credentialsList.addElement((Credentials)cred); |
|
} else { |
|
addConfigEntry((CredentialsCache.ConfigEntry)cred); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
/** |
|
* Updates the credentials list. If the specified credentials for the |
|
* service is new, add it to the list. If there is an entry in the list, |
|
* replace the old credentials with the new one. |
|
* @param c the credentials. |
|
*/ |
|
|
|
public synchronized void update(Credentials c) { |
|
if (credentialsList != null) { |
|
if (credentialsList.isEmpty()) { |
|
credentialsList.addElement(c); |
|
} else { |
|
Credentials tmp = null; |
|
boolean matched = false; |
|
|
|
for (int i = 0; i < credentialsList.size(); i++) { |
|
tmp = credentialsList.elementAt(i); |
|
if (match(c.sname.getNameStrings(), |
|
tmp.sname.getNameStrings()) && |
|
((c.sname.getRealmString()).equalsIgnoreCase( |
|
tmp.sname.getRealmString()))) { |
|
matched = true; |
|
if (c.endtime.getTime() >= tmp.endtime.getTime()) { |
|
if (DEBUG) { |
|
System.out.println(" >>> FileCredentialsCache " |
|
+ "Ticket matched, overwrite " |
|
+ "the old one."); |
|
} |
|
credentialsList.removeElementAt(i); |
|
credentialsList.addElement(c); |
|
} |
|
} |
|
} |
|
if (matched == false) { |
|
if (DEBUG) { |
|
System.out.println(" >>> FileCredentialsCache Ticket " |
|
+ "not exactly matched, " |
|
+ "add new one into cache."); |
|
} |
|
|
|
credentialsList.addElement(c); |
|
} |
|
} |
|
} |
|
} |
|
|
|
public synchronized PrincipalName getPrimaryPrincipal() { |
|
return primaryPrincipal; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public synchronized void save() throws IOException, Asn1Exception { |
|
try (FileOutputStream fos = new FileOutputStream(cacheName); |
|
CCacheOutputStream cos = new CCacheOutputStream(fos)) { |
|
cos.writeHeader(primaryPrincipal, version); |
|
Credentials[] tmp = null; |
|
if ((tmp = getCredsList()) != null) { |
|
for (int i = 0; i < tmp.length; i++) { |
|
cos.addCreds(tmp[i]); |
|
} |
|
} |
|
for (ConfigEntry e : getConfigEntries()) { |
|
cos.addConfigEntry(primaryPrincipal, e); |
|
} |
|
} |
|
} |
|
|
|
boolean match(String[] s1, String[] s2) { |
|
if (s1.length != s2.length) { |
|
return false; |
|
} else { |
|
for (int i = 0; i < s1.length; i++) { |
|
if (!(s1[i].equalsIgnoreCase(s2[i]))) { |
|
return false; |
|
} |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public synchronized Credentials[] getCredsList() { |
|
if ((credentialsList == null) || (credentialsList.isEmpty())) { |
|
return null; |
|
} else { |
|
Credentials[] tmp = new Credentials[credentialsList.size()]; |
|
for (int i = 0; i < credentialsList.size(); i++) { |
|
tmp[i] = credentialsList.elementAt(i); |
|
} |
|
return tmp; |
|
} |
|
|
|
} |
|
|
|
public Credentials getCreds(LoginOptions options, PrincipalName sname) { |
|
if (options == null) { |
|
return getCreds(sname); |
|
} else { |
|
Credentials[] list = getCredsList(); |
|
if (list == null) { |
|
return null; |
|
} else { |
|
for (int i = 0; i < list.length; i++) { |
|
if (sname.match(list[i].sname)) { |
|
if (list[i].flags.match(options)) { |
|
return list[i]; |
|
} |
|
} |
|
} |
|
} |
|
return null; |
|
} |
|
} |
|
|
|
private List<ConfigEntry> configEntries = new ArrayList<>(); |
|
|
|
@Override |
|
public void addConfigEntry(ConfigEntry e) { |
|
configEntries.add(e); |
|
} |
|
|
|
@Override |
|
public List<ConfigEntry> getConfigEntries() { |
|
return Collections.unmodifiableList(configEntries); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public Credentials getCreds(PrincipalName sname) { |
|
Credentials[] list = getCredsList(); |
|
if (list == null) { |
|
return null; |
|
} else { |
|
for (int i = 0; i < list.length; i++) { |
|
if (sname.match(list[i].sname)) { |
|
return list[i]; |
|
} |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
public sun.security.krb5.Credentials getInitialCreds() { |
|
|
|
Credentials defaultCreds = getDefaultCreds(); |
|
if (defaultCreds == null) { |
|
return null; |
|
} |
|
sun.security.krb5.Credentials tgt = defaultCreds.setKrbCreds(); |
|
|
|
CredentialsCache.ConfigEntry entry = getConfigEntry("proxy_impersonator"); |
|
if (entry == null) { |
|
if (DEBUG) { |
|
System.out.println("get normal credential"); |
|
} |
|
return tgt; |
|
} |
|
|
|
boolean force; |
|
String prop = SecurityProperties.privilegedGetOverridable( |
|
"jdk.security.krb5.default.initiate.credential"); |
|
if (prop == null) { |
|
prop = "always-impersonate"; |
|
} |
|
switch (prop) { |
|
case "no-impersonate": |
|
if (DEBUG) { |
|
System.out.println("get normal credential"); |
|
} |
|
return tgt; |
|
case "try-impersonate": |
|
force = false; |
|
break; |
|
case "always-impersonate": |
|
force = true; |
|
break; |
|
default: |
|
throw new RuntimeException( |
|
"Invalid jdk.security.krb5.default.initiate.credential"); |
|
} |
|
|
|
try { |
|
PrincipalName service = new PrincipalName( |
|
new String(entry.getData(), StandardCharsets.UTF_8)); |
|
if (!tgt.getClient().equals(service)) { |
|
if (DEBUG) { |
|
System.out.println("proxy_impersonator does not match service name"); |
|
} |
|
return force ? null : tgt; |
|
} |
|
PrincipalName client = getPrimaryPrincipal(); |
|
Credentials proxy = null; |
|
for (Credentials c : getCredsList()) { |
|
if (c.getClientPrincipal().equals(client) |
|
&& c.getServicePrincipal().equals(service)) { |
|
proxy = c; |
|
break; |
|
} |
|
} |
|
if (proxy == null) { |
|
if (DEBUG) { |
|
System.out.println("Cannot find evidence ticket in ccache"); |
|
} |
|
return force ? null : tgt; |
|
} |
|
if (DEBUG) { |
|
System.out.println("Get proxied credential"); |
|
} |
|
return tgt.setProxy(proxy.setKrbCreds()); |
|
} catch (KrbException e) { |
|
if (DEBUG) { |
|
System.out.println("Impersonation with ccache failed"); |
|
} |
|
return force ? null : tgt; |
|
} |
|
} |
|
|
|
public Credentials getDefaultCreds() { |
|
Credentials[] list = getCredsList(); |
|
if (list == null) { |
|
return null; |
|
} else { |
|
for (int i = list.length-1; i >= 0; i--) { |
|
if (list[i].sname.toString().startsWith("krbtgt")) { |
|
String[] nameStrings = list[i].sname.getNameStrings(); |
|
|
|
if (nameStrings[1].equals(list[i].sname.getRealm().toString())) { |
|
return list[i]; |
|
} |
|
} |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
/* |
|
* Returns path name of the credentials cache file. |
|
* The path name is searched in the following order: |
|
* |
|
* 1. KRB5CCNAME (bare file name without FILE:) |
|
* 2. /tmp/krb5cc_<uid> on unix systems |
|
* 3. <user.home>/krb5cc_<user.name> |
|
* 4. <user.home>/krb5cc (if can't get <user.name>) |
|
*/ |
|
|
|
public static String getDefaultCacheName() { |
|
|
|
String stdCacheNameComponent = "krb5cc"; |
|
String name; |
|
|
|
// The env var can start with TYPE:, we only support FILE: here. |
|
|
|
name = java.security.AccessController.doPrivileged( |
|
new java.security.PrivilegedAction<String>() { |
|
@Override |
|
public String run() { |
|
String cache = System.getenv("KRB5CCNAME"); |
|
if (cache != null && |
|
(cache.length() >= 5) && |
|
cache.regionMatches(true, 0, "FILE:", 0, 5)) { |
|
cache = cache.substring(5); |
|
} |
|
return cache; |
|
} |
|
}); |
|
if (name != null) { |
|
if (DEBUG) { |
|
System.out.println(">>>KinitOptions cache name is " + name); |
|
} |
|
return name; |
|
} |
|
|
|
|
|
String osname = |
|
java.security.AccessController.doPrivileged( |
|
new sun.security.action.GetPropertyAction("os.name")); |
|
|
|
/* |
|
* For Unix platforms we use the default cache name to be |
|
* /tmp/krbcc_uid ; for all other platforms we use |
|
* {user_home}/krb5_cc{user_name} |
|
* Please note that for Windows 2K we will use LSA to get |
|
* the TGT from the the default cache even before we come here; |
|
* however when we create cache we will create a cache under |
|
* {user_home}/krb5_cc{user_name} for non-Unix platforms including |
|
* Windows 2K. |
|
*/ |
|
|
|
if (osname != null) { |
|
String cmd = null; |
|
String uidStr = null; |
|
long uid = 0; |
|
|
|
if (!osname.startsWith("Windows")) { |
|
try { |
|
Class<?> c = Class.forName |
|
("com.sun.security.auth.module.UnixSystem"); |
|
Constructor<?> constructor = c.getConstructor(); |
|
Object obj = constructor.newInstance(); |
|
Method method = c.getMethod("getUid"); |
|
uid = ((Long)method.invoke(obj)).longValue(); |
|
name = File.separator + "tmp" + |
|
File.separator + stdCacheNameComponent + "_" + uid; |
|
if (DEBUG) { |
|
System.out.println(">>>KinitOptions cache name is " + |
|
name); |
|
} |
|
return name; |
|
} catch (Exception e) { |
|
if (DEBUG) { |
|
System.out.println("Exception in obtaining uid " + |
|
"for Unix platforms " + |
|
"Using user's home directory"); |
|
|
|
|
|
e.printStackTrace(); |
|
} |
|
} |
|
} |
|
} |
|
|
|
// we did not get the uid; |
|
|
|
|
|
String user_name = |
|
java.security.AccessController.doPrivileged( |
|
new sun.security.action.GetPropertyAction("user.name")); |
|
|
|
String user_home = |
|
java.security.AccessController.doPrivileged( |
|
new sun.security.action.GetPropertyAction("user.home")); |
|
|
|
if (user_home == null) { |
|
user_home = |
|
java.security.AccessController.doPrivileged( |
|
new sun.security.action.GetPropertyAction("user.dir")); |
|
} |
|
|
|
if (user_name != null) { |
|
name = user_home + File.separator + |
|
stdCacheNameComponent + "_" + user_name; |
|
} else { |
|
name = user_home + File.separator + stdCacheNameComponent; |
|
} |
|
|
|
if (DEBUG) { |
|
System.out.println(">>>KinitOptions cache name is " + name); |
|
} |
|
|
|
return name; |
|
} |
|
|
|
public static String checkValidation(String name) { |
|
String fullname = null; |
|
if (name == null) { |
|
return null; |
|
} |
|
try { |
|
|
|
fullname = (new File(name)).getCanonicalPath(); |
|
File fCheck = new File(fullname); |
|
if (!(fCheck.exists())) { |
|
|
|
File temp = new File(fCheck.getParent()); |
|
|
|
if (!(temp.isDirectory())) |
|
fullname = null; |
|
temp = null; |
|
} |
|
fCheck = null; |
|
|
|
} catch (IOException e) { |
|
fullname = null; |
|
} |
|
return fullname; |
|
} |
|
|
|
|
|
private static String exec(String c) { |
|
StringTokenizer st = new StringTokenizer(c); |
|
Vector<String> v = new Vector<>(); |
|
while (st.hasMoreTokens()) { |
|
v.addElement(st.nextToken()); |
|
} |
|
final String[] command = new String[v.size()]; |
|
v.copyInto(command); |
|
try { |
|
|
|
Process p = |
|
java.security.AccessController.doPrivileged |
|
(new java.security.PrivilegedAction<Process> () { |
|
public Process run() { |
|
try { |
|
return (Runtime.getRuntime().exec(command)); |
|
} catch (java.io.IOException e) { |
|
if (DEBUG) { |
|
e.printStackTrace(); |
|
} |
|
return null; |
|
} |
|
} |
|
}); |
|
if (p == null) { |
|
|
|
return null; |
|
} |
|
|
|
BufferedReader commandResult = |
|
new BufferedReader |
|
(new InputStreamReader(p.getInputStream(), "8859_1")); |
|
String s1 = null; |
|
if ((command.length == 1) && |
|
(command[0].equals("/usr/bin/env"))) { |
|
while ((s1 = commandResult.readLine()) != null) { |
|
if (s1.length() >= 11) { |
|
if ((s1.substring(0, 11)).equalsIgnoreCase |
|
("KRB5CCNAME=")) { |
|
s1 = s1.substring(11); |
|
break; |
|
} |
|
} |
|
} |
|
} else s1 = commandResult.readLine(); |
|
commandResult.close(); |
|
return s1; |
|
} catch (Exception e) { |
|
if (DEBUG) { |
|
e.printStackTrace(); |
|
} |
|
} |
|
return null; |
|
} |
|
} |