|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
/* |
|
* |
|
* (C) Copyright IBM Corp. 1999 All Rights Reserved. |
|
* Copyright 1997 The Open Group Research Institute. All rights reserved. |
|
*/ |
|
|
|
package sun.security.krb5.internal; |
|
|
|
import sun.security.krb5.*; |
|
import sun.security.util.DerValue; |
|
|
|
import java.io.IOException; |
|
import java.util.LinkedList; |
|
import java.util.List; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public class CredentialsUtil { |
|
|
|
private static boolean DEBUG = sun.security.krb5.internal.Krb5.DEBUG; |
|
|
|
private static enum S4U2Type { |
|
NONE, SELF, PROXY |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static Credentials acquireS4U2selfCreds(PrincipalName client, |
|
Credentials ccreds) throws KrbException, IOException { |
|
if (!ccreds.isForwardable()) { |
|
throw new KrbException("S4U2self needs a FORWARDABLE ticket"); |
|
} |
|
PrincipalName sname = ccreds.getClient(); |
|
String uRealm = client.getRealmString(); |
|
String localRealm = ccreds.getClient().getRealmString(); |
|
if (!uRealm.equals(localRealm)) { |
|
// Referrals will be required because the middle service |
|
|
|
if (Config.DISABLE_REFERRALS) { |
|
throw new KrbException("Cross-realm S4U2Self request not" + |
|
" possible when referrals are disabled."); |
|
} |
|
if (ccreds.getClientAlias() != null) { |
|
// If the name was canonicalized, the user pick |
|
// has preference. This gives the possibility of |
|
// using FQDNs that KDCs may use to return referrals. |
|
// I.e.: a SVC/host.realm-2.com@REALM-1.COM name |
|
// may be used by REALM-1.COM KDC to return a |
|
|
|
sname = ccreds.getClientAlias(); |
|
} |
|
sname = new PrincipalName(sname.getNameType(), |
|
sname.getNameStrings(), new Realm(uRealm)); |
|
} |
|
Credentials creds = serviceCreds( |
|
KDCOptions.with(KDCOptions.FORWARDABLE), |
|
ccreds, ccreds.getClient(), sname, null, |
|
new PAData[] { |
|
new PAData(Krb5.PA_FOR_USER, |
|
new PAForUserEnc(client, |
|
ccreds.getSessionKey()).asn1Encode()), |
|
new PAData(Krb5.PA_PAC_OPTIONS, |
|
new PaPacOptions() |
|
.setResourceBasedConstrainedDelegation(true) |
|
.setClaims(true) |
|
.asn1Encode()) |
|
}, S4U2Type.SELF); |
|
if (!creds.getClient().equals(client)) { |
|
throw new KrbException("S4U2self request not honored by KDC"); |
|
} |
|
if (!creds.isForwardable()) { |
|
throw new KrbException("S4U2self ticket must be FORWARDABLE"); |
|
} |
|
return creds; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static Credentials acquireS4U2proxyCreds( |
|
String backend, Ticket second, |
|
PrincipalName client, Credentials ccreds) |
|
throws KrbException, IOException { |
|
PrincipalName backendPrincipal = new PrincipalName(backend); |
|
String backendRealm = backendPrincipal.getRealmString(); |
|
String localRealm = ccreds.getClient().getRealmString(); |
|
if (!backendRealm.equals(localRealm)) { |
|
// The middle service and the backend service are on |
|
|
|
if (Config.DISABLE_REFERRALS) { |
|
throw new KrbException("Cross-realm S4U2Proxy request not" + |
|
" possible when referrals are disabled."); |
|
} |
|
backendPrincipal = new PrincipalName( |
|
backendPrincipal.getNameType(), |
|
backendPrincipal.getNameStrings(), |
|
new Realm(localRealm)); |
|
} |
|
Credentials creds = serviceCreds(KDCOptions.with( |
|
KDCOptions.CNAME_IN_ADDL_TKT, KDCOptions.FORWARDABLE), |
|
ccreds, ccreds.getClient(), backendPrincipal, |
|
new Ticket[] {second}, new PAData[] { |
|
new PAData(Krb5.PA_PAC_OPTIONS, |
|
new PaPacOptions() |
|
.setResourceBasedConstrainedDelegation(true) |
|
.setClaims(true) |
|
.asn1Encode()) |
|
}, S4U2Type.PROXY); |
|
if (!creds.getClient().equals(client)) { |
|
throw new KrbException("S4U2proxy request not honored by KDC"); |
|
} |
|
return creds; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static Credentials acquireServiceCreds( |
|
String service, Credentials ccreds) |
|
throws KrbException, IOException { |
|
PrincipalName sname = new PrincipalName(service, |
|
PrincipalName.KRB_NT_UNKNOWN); |
|
return serviceCreds(sname, ccreds); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private static Credentials getTGTforRealm(String localRealm, |
|
String serviceRealm, Credentials ccreds, boolean[] okAsDelegate) |
|
throws KrbException { |
|
|
|
|
|
String[] realms = Realm.getRealmsList(localRealm, serviceRealm); |
|
|
|
int i = 0, k = 0; |
|
Credentials cTgt = null, newTgt = null, theTgt = null; |
|
PrincipalName tempService = null; |
|
String newTgtRealm = null; |
|
|
|
okAsDelegate[0] = true; |
|
for (cTgt = ccreds, i = 0; i < realms.length;) { |
|
tempService = PrincipalName.tgsService(serviceRealm, realms[i]); |
|
|
|
if (DEBUG) { |
|
System.out.println( |
|
">>> Credentials acquireServiceCreds: main loop: [" |
|
+ i +"] tempService=" + tempService); |
|
} |
|
|
|
try { |
|
newTgt = serviceCreds(tempService, cTgt); |
|
} catch (Exception exc) { |
|
newTgt = null; |
|
} |
|
|
|
if (newTgt == null) { |
|
if (DEBUG) { |
|
System.out.println(">>> Credentials acquireServiceCreds: " |
|
+ "no tgt; searching thru capath"); |
|
} |
|
|
|
|
|
|
|
*/ |
|
for (newTgt = null, k = i+1; |
|
newTgt == null && k < realms.length; k++) { |
|
tempService = PrincipalName.tgsService(realms[k], realms[i]); |
|
if (DEBUG) { |
|
System.out.println( |
|
">>> Credentials acquireServiceCreds: " |
|
+ "inner loop: [" + k |
|
+ "] tempService=" + tempService); |
|
} |
|
try { |
|
newTgt = serviceCreds(tempService, cTgt); |
|
} catch (Exception exc) { |
|
newTgt = null; |
|
} |
|
} |
|
} // Ends 'if (newTgt == null)' |
|
|
|
if (newTgt == null) { |
|
if (DEBUG) { |
|
System.out.println(">>> Credentials acquireServiceCreds: " |
|
+ "no tgt; cannot get creds"); |
|
} |
|
break; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
newTgtRealm = newTgt.getServer().getInstanceComponent(); |
|
if (okAsDelegate[0] && !newTgt.checkDelegate()) { |
|
if (DEBUG) { |
|
System.out.println(">>> Credentials acquireServiceCreds: " + |
|
"global OK-AS-DELEGATE turned off at " + |
|
newTgt.getServer()); |
|
} |
|
okAsDelegate[0] = false; |
|
} |
|
|
|
if (DEBUG) { |
|
System.out.println(">>> Credentials acquireServiceCreds: " |
|
+ "got tgt"); |
|
} |
|
|
|
if (newTgtRealm.equals(serviceRealm)) { |
|
|
|
theTgt = newTgt; |
|
break; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
for (k = i+1; k < realms.length; k++) { |
|
if (newTgtRealm.equals(realms[k])) { |
|
break; |
|
} |
|
} |
|
|
|
if (k < realms.length) { |
|
|
|
|
|
|
|
*/ |
|
i = k; |
|
cTgt = newTgt; |
|
|
|
if (DEBUG) { |
|
System.out.println(">>> Credentials acquireServiceCreds: " |
|
+ "continuing with main loop counter reset to " + i); |
|
} |
|
continue; |
|
} |
|
else { |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
break; |
|
} |
|
} // Ends outermost/main 'for' loop |
|
|
|
return theTgt; |
|
} |
|
|
|
|
|
|
|
*/ |
|
private static Credentials serviceCreds( |
|
PrincipalName service, Credentials ccreds) |
|
throws KrbException, IOException { |
|
return serviceCreds(new KDCOptions(), ccreds, |
|
ccreds.getClient(), service, null, null, |
|
S4U2Type.NONE); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private static Credentials serviceCreds( |
|
KDCOptions options, Credentials asCreds, |
|
PrincipalName cname, PrincipalName sname, |
|
Ticket[] additionalTickets, PAData[] extraPAs, |
|
S4U2Type s4u2Type) |
|
throws KrbException, IOException { |
|
if (!Config.DISABLE_REFERRALS) { |
|
try { |
|
return serviceCredsReferrals(options, asCreds, cname, sname, |
|
s4u2Type, additionalTickets, extraPAs); |
|
} catch (KrbException e) { |
|
// Server may raise an error if CANONICALIZE is true. |
|
// Try CANONICALIZE false. |
|
} |
|
} |
|
return serviceCredsSingle(options, asCreds, cname, |
|
asCreds.getClientAlias(), sname, sname, s4u2Type, |
|
additionalTickets, extraPAs); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private static Credentials serviceCredsReferrals( |
|
KDCOptions options, Credentials asCreds, |
|
PrincipalName cname, PrincipalName sname, |
|
S4U2Type s4u2Type, Ticket[] additionalTickets, |
|
PAData[] extraPAs) |
|
throws KrbException, IOException { |
|
options = new KDCOptions(options.toBooleanArray()); |
|
options.set(KDCOptions.CANONICALIZE, true); |
|
PrincipalName cSname = sname; |
|
PrincipalName refSname = sname; |
|
Credentials creds = null; |
|
boolean isReferral = false; |
|
List<String> referrals = new LinkedList<>(); |
|
PrincipalName clientAlias = asCreds.getClientAlias(); |
|
while (referrals.size() <= Config.MAX_REFERRALS) { |
|
ReferralsCache.ReferralCacheEntry ref = |
|
ReferralsCache.get(cname, sname, refSname.getRealmString()); |
|
String toRealm = null; |
|
if (ref == null) { |
|
creds = serviceCredsSingle(options, asCreds, cname, |
|
clientAlias, refSname, cSname, s4u2Type, |
|
additionalTickets, extraPAs); |
|
PrincipalName server = creds.getServer(); |
|
if (!refSname.equals(server)) { |
|
String[] serverNameStrings = server.getNameStrings(); |
|
if (serverNameStrings.length == 2 && |
|
serverNameStrings[0].equals( |
|
PrincipalName.TGS_DEFAULT_SRV_NAME) && |
|
!refSname.getRealmAsString().equals( |
|
serverNameStrings[1])) { |
|
// Server Name (sname) has the following format: |
|
|
|
if (s4u2Type == S4U2Type.NONE) { |
|
// Do not store S4U2Self or S4U2Proxy referral |
|
// TGTs in the cache. Caching such tickets is not |
|
// defined in MS-SFU and may cause unexpected |
|
|
|
ReferralsCache.put(cname, sname, |
|
server.getRealmString(), |
|
serverNameStrings[1], creds); |
|
} |
|
toRealm = serverNameStrings[1]; |
|
isReferral = true; |
|
} |
|
} |
|
} else { |
|
creds = ref.getCreds(); |
|
toRealm = ref.getToRealm(); |
|
isReferral = true; |
|
} |
|
if (isReferral) { |
|
if (s4u2Type == S4U2Type.PROXY) { |
|
Credentials[] credsInOut = |
|
new Credentials[] {creds, null}; |
|
toRealm = handleS4U2ProxyReferral(asCreds, |
|
credsInOut, sname); |
|
creds = credsInOut[0]; |
|
if (additionalTickets == null || |
|
additionalTickets.length == 0 || |
|
credsInOut[1] == null) { |
|
throw new KrbException("Additional tickets expected" + |
|
" for S4U2Proxy."); |
|
} |
|
additionalTickets[0] = credsInOut[1].getTicket(); |
|
} else if (s4u2Type == S4U2Type.SELF) { |
|
handleS4U2SelfReferral(extraPAs, asCreds, creds); |
|
} |
|
if (referrals.contains(toRealm)) { |
|
|
|
return null; |
|
} |
|
asCreds = creds; |
|
refSname = new PrincipalName(refSname.getNameString(), |
|
refSname.getNameType(), toRealm); |
|
referrals.add(toRealm); |
|
isReferral = false; |
|
continue; |
|
} |
|
break; |
|
} |
|
return creds; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private static Credentials serviceCredsSingle( |
|
KDCOptions options, Credentials asCreds, |
|
PrincipalName cname, PrincipalName clientAlias, |
|
PrincipalName refSname, PrincipalName sname, |
|
S4U2Type s4u2Type, Ticket[] additionalTickets, |
|
PAData[] extraPAs) |
|
throws KrbException, IOException { |
|
Credentials theCreds = null; |
|
boolean[] okAsDelegate = new boolean[]{true}; |
|
String[] serverAsCredsNames = asCreds.getServer().getNameStrings(); |
|
String tgtRealm = serverAsCredsNames[1]; |
|
String serviceRealm = refSname.getRealmString(); |
|
if (!serviceRealm.equals(tgtRealm)) { |
|
|
|
if (DEBUG) { |
|
System.out.println(">>> serviceCredsSingle:" + |
|
" cross-realm authentication"); |
|
System.out.println(">>> serviceCredsSingle:" + |
|
" obtaining credentials from " + tgtRealm + |
|
" to " + serviceRealm); |
|
} |
|
Credentials newTgt = getTGTforRealm(tgtRealm, serviceRealm, |
|
asCreds, okAsDelegate); |
|
if (newTgt == null) { |
|
throw new KrbApErrException(Krb5.KRB_AP_ERR_GEN_CRED, |
|
"No service creds"); |
|
} |
|
if (DEBUG) { |
|
System.out.println(">>> Cross-realm TGT Credentials" + |
|
" serviceCredsSingle: "); |
|
Credentials.printDebug(newTgt); |
|
} |
|
if (s4u2Type == S4U2Type.SELF) { |
|
handleS4U2SelfReferral(extraPAs, asCreds, newTgt); |
|
} |
|
asCreds = newTgt; |
|
cname = asCreds.getClient(); |
|
} else if (DEBUG) { |
|
System.out.println(">>> Credentials serviceCredsSingle:" + |
|
" same realm"); |
|
} |
|
KrbTgsReq req = new KrbTgsReq(options, asCreds, cname, clientAlias, |
|
refSname, sname, additionalTickets, extraPAs); |
|
theCreds = req.sendAndGetCreds(); |
|
if (theCreds != null) { |
|
if (DEBUG) { |
|
System.out.println(">>> TGS credentials serviceCredsSingle:"); |
|
Credentials.printDebug(theCreds); |
|
} |
|
if (!okAsDelegate[0]) { |
|
theCreds.resetDelegate(); |
|
} |
|
} |
|
return theCreds; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private static void handleS4U2SelfReferral(PAData[] pas, |
|
Credentials oldCeds, Credentials newCreds) |
|
throws Asn1Exception, KrbException, IOException { |
|
if (DEBUG) { |
|
System.out.println(">>> Handling S4U2Self referral"); |
|
} |
|
for (int i = 0; i < pas.length; i++) { |
|
PAData pa = pas[i]; |
|
if (pa.getType() == Krb5.PA_FOR_USER) { |
|
PAForUserEnc paForUser = new PAForUserEnc( |
|
new DerValue(pa.getValue()), |
|
oldCeds.getSessionKey()); |
|
pas[i] = new PAData(Krb5.PA_FOR_USER, |
|
new PAForUserEnc(paForUser.getName(), |
|
newCreds.getSessionKey()).asn1Encode()); |
|
break; |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private static String handleS4U2ProxyReferral(Credentials asCreds, |
|
Credentials[] credsInOut, PrincipalName sname) |
|
throws KrbException, IOException { |
|
if (DEBUG) { |
|
System.out.println(">>> Handling S4U2Proxy referral"); |
|
} |
|
Credentials refTGT = null; |
|
// Get a credential for the middle service to the backend so we know |
|
|
|
Credentials middleSvcCredsInBackendRealm = |
|
serviceCreds(sname, asCreds); |
|
String backendRealm = |
|
middleSvcCredsInBackendRealm.getServer().getRealmString(); |
|
String toRealm = credsInOut[0].getServer().getNameStrings()[1]; |
|
if (!toRealm.equals(backendRealm)) { |
|
// More than 1 hop. Follow the referrals chain and obtain a |
|
|
|
refTGT = getTGTforRealm(toRealm, backendRealm, credsInOut[0], |
|
new boolean[1]); |
|
} else { |
|
// There was only 1 hop. The referral TGT received is already |
|
|
|
refTGT = credsInOut[0]; |
|
} |
|
credsInOut[0] = getTGTforRealm(asCreds.getClient().getRealmString(), |
|
backendRealm, asCreds, new boolean[1]); |
|
credsInOut[1] = refTGT; |
|
return backendRealm; |
|
} |
|
} |