Hi,

I have developed a fix for the lack of cross-realm S4USelf support in OpenJDK 8 which I would like to contribute.

The fix does not address realm referral as discussed in the bug and so requires both realms to be present in the krb5.conf file.


Bug: https://bugs.openjdk.java.net/browse/JDK-8005819

Diff:

--- ../jdk8u/jdk/src/share/classes/sun/security/krb5/internal/CredentialsUtil.java 2016-08-22 15:58:34.720949000 +0100 +++ ../jdkpatch/jdk/src/share/classes/sun/security/krb5/internal/CredentialsUtil.java 2016-09-16 15:11:02.967278000 +0100
@@ -54,19 +54,57 @@
             Credentials ccreds) throws KrbException, IOException {
         String uRealm = client.getRealmString();
         String localRealm = ccreds.getClient().getRealmString();
+        KrbTgsReq req;
         if (!uRealm.equals(localRealm)) {
-            // TODO: we do not support kerberos referral now
- throw new KrbException("Cross realm impersonation not supported");
+               //get a cross realm TGT
+ String tname = PrincipalName.TGS_DEFAULT_SRV_NAME + PrincipalName.NAME_COMPONENT_SEPARATOR_STR +
+                               uRealm + PrincipalName.NAME_REALM_SEPARATOR_STR 
+ uRealm;
+
+               Credentials foreignTGT = acquireServiceCreds(tname, ccreds);
+
+                       //get a referral TGT from the foreign realm for the user
+ String [] svcUPN = ccreds.getClient().getNameStrings().clone(); + svcUPN[svcUPN.length-1] += PrincipalName.NAME_REALM_SEPARATOR_STR + localRealm;
+
+ PrincipalName svcPrinc = new PrincipalName(PrincipalName.KRB_NT_ENTERPRISE, svcUPN, new Realm(uRealm));
+
+            req = new KrbTgsReq(
+                       foreignTGT,
+                       svcPrinc,
+                    new PAData(Krb5.PA_FOR_USER,
+                        new PAForUserEnc(client,
+                                       
foreignTGT.getSessionKey()).asn1Encode()),
+                    client);
+
+            if (!foreignTGT.isForwardable()) {
+ throw new KrbException("S4U2self needs a FORWARDABLE ticket");
+            }
+
+            Credentials referralTGT = req.sendAndGetCreds();
+
+ //create request to local realm for user in foreign realm using the referral TGT from the
+            //foreign realm
+            req = new KrbTgsReq(
+                       referralTGT,
+                       ccreds.getClient(),
+                    new PAData(Krb5.PA_FOR_USER,
+                        new PAForUserEnc(client,
+                                       
referralTGT.getSessionKey()).asn1Encode()));
         }
-        if (!ccreds.isForwardable()) {
- throw new KrbException("S4U2self needs a FORWARDABLE ticket");
+        else {
+               //same realm
+               req = new KrbTgsReq(
+                    ccreds,
+                    ccreds.getClient(),
+                    new PAData(Krb5.PA_FOR_USER,
+                        new PAForUserEnc(client,
+                            ccreds.getSessionKey()).asn1Encode()));
+
+               if (!ccreds.isForwardable()) {
+ throw new KrbException("S4U2self needs a FORWARDABLE ticket");
+            }
         }
-        KrbTgsReq req = new KrbTgsReq(
-                ccreds,
-                ccreds.getClient(),
-                new PAData(Krb5.PA_FOR_USER,
-                    new PAForUserEnc(client,
-                        ccreds.getSessionKey()).asn1Encode()));
+
         Credentials creds = req.sendAndGetCreds();
         if (!creds.getClient().equals(client)) {
throw new KrbException("S4U2self request not honored by KDC"); --- ../jdk8u/jdk/src/share/classes/sun/security/krb5/KrbTgsReq.java 2016-08-22 15:58:34.718972000 +0100 +++ ../jdkpatch/jdk/src/share/classes/sun/security/krb5/KrbTgsReq.java 2016-09-16 15:11:02.979473000 +0100
@@ -45,6 +45,7 @@

     private PrincipalName princName;
     private PrincipalName servName;
+    private PrincipalName targetName;
     private TGSReq tgsReqMessg;
     private KerberosTime ctime;
     private Ticket secondTicket = null;
@@ -109,8 +110,31 @@
             null,
             null,
             null,
-            extraPA); // the PA-FOR-USER
+            extraPA,// the PA-FOR-USER
+            null);
     }
+
+    public KrbTgsReq(Credentials asCreds,
+            PrincipalName sname,
+            PAData extraPA,
+            PrincipalName tname)
+       throws KrbException, IOException {
+       this(KDCOptions.with(KDCOptions.FORWARDABLE, KDCOptions.CANONICALIZE),
+          asCreds,
+          asCreds.getClient(),
+          sname,
+          null,
+          null,
+          null,
+          null,
+          null,
+          null,
+          null,
+          null,
+          extraPA,// the PA-FOR-USER
+          tname);
+       }
+

     // Called by Credentials, KrbCred
     KrbTgsReq(
@@ -127,7 +151,7 @@
             EncryptionKey subKey) throws KrbException, IOException {
         this(options, asCreds, asCreds.getClient(), sname,
                 from, till, rtime, eTypes, addresses,
-                authorizationData, additionalTickets, subKey, null);
+ authorizationData, additionalTickets, subKey, null, null);
     }

     private KrbTgsReq(
@@ -143,10 +167,12 @@
             AuthorizationData authorizationData,
             Ticket[] additionalTickets,
             EncryptionKey subKey,
-            PAData extraPA) throws KrbException, IOException {
+            PAData extraPA,
+            PrincipalName tname) throws KrbException, IOException {

         princName = cname;
         servName = sname;
+        targetName = tname;
         ctime = KerberosTime.now();

         // check if they are valid arguments. The optional fields
@@ -240,8 +266,13 @@
      */
     public void send() throws IOException, KrbException {
         String realmStr = null;
-        if (servName != null)
+        if (targetName != null){
+               realmStr = targetName.getRealmString();
+        }
+        else if (servName != null) {
             realmStr = servName.getRealmString();
+        }
+
         KdcComm comm = new KdcComm(realmStr);
         ibuf = comm.send(obuf);
     }
--- ../jdk8u/jdk/src/share/classes/sun/security/krb5/KrbKdcRep.java 2016-08-22 15:58:34.801487000 +0100 +++ ../jdkpatch/jdk/src/share/classes/sun/security/krb5/KrbKdcRep.java 2016-09-16 15:11:02.986718000 +0100
@@ -45,7 +45,8 @@
             throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
         }

-        if (!req.reqBody.sname.equals(rep.encKDCRepPart.sname)) {
+        if (!req.reqBody.kdcOptions.get(KDCOptions.CANONICALIZE) &&
+               !req.reqBody.sname.equals(rep.encKDCRepPart.sname)) {
             rep.encKDCRepPart.key.destroy();
             throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
         }
--- ../jdk8u/jdk/src/share/classes/sun/security/krb5/internal/KDCOptions.java 2016-08-22 15:58:34.758240000 +0100 +++ ../jdkpatch/jdk/src/share/classes/sun/security/krb5/internal/KDCOptions.java 2016-09-16 15:11:02.990754000 +0100
@@ -140,6 +140,7 @@
     public static final int UNUSED10        = 10;
     public static final int UNUSED11        = 11;
     public static final int CNAME_IN_ADDL_TKT = 14;
+    public static final int CANONICALIZE = 15;
     public static final int RENEWABLE_OK    = 27;
     public static final int ENC_TKT_IN_SKEY = 28;
     public static final int RENEW           = 30;
@@ -160,7 +161,8 @@
         "UNUSED11",         //11;
         null,null,
         "CNAME_IN_ADDL_TKT",//14;
-        null,null,null,null,null,null,null,null,null,null,null,null,
+        "CANONICALIZE",     //15;
+        null,null,null,null,null,null,null,null,null,null,null,
         "RENEWABLE_OK",     //27;
         "ENC_TKT_IN_SKEY",  //28;
         null,
--- ../jdk8u/jdk/src/share/classes/sun/security/krb5/PrincipalName.java 2016-08-22 15:58:34.708329000 +0100 +++ ../jdkpatch/jdk/src/share/classes/sun/security/krb5/PrincipalName.java 2016-09-16 15:11:02.995791000 +0100
@@ -89,6 +89,11 @@
      * Unique ID
      */
     public static final int KRB_NT_UID = 5;
+
+    /**
+     * Enterprise name; may be mapped to principal name
+     */
+    public static final int KRB_NT_ENTERPRISE = 10;

     /**
      * TGS Name

Reply via email to