< prev index next >

src/java.security.jgss/share/classes/sun/security/krb5/KrbAsReqBuilder.java

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

@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 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

@@ -260,29 +260,40 @@
      * @param key null (initial AS-REQ) or pakey (with preauth)
      * @return the KrbAsReq object
      * @throws KrbException
      * @throws IOException
      */
-    private KrbAsReq build(EncryptionKey key) throws KrbException, IOException {
+    private KrbAsReq build(EncryptionKey key, ReferralsState referralsState)
+            throws KrbException, IOException {
+        PAData[] extraPAs = null;
         int[] eTypes;
         if (password != null) {
             eTypes = EType.getDefaults("default_tkt_enctypes");
         } else {
             EncryptionKey[] ks = Krb5Util.keysFromJavaxKeyTab(ktab, cname);
             eTypes = EType.getDefaults("default_tkt_enctypes",
                     ks);
             for (EncryptionKey k: ks) k.destroy();
         }
+        options = (options == null) ? new KDCOptions() : options;
+        if (referralsState.isEnabled()) {
+            options.set(KDCOptions.CANONICALIZE, true);
+            extraPAs = new PAData[]{ new PAData(Krb5.PA_REQ_ENC_PA_REP,
+                    new byte[]{}) };
+        } else {
+            options.set(KDCOptions.CANONICALIZE, false);
+        }
         return new KrbAsReq(key,
             options,
             cname,
             sname,
             from,
             till,
             rtime,
             eTypes,
-            addresses);
+            addresses,
+            extraPAs);
     }
 
     /**
      * Parses AS-REP, decrypts enc-part, retrieves ticket and session key
      * @throws KrbException

@@ -316,15 +327,19 @@
      * @throws KrbException
      * @throws IOException
      */
     private KrbAsReqBuilder send() throws KrbException, IOException {
         boolean preAuthFailedOnce = false;
-        KdcComm comm = new KdcComm(cname.getRealmAsString());
+        KdcComm comm = null;
         EncryptionKey pakey = null;
+        ReferralsState referralsState = new ReferralsState();
         while (true) {
+            if (referralsState.refreshComm()) {
+                comm = new KdcComm(cname.getRealmAsString());
+            }
             try {
-                req = build(pakey);
+                req = build(pakey, referralsState);
                 rep = new KrbAsRep(comm.send(req.encoding()));
                 return this;
             } catch (KrbException ke) {
                 if (!preAuthFailedOnce && (
                         ke.returnCode() == Krb5.KDC_ERR_PREAUTH_FAILED ||

@@ -349,16 +364,75 @@
                                 PAData.getSaltAndParams(
                                     paEType, kerr.getPA()));
                     }
                     paList = kerr.getPA();  // Update current paList
                 } else {
+                    if (referralsState.handleError(ke)) {
+                        continue;
+                    }
                     throw ke;
                 }
             }
         }
     }
 
+    private final class ReferralsState {
+        private boolean enabled;
+        private int count;
+        private boolean refreshComm;
+
+        ReferralsState() throws KrbException {
+            if (Config.DISABLE_REFERRALS) {
+                if (cname.getNameType() == PrincipalName.KRB_NT_ENTERPRISE) {
+                    throw new KrbException("NT-ENTERPRISE principals only allowed" +
+                            " when referrals are enabled.");
+                }
+                enabled = false;
+            } else {
+                enabled = true;
+            }
+            refreshComm = true;
+        }
+
+        boolean handleError(KrbException ke) throws RealmException {
+            if (enabled) {
+                if (ke.returnCode() == Krb5.KRB_ERR_WRONG_REALM) {
+                    Realm referredRealm = ke.getError().getClientRealm();
+                    if (req.getMessage().reqBody.kdcOptions.get(KDCOptions.CANONICALIZE) &&
+                            referredRealm != null && referredRealm.toString().length() > 0 &&
+                            count < Config.MAX_REFERRALS) {
+                        cname = new PrincipalName(cname.getNameString().replaceAll(
+                                PrincipalName.NAME_REALM_SEPARATOR + "", "\\\\" +
+                                PrincipalName.NAME_REALM_SEPARATOR), cname.getNameType(),
+                                referredRealm.toString());
+                        refreshComm = true;
+                        count++;
+                        return true;
+                    }
+                }
+                if (count < Config.MAX_REFERRALS &&
+                        cname.getNameType() != PrincipalName.KRB_NT_ENTERPRISE) {
+                    // Server may raise an error if CANONICALIZE is true.
+                    // Try CANONICALIZE false.
+                    enabled = false;
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        boolean refreshComm() {
+            boolean retRefreshComm = refreshComm;
+            refreshComm = false;
+            return retRefreshComm;
+        }
+
+        boolean isEnabled() {
+            return enabled;
+        }
+    }
+
     /**
      * Performs AS-REQ send and AS-REP receive.
      * Maybe a state is needed here, to divide prepare process and getCreds.
      * @throws KrbException
      * @throws Asn1Exception
< prev index next >