Skip to content
Permalink
Browse files

8005819: Support cross-realm MSSFU

Reviewed-by: weijun
  • Loading branch information
martinuy committed Dec 11, 2019
1 parent 06ecbfd commit 4fa827ec92665dae9c3cd6505d885ba5b7998df2
@@ -95,9 +95,16 @@
}
}

PrincipalName clientAlias = null;
if (rep.cname.equals(req.reqBody.cname)) {
// Only propagate the client alias if it is not an
// impersonation ticket (S4U2Self or S4U2Proxy).
clientAlias = tgsReq.getClientAlias();
}

this.creds = new Credentials(rep.ticket,
rep.cname,
tgsReq.getClientAlias(),
clientAlias,
enc_part.sname,
serverAlias,
enc_part.key,
@@ -32,6 +32,8 @@
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;
@@ -45,6 +47,10 @@

private static boolean DEBUG = sun.security.krb5.internal.Krb5.DEBUG;

private static enum S4U2Type {
NONE, SELF, PROXY
}

/**
* Used by a middle server to acquire credentials on behalf of a
* client to itself using the S4U2self extension.
@@ -54,20 +60,44 @@
*/
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)) {
// TODO: we do not support kerberos referral now
throw new KrbException("Cross realm impersonation not supported");
}
if (!ccreds.isForwardable()) {
throw new KrbException("S4U2self needs a FORWARDABLE ticket");
// Referrals will be required because the middle service
// and the client impersonated are on different realms.
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
// referral to REALM-2.COM.
sname = ccreds.getClientAlias();
}
sname = new PrincipalName(sname.getNameType(),
sname.getNameStrings(), new Realm(uRealm));
}
Credentials creds = serviceCreds(KDCOptions.with(KDCOptions.FORWARDABLE),
ccreds, ccreds.getClient(), ccreds.getClient(), null,
new PAData[] {new PAData(Krb5.PA_FOR_USER,
new PAForUserEnc(client,
ccreds.getSessionKey()).asn1Encode())});
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");
}
@@ -89,10 +119,31 @@ 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
// different realms, so referrals will be required.
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(), new PrincipalName(backend),
new Ticket[] {second}, null);
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");
}
@@ -261,7 +312,8 @@ private static Credentials serviceCreds(
PrincipalName service, Credentials ccreds)
throws KrbException, IOException {
return serviceCreds(new KDCOptions(), ccreds,
ccreds.getClient(), service, null, null);
ccreds.getClient(), service, null, null,
S4U2Type.NONE);
}

/*
@@ -273,20 +325,21 @@ private static Credentials serviceCreds(
private static Credentials serviceCreds(
KDCOptions options, Credentials asCreds,
PrincipalName cname, PrincipalName sname,
Ticket[] additionalTickets, PAData[] extraPAs)
Ticket[] additionalTickets, PAData[] extraPAs,
S4U2Type s4u2Type)
throws KrbException, IOException {
if (!Config.DISABLE_REFERRALS) {
try {
return serviceCredsReferrals(options, asCreds,
cname, sname, additionalTickets, extraPAs);
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, additionalTickets,
extraPAs);
asCreds.getClientAlias(), sname, sname, s4u2Type,
additionalTickets, extraPAs);
}

/*
@@ -296,8 +349,9 @@ private static Credentials serviceCreds(
private static Credentials serviceCredsReferrals(
KDCOptions options, Credentials asCreds,
PrincipalName cname, PrincipalName sname,
Ticket[] additionalTickets, PAData[] extraPAs)
throws KrbException, IOException {
S4U2Type s4u2Type, Ticket[] additionalTickets,
PAData[] extraPAs)
throws KrbException, IOException {
options = new KDCOptions(options.toBooleanArray());
options.set(KDCOptions.CANONICALIZE, true);
PrincipalName cSname = sname;
@@ -312,34 +366,58 @@ private static Credentials serviceCredsReferrals(
String toRealm = null;
if (ref == null) {
creds = serviceCredsSingle(options, asCreds, cname,
clientAlias, refSname, cSname, additionalTickets,
extraPAs);
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])) {
!refSname.getRealmAsString().equals(
serverNameStrings[1])) {
// Server Name (sname) has the following format:
// krbtgt/TO-REALM.COM@FROM-REALM.COM
ReferralsCache.put(cname, sname, server.getRealmString(),
serverNameStrings[1], creds);
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
// results when using them in a different context.
ReferralsCache.put(cname, sname,
server.getRealmString(),
serverNameStrings[1], creds);
}
toRealm = serverNameStrings[1];
isReferral = true;
asCreds = creds;
}
}
} else {
creds = ref.getCreds();
toRealm = ref.getToRealm();
asCreds = ref.getCreds();
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)) {
// Referrals loop detected
return null;
}
asCreds = creds;
refSname = new PrincipalName(refSname.getNameString(),
refSname.getNameType(), toRealm);
referrals.add(toRealm);
@@ -362,8 +440,9 @@ private static Credentials serviceCredsSingle(
KDCOptions options, Credentials asCreds,
PrincipalName cname, PrincipalName clientAlias,
PrincipalName refSname, PrincipalName sname,
Ticket[] additionalTickets, PAData[] extraPAs)
throws KrbException, IOException {
S4U2Type s4u2Type, Ticket[] additionalTickets,
PAData[] extraPAs)
throws KrbException, IOException {
Credentials theCreds = null;
boolean[] okAsDelegate = new boolean[]{true};
String[] serverAsCredsNames = asCreds.getServer().getNameStrings();
@@ -389,6 +468,9 @@ private static Credentials serviceCredsSingle(
" serviceCredsSingle: ");
Credentials.printDebug(newTgt);
}
if (s4u2Type == S4U2Type.SELF) {
handleS4U2SelfReferral(extraPAs, asCreds, newTgt);
}
asCreds = newTgt;
cname = asCreds.getClient();
} else if (DEBUG) {
@@ -409,4 +491,79 @@ private static Credentials serviceCredsSingle(
}
return theCreds;
}

/**
* PA-FOR-USER may need to be regenerated if credentials
* change. This may happen when obtaining a TGT for a
* different realm or when using a referral TGT.
*/
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;
}
}
}

/**
* This method is called after receiving the first realm referral for
* a S4U2Proxy request. The credentials and tickets needed for the
* final S4U2Proxy request (in the referrals chain) are returned.
*
* Referrals are handled as described by MS-SFU (section 3.1.5.2.2
* Receives Referral).
*
* @param asCreds middle service credentials used for the first S4U2Proxy
* request
* @param credsInOut (in/out parameter):
* * input: first S4U2Proxy referral TGT received, null
* * output: referral TGT for final S4U2Proxy service request,
* client referral TGT for final S4U2Proxy service request
* (to be sent as additional-ticket)
* @param sname the backend service name
* @param additionalTickets (out parameter): the additional ticket for the
* last S4U2Proxy request is returned
* @return the backend realm for the last S4U2Proxy request
*/
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
// the backend realm, as described in MS-SFU (section 3.1.5.2.2).
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
// TGT for the backend realm.
refTGT = getTGTforRealm(toRealm, backendRealm, credsInOut[0],
new boolean[1]);
} else {
// There was only 1 hop. The referral TGT received is already
// for the backend realm.
refTGT = credsInOut[0];
}
credsInOut[0] = getTGTforRealm(asCreds.getClient().getRealmString(),
backendRealm, asCreds, new boolean[1]);
credsInOut[1] = refTGT;
return backendRealm;
}
}
@@ -165,6 +165,7 @@

// S4U2user info
public static final int PA_FOR_USER = 129;
public static final int PA_PAC_OPTIONS = 167;

// FAST (RFC 6806)
public static final int PA_REQ_ENC_PA_REP = 149;
@@ -181,6 +181,10 @@ public PAForUserEnc(DerValue encoding, EncryptionKey key)
return output;
}

public PrincipalName getName() {
return name;
}

public String toString() {
return "PA-FOR-USER: " + name;
}

0 comments on commit 4fa827e

Please sign in to comment.
You can’t perform that action at this time.