< prev index next >

src/java.security.jgss/share/classes/sun/security/krb5/internal/CredentialsUtil.java

Print this page
rev 54745 : 8215032: Support Kerberos cross-realm referrals (RFC 6806)
Reviewed-by: weijun

@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License version 2 only, as
  * published by the Free Software Foundation.  Oracle designates this

@@ -31,10 +31,13 @@
 
 package sun.security.krb5.internal;
 
 import sun.security.krb5.*;
 import java.io.IOException;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
 
 /**
  * This class is a utility that contains much of the TGS-Exchange
  * protocol. It is used by ../Credentials.java for service ticket
  * acquisition in both the normal and the x-realm case.

@@ -59,17 +62,15 @@
             throw new KrbException("Cross realm impersonation not supported");
         }
         if (!ccreds.isForwardable()) {
             throw new KrbException("S4U2self needs a FORWARDABLE ticket");
         }
-        KrbTgsReq req = new KrbTgsReq(
-                ccreds,
-                ccreds.getClient(),
-                new PAData(Krb5.PA_FOR_USER,
+        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 = req.sendAndGetCreds();
+                            ccreds.getSessionKey()).asn1Encode())});
         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");

@@ -87,15 +88,14 @@
      */
     public static Credentials acquireS4U2proxyCreds(
                 String backend, Ticket second,
                 PrincipalName client, Credentials ccreds)
             throws KrbException, IOException {
-        KrbTgsReq req = new KrbTgsReq(
-                ccreds,
-                second,
-                new PrincipalName(backend));
-        Credentials creds = req.sendAndGetCreds();
+        Credentials creds = serviceCreds(KDCOptions.with(
+                KDCOptions.CNAME_IN_ADDL_TKT, KDCOptions.FORWARDABLE),
+                ccreds, ccreds.getClient(), new PrincipalName(backend),
+                new Ticket[] {second}, null);
         if (!creds.getClient().equals(client)) {
             throw new KrbException("S4U2proxy request not honored by KDC");
         }
         return creds;
     }

@@ -112,58 +112,14 @@
      * @param ccreds client's initial credential
      */
     public static Credentials acquireServiceCreds(
                 String service, Credentials ccreds)
             throws KrbException, IOException {
-        PrincipalName sname = new PrincipalName(service);
-        String serviceRealm = sname.getRealmString();
-        String localRealm = ccreds.getClient().getRealmString();
-
-        if (localRealm.equals(serviceRealm)) {
-            if (DEBUG) {
-                System.out.println(
-                        ">>> Credentials acquireServiceCreds: same realm");
-            }
+        PrincipalName sname = new PrincipalName(service,
+                PrincipalName.KRB_NT_SRV_HST);
             return serviceCreds(sname, ccreds);
         }
-        Credentials theCreds = null;
-
-        boolean[] okAsDelegate = new boolean[1];
-        Credentials theTgt = getTGTforRealm(localRealm, serviceRealm,
-                ccreds, okAsDelegate);
-        if (theTgt != null) {
-            if (DEBUG) {
-                System.out.println(">>> Credentials acquireServiceCreds: "
-                        + "got right tgt");
-                System.out.println(">>> Credentials acquireServiceCreds: "
-                        + "obtaining service creds for " + sname);
-            }
-
-            try {
-                theCreds = serviceCreds(sname, theTgt);
-            } catch (Exception exc) {
-                if (DEBUG) {
-                    System.out.println(exc);
-                }
-                theCreds = null;
-            }
-        }
-
-        if (theCreds != null) {
-            if (DEBUG) {
-                System.out.println(">>> Credentials acquireServiceCreds: "
-                        + "returning creds:");
-                Credentials.printDebug(theCreds);
-            }
-            if (!okAsDelegate[0]) {
-                theCreds.resetDelegate();
-            }
-            return theCreds;
-        }
-        throw new KrbApErrException(Krb5.KRB_AP_ERR_GEN_CRED,
-                                    "No service creds");
-    }
 
     /**
      * Gets a TGT to another realm
      * @param localRealm this realm
      * @param serviceRealm the other realm, cannot equals to localRealm

@@ -303,8 +259,137 @@
     * This method does the real job to request the service credential.
     */
     private static Credentials serviceCreds(
             PrincipalName service, Credentials ccreds)
             throws KrbException, IOException {
-        return new KrbTgsReq(ccreds, service).sendAndGetCreds();
+        return serviceCreds(new KDCOptions(), ccreds,
+                ccreds.getClient(), service, null, null);
+    }
+
+    private static Credentials serviceCreds(
+            KDCOptions options, Credentials asCreds,
+            PrincipalName cname, PrincipalName sname,
+            Ticket[] additionalTickets, PAData[] extraPAs)
+            throws KrbException, IOException {
+        if (!Config.DISABLE_REFERRALS) {
+            try {
+                return serviceCredsReferrals(options, asCreds,
+                        cname, sname, additionalTickets, extraPAs);
+            } catch (KrbException e) {
+                // Server may raise an error if CANONICALIZE is true.
+                // Try CANONICALIZE false.
+            }
+        }
+        return serviceCredsSingle(options, asCreds,
+                cname, sname, additionalTickets, extraPAs);
+    }
+
+    private static Credentials serviceCredsReferrals(
+            KDCOptions options, Credentials asCreds,
+            PrincipalName cname, PrincipalName sname,
+            Ticket[] additionalTickets, PAData[] extraPAs)
+            throws KrbException, IOException {
+        options = new KDCOptions(options.toBooleanArray());
+        options.set(KDCOptions.CANONICALIZE, true);
+        PrincipalName cSname = (PrincipalName) sname.clone();
+        Credentials creds = null;
+        boolean isReferral = false;
+        List<String> referrals = new LinkedList<>();
+        while (referrals.size() <= Config.MAX_REFERRALS) {
+            ReferralsCache.ReferralCacheEntry ref =
+                    ReferralsCache.get(sname, cSname.getRealmString());
+            String toRealm = null;
+            if (ref == null) {
+                creds = serviceCredsSingle(options, asCreds,
+                        cname, cSname, additionalTickets, extraPAs);
+                PrincipalName server = creds.getServer();
+                if (!cSname.equals(server)) {
+                    String[] serverNameStrings = server.getNameStrings();
+                    if (serverNameStrings.length == 2 &&
+                        serverNameStrings[0].equals(
+                                PrincipalName.TGS_DEFAULT_SRV_NAME) &&
+                        !cSname.getRealmAsString().equals(serverNameStrings[1])) {
+                        // Server Name (sname) has the following format:
+                        //      krbtgt/TO-REALM.COM@FROM-REALM.COM
+                        ReferralsCache.put(sname, server.getRealmString(),
+                                serverNameStrings[1], creds);
+                        toRealm = serverNameStrings[1];
+                        isReferral = true;
+                        asCreds = creds;
+                    }
+                }
+            } else {
+                toRealm = ref.getToRealm();
+                asCreds = ref.getCreds();
+                isReferral = true;
+            }
+            if (isReferral) {
+                if (referrals.contains(toRealm)) {
+                    // Referrals loop detected
+                    return null;
+                }
+                cSname = new PrincipalName(cSname.getNameString(),
+                        cSname.getNameType(), toRealm);
+                referrals.add(toRealm);
+                isReferral = false;
+                continue;
+            }
+            break;
+        }
+        return creds;
+    }
+
+    private static Credentials serviceCredsSingle(
+            KDCOptions options, Credentials asCreds,
+            PrincipalName cname, PrincipalName sname,
+            Ticket[] additionalTickets, PAData[] extraPAs)
+            throws KrbException, IOException {
+        Credentials theCreds = null;
+        boolean[] okAsDelegate = new boolean[]{true};
+        String[] serverAsCredsNames = asCreds.getServer().getNameStrings();
+        if (serverAsCredsNames.length == 2 &&
+                serverAsCredsNames[0].equals(
+                        PrincipalName.TGS_DEFAULT_SRV_NAME)) {
+            String tgtRealm = serverAsCredsNames[1];
+            String serviceRealm = sname.getRealmString();
+            if (!serviceRealm.equals(tgtRealm)) {
+                // This is a cross-realm service request
+                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);
+                }
+                asCreds = newTgt;
+                cname = asCreds.getClient();
+            } else if (DEBUG) {
+                System.out.println(">>> Credentials serviceCredsSingle:" +
+                        " same realm");
+            }
+        }
+        KrbTgsReq req = new KrbTgsReq(options, asCreds,
+                cname, 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;
     }
 }
< prev index next >