1 /*
   2  * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
   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 import java.io.File;
  25 import java.io.FileOutputStream;
  26 import java.io.IOException;
  27 import java.security.Security;
  28 import javax.security.auth.callback.Callback;
  29 import javax.security.auth.callback.CallbackHandler;
  30 import javax.security.auth.callback.NameCallback;
  31 import javax.security.auth.callback.PasswordCallback;
  32 import sun.security.krb5.Config;
  33 
  34 /**
  35  * This class starts a simple KDC with one realm, several typical principal
  36  * names, generates delete-on-exit krb5.conf and keytab files, and setup
  37  * system properties for them. There's also a helper method to generate a
  38  * JAAS login config file that can be used for JAAS or JGSS apps.
  39  * <p>
  40  * Just call this line to start everything:
  41  * <pre>
  42  * new OneKDC(null).writeJaasConf();
  43  * </pre>
  44  */
  45 public class OneKDC extends KDC {
  46 
  47     public static final String USER = "dummy";
  48     public static final char[] PASS = "bogus".toCharArray();
  49     public static final String USER2 = "foo";
  50     public static final char[] PASS2 = "bar".toCharArray();
  51     public static final String KRB5_CONF = "localkdc-krb5.conf";
  52     public static final String KTAB = "localkdc.ktab";
  53     public static final String JAAS_CONF = "localkdc-jaas.conf";
  54     public static final String REALM = "RABBIT.HOLE";
  55     public static String SERVER = "server/host." + REALM.toLowerCase();
  56     public static String BACKEND = "backend/host." + REALM.toLowerCase();
  57     public static String KDCHOST = "kdc." + REALM.toLowerCase();
  58     /**
  59      * Creates the KDC and starts it.
  60      * @param etype Encryption type, null if not specified
  61      * @throws java.lang.Exception if there's anything wrong
  62      */
  63     public OneKDC(String etype) throws Exception {
  64         super(REALM, KDCHOST, 0, true);
  65         addPrincipal(USER, PASS);
  66         addPrincipal(USER2, PASS2);
  67         addPrincipalRandKey("krbtgt/" + REALM);
  68         addPrincipalRandKey(SERVER);
  69         addPrincipalRandKey(BACKEND);
  70         KDC.saveConfig(KRB5_CONF, this,
  71                 "forwardable = true",
  72                 "default_keytab_name = " + KTAB,
  73                 etype == null ? "" : "default_tkt_enctypes=" + etype + "\ndefault_tgs_enctypes=" + etype);
  74         System.setProperty("java.security.krb5.conf", KRB5_CONF);
  75         // Whatever krb5.conf had been loaded before, we reload ours now.
  76         Config.refresh();
  77 
  78         writeKtab(KTAB);
  79         Security.setProperty("auth.login.defaultCallbackHandler",
  80                 "OneKDC$CallbackForClient");
  81     }
  82 
  83     /**
  84      * Writes a JAAS login config file, which contains as many as useful
  85      * entries, including JGSS style initiator/acceptor and normal JAAS
  86      * entries with names using existing OneKDC principals.
  87      * @throws java.lang.Exception if anything goes wrong
  88      */
  89     public void writeJAASConf() throws IOException {
  90         System.setProperty("java.security.auth.login.config", JAAS_CONF);
  91         File f = new File(JAAS_CONF);
  92         FileOutputStream fos = new FileOutputStream(f);
  93         fos.write((
  94                 "com.sun.security.jgss.krb5.initiate {\n" +
  95                 "    com.sun.security.auth.module.Krb5LoginModule required;\n};\n" +
  96                 "com.sun.security.jgss.krb5.accept {\n" +
  97                 "    com.sun.security.auth.module.Krb5LoginModule required\n" +
  98                 "    principal=\"*\"\n" +
  99                 "    useKeyTab=true\n" +
 100                 "    isInitiator=false\n" +
 101                 "    storeKey=true;\n};\n" +
 102                 "client {\n" +
 103                 "    com.sun.security.auth.module.Krb5LoginModule required;\n};\n" +
 104                 "server {\n" +
 105                 "    com.sun.security.auth.module.Krb5LoginModule required\n" +
 106                 "    principal=\"" + SERVER + "\"\n" +
 107                 "    useKeyTab=true\n" +
 108                 "    storeKey=true;\n};\n" +
 109                 "backend {\n" +
 110                 "    com.sun.security.auth.module.Krb5LoginModule required\n" +
 111                 "    principal=\"" + BACKEND + "\"\n" +
 112                 "    useKeyTab=true\n" +
 113                 "    storeKey=true\n" +
 114                 "    isInitiator=false;\n};\n"
 115                 ).getBytes());
 116         fos.close();
 117     }
 118 
 119     /**
 120      * The default callback handler for JAAS login. Note that this handler is
 121      * hard coded to provide only info for USER1. If you need to provide info
 122      * for another principal, please use Context.fromUserPass() instead.
 123      */
 124     public static class CallbackForClient implements CallbackHandler {
 125         public void handle(Callback[] callbacks) {
 126             String user = OneKDC.USER;
 127             char[] pass = OneKDC.PASS;
 128             for (Callback callback : callbacks) {
 129                 if (callback instanceof NameCallback) {
 130                     System.out.println("Callback for name: " + user);
 131                     ((NameCallback) callback).setName(user);
 132                 }
 133                 if (callback instanceof PasswordCallback) {
 134                     System.out.println("Callback for pass: "
 135                             + new String(pass));
 136                     ((PasswordCallback) callback).setPassword(pass);
 137                 }
 138             }
 139         }
 140     }
 141 }