1 /*
   2  * Copyright (c) 2019, Red Hat, Inc.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /*
  25  * @test
  26  * @bug 8215032
  27  * @library /test/lib
  28  * @run main/othervm/timeout=120 -Dsun.security.krb5.debug=true ReferralsTest
  29  * @summary Test Kerberos cross-realm referrals (RFC 6806)
  30  */
  31 
  32 import java.io.File;
  33 import sun.security.krb5.Credentials;
  34 import sun.security.krb5.internal.CredentialsUtil;
  35 import sun.security.krb5.KrbAsReqBuilder;
  36 import sun.security.krb5.PrincipalName;
  37 
  38 public class ReferralsTest {
  39     private static final boolean DEBUG = true;
  40     private static final String krbConfigName = "krb5-localkdc.conf";
  41     private static final String realmKDC1 = "RABBIT.HOLE";
  42     private static final String realmKDC2 = "DEV.RABBIT.HOLE";
  43     private static final char[] password = "123qwe@Z".toCharArray();
  44     private static final String clientName = "test";
  45 
  46     private static final String clientAlias = clientName +
  47             PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC1;
  48 
  49     private static final String clientKDC1QueryName = clientAlias.replaceAll(
  50             PrincipalName.NAME_REALM_SEPARATOR_STR, "\\\\" +
  51             PrincipalName.NAME_REALM_SEPARATOR_STR) +
  52             PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC1;
  53     private static PrincipalName clientKDC1QueryPrincipal = null;
  54     static {
  55         try {
  56             clientKDC1QueryPrincipal = new PrincipalName(
  57                     clientKDC1QueryName, PrincipalName.KRB_NT_ENTERPRISE,
  58                     null);
  59         } catch (Throwable t) {}
  60     }
  61 
  62     private static final String clientKDC2Name = clientName +
  63             PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC2;
  64 
  65     private static final String serviceName = "http" +
  66             PrincipalName.NAME_COMPONENT_SEPARATOR_STR +
  67             "server.dev.rabbit.hole";
  68 
  69     private static Credentials tgt;
  70     private static Credentials tgs;
  71 
  72     public static void main(String[] args) throws Exception {
  73         try {
  74             initializeKDCs();
  75             getTGT();
  76             getTGS();
  77         } finally {
  78             cleanup();
  79         }
  80     }
  81 
  82     private static void initializeKDCs() throws Exception {
  83         KDC kdc1 = KDC.create(realmKDC1, "localhost", 0, true);
  84         kdc1.addPrincipalRandKey(PrincipalName.TGS_DEFAULT_SRV_NAME +
  85                 PrincipalName.NAME_COMPONENT_SEPARATOR_STR + realmKDC1);
  86         kdc1.addPrincipal(PrincipalName.TGS_DEFAULT_SRV_NAME +
  87                 PrincipalName.NAME_COMPONENT_SEPARATOR_STR + realmKDC1 +
  88                 PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC2,
  89                 password);
  90         kdc1.addPrincipal(PrincipalName.TGS_DEFAULT_SRV_NAME +
  91                 PrincipalName.NAME_COMPONENT_SEPARATOR_STR + realmKDC2,
  92                 password);
  93 
  94         KDC kdc2 = KDC.create(realmKDC2, "localhost", 0, true);
  95         kdc2.addPrincipalRandKey(PrincipalName.TGS_DEFAULT_SRV_NAME +
  96                 PrincipalName.NAME_COMPONENT_SEPARATOR_STR + realmKDC2);
  97         kdc2.addPrincipal(clientKDC2Name, password);
  98         kdc2.addPrincipal(serviceName, password);
  99         kdc2.addPrincipal(PrincipalName.TGS_DEFAULT_SRV_NAME +
 100                 PrincipalName.NAME_COMPONENT_SEPARATOR_STR + realmKDC1,
 101                 password);
 102         kdc2.addPrincipal(PrincipalName.TGS_DEFAULT_SRV_NAME +
 103                 PrincipalName.NAME_COMPONENT_SEPARATOR_STR + realmKDC2 +
 104                 PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC1,
 105                 password);
 106 
 107         kdc1.registerAlias(clientAlias, kdc2);
 108         kdc1.registerAlias(serviceName, kdc2);
 109         kdc2.registerAlias(clientAlias, clientKDC2Name);
 110 
 111         KDC.saveConfig(krbConfigName, kdc1, kdc2,
 112                     "forwardable=true");
 113         System.setProperty("java.security.krb5.conf", krbConfigName);
 114     }
 115 
 116     private static void cleanup() {
 117         File f = new File(krbConfigName);
 118         if (f.exists()) {
 119             f.delete();
 120         }
 121     }
 122 
 123     private static void getTGT() throws Exception {
 124         KrbAsReqBuilder builder = new KrbAsReqBuilder(clientKDC1QueryPrincipal,
 125                 password);
 126         tgt = builder.action().getCreds();
 127         builder.destroy();
 128         if (DEBUG) {
 129             System.out.println("TGT");
 130             System.out.println("----------------------");
 131             System.out.println(tgt);
 132             System.out.println("----------------------");
 133         }
 134         if (tgt == null) {
 135             throw new Exception("TGT is null");
 136         }
 137         if (!tgt.getClient().getName().equals(clientKDC2Name)) {
 138             throw new Exception("Unexpected TGT client");
 139         }
 140         String[] tgtServerNames = tgt.getServer().getNameStrings();
 141         if (tgtServerNames.length != 2 || !tgtServerNames[0].equals(
 142                 PrincipalName.TGS_DEFAULT_SRV_NAME) ||
 143                 !tgtServerNames[1].equals(realmKDC2) ||
 144                 !tgt.getServer().getRealmString().equals(realmKDC2)) {
 145             throw new Exception("Unexpected TGT server");
 146         }
 147     }
 148 
 149     private static void getTGS() throws Exception {
 150         tgs = CredentialsUtil.acquireServiceCreds(serviceName +
 151                 PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC1, tgt);
 152         if (DEBUG) {
 153             System.out.println("TGS");
 154             System.out.println("----------------------");
 155             System.out.println(tgs);
 156             System.out.println("----------------------");
 157         }
 158         if (tgs == null) {
 159             throw new Exception("TGS is null");
 160         }
 161         if (!tgs.getClient().getName().equals(clientKDC2Name)) {
 162             throw new Exception("Unexpected TGS client");
 163         }
 164         if (!tgs.getServer().getNameString().equals(serviceName) ||
 165                 !tgs.getServer().getRealmString().equals(realmKDC2)) {
 166             throw new Exception("Unexpected TGS server");
 167         }
 168     }
 169 }