package sun.security.krb5.internal.ccache;
import sun.security.action.GetPropertyAction;
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 static java.nio.charset.StandardCharsets.ISO_8859_1;
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);
}
}
}
}
}
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;
}
public static String getDefaultCacheName() {
String stdCacheNameComponent = "krb5cc";
String name;
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 = GetPropertyAction.privilegedGetProperty("os.name");
if (osname != null && !osname.startsWith("Windows")) {
long uid = jdk.internal.misc.VM.getuid();
if (uid != -1) {
name = File.separator + "tmp" +
File.separator + stdCacheNameComponent + "_" + uid;
if (DEBUG) {
System.out.println(">>>KinitOptions cache name is " +
name);
}
return name;
} else {
if (DEBUG) {
System.out.println("Error in obtaining uid " +
"for Unix platforms " +
"Using user's home directory");
}
}
}
String user_name = GetPropertyAction.privilegedGetProperty("user.name");
String user_home = GetPropertyAction.privilegedGetProperty("user.home");
if (user_home == null) {
user_home = GetPropertyAction.privilegedGetProperty("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(), ISO_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;
}
}