2034N/A/*
4125N/A * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
2034N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
2034N/A *
2034N/A * This code is free software; you can redistribute it and/or modify it
2034N/A * under the terms of the GNU General Public License version 2 only, as
2034N/A * published by the Free Software Foundation.
2034N/A *
2034N/A * This code is distributed in the hope that it will be useful, but WITHOUT
2034N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
2034N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
2034N/A * version 2 for more details (a copy is included in the LICENSE file that
2034N/A * accompanied this code).
2034N/A *
2034N/A * You should have received a copy of the GNU General Public License version
2034N/A * 2 along with this work; if not, write to the Free Software Foundation,
2034N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
2034N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
2034N/A */
2034N/A
2034N/Aimport java.io.*;
4125N/Aimport java.net.BindException;
4125N/Aimport java.net.DatagramPacket;
4125N/Aimport java.net.DatagramSocket;
4125N/Aimport java.net.InetAddress;
2034N/Aimport java.util.regex.Matcher;
2034N/Aimport java.util.regex.Pattern;
4125N/Aimport javax.security.auth.login.LoginException;
4125N/Aimport sun.security.krb5.Asn1Exception;
2034N/Aimport sun.security.krb5.Config;
2034N/A
2034N/Apublic class BadKdc {
2034N/A
2034N/A // Matches the krb5 debug output:
2034N/A // >>> KDCCommunication: kdc=kdc.rabbit.hole UDP:14319, timeout=2000,...
2034N/A // ^ kdc# ^ timeout
2034N/A static final Pattern re = Pattern.compile(
2034N/A ">>> KDCCommunication: kdc=kdc.rabbit.hole UDP:(\\d)...., " +
2034N/A "timeout=(\\d)000,");
4125N/A
4125N/A /*
4125N/A * There are several cases this test fails:
4125N/A *
4125N/A * 1. The random selected port is used by another process. No good way to
4125N/A * prevent this happening, coz krb5.conf must be written before KDC starts.
4125N/A * There are two different outcomes:
4125N/A *
4125N/A * a. Cannot start the KDC. A BindException thrown.
4125N/A * b. When trying to access a non-existing KDC, a response is received!
4125N/A * Most likely a Asn1Exception thrown
4125N/A *
4125N/A * 2. Even if a KDC is started, and more than 20 seconds pass by, a timeout
4125N/A * can still happens for the first UDP request. In fact, the KDC did not
4125N/A * received it at all. This happens on almost all platforms, especially
4125N/A * solaris-i586 and solaris-x64.
4125N/A *
4125N/A * To avoid them:
4125N/A *
4125N/A * 1. Catch those exceptions and ignore
4125N/A *
4125N/A * 2. a. Make the timeout longer? useless
4125N/A * b. Read the output carefully, if there is a timeout, it's OK.
4125N/A * Just make sure the retries times and KDCs are correct.
4125N/A * This is tough.
4125N/A * c. Feed the KDC a UDP packet first. The current "solution".
4125N/A */
5496N/A public static void go(String... expected)
2034N/A throws Exception {
4125N/A try {
4125N/A go0(expected);
4125N/A } catch (BindException be) {
4125N/A System.out.println("The random port is used by another process");
4125N/A } catch (LoginException le) {
4125N/A Throwable cause = le.getCause();
4125N/A if (cause instanceof Asn1Exception) {
4125N/A System.out.println("Bad packet possibly from another process");
4125N/A return;
4125N/A }
4125N/A throw le;
4125N/A }
4125N/A }
4125N/A
5496N/A public static void go0(String... expected)
4125N/A throws Exception {
2034N/A System.setProperty("sun.security.krb5.debug", "true");
2034N/A
2034N/A // Make sure KDCs' ports starts with 1 and 2 and 3,
2034N/A // useful for checking debug output.
2034N/A int p1 = 10000 + new java.util.Random().nextInt(10000);
2034N/A int p2 = 20000 + new java.util.Random().nextInt(10000);
2034N/A int p3 = 30000 + new java.util.Random().nextInt(10000);
2034N/A
2034N/A FileWriter fw = new FileWriter("alternative-krb5.conf");
2034N/A
2034N/A fw.write("[libdefaults]\n" +
2034N/A "default_realm = " + OneKDC.REALM + "\n" +
2034N/A "kdc_timeout = 2000\n");
2034N/A fw.write("[realms]\n" + OneKDC.REALM + " = {\n" +
2034N/A "kdc = " + OneKDC.KDCHOST + ":" + p1 + "\n" +
2034N/A "kdc = " + OneKDC.KDCHOST + ":" + p2 + "\n" +
2034N/A "kdc = " + OneKDC.KDCHOST + ":" + p3 + "\n" +
2034N/A "}\n");
2034N/A
2034N/A fw.close();
2034N/A System.setProperty("java.security.krb5.conf", "alternative-krb5.conf");
2034N/A Config.refresh();
2034N/A
2034N/A // Turn on k3 only
2034N/A KDC k3 = on(p3);
2034N/A
2034N/A test(expected[0]);
2034N/A test(expected[1]);
2034N/A Config.refresh();
2034N/A test(expected[2]);
2034N/A
2034N/A k3.terminate(); // shutdown k3
2034N/A on(p2); // k2 is on
2034N/A test(expected[3]);
2034N/A on(p1); // k1 and k2 is on
2034N/A test(expected[4]);
2034N/A }
2034N/A
2034N/A private static KDC on(int p) throws Exception {
2034N/A KDC k = new KDC(OneKDC.REALM, OneKDC.KDCHOST, p, true);
2034N/A k.addPrincipal(OneKDC.USER, OneKDC.PASS);
2034N/A k.addPrincipalRandKey("krbtgt/" + OneKDC.REALM);
4125N/A // Feed a packet to newly started KDC to warm it up
4125N/A System.err.println("-------- IGNORE THIS ERROR MESSAGE --------");
4125N/A new DatagramSocket().send(
4125N/A new DatagramPacket("Hello".getBytes(), 5,
4125N/A InetAddress.getByName(OneKDC.KDCHOST), p));
2034N/A return k;
2034N/A }
2034N/A
5496N/A private static void test(String expected) throws Exception {
4125N/A ByteArrayOutputStream bo = new ByteArrayOutputStream();
5496N/A System.out.println("----------------- TEST -----------------");
4125N/A try {
4125N/A test0(bo, expected);
4125N/A } catch (Exception e) {
4125N/A System.out.println("----------------- ERROR -----------------");
4125N/A System.out.println(new String(bo.toByteArray()));
4125N/A System.out.println("--------------- ERROR END ---------------");
4125N/A throw e;
4125N/A }
4125N/A }
4125N/A
2034N/A /**
2034N/A * One round of test for max_retries and timeout.
2034N/A * @param expected the expected kdc# timeout kdc# timeout...
2034N/A */
5496N/A private static void test0(ByteArrayOutputStream bo, String expected)
4125N/A throws Exception {
2034N/A PrintStream oldout = System.out;
5496N/A boolean failed = false;
2034N/A System.setOut(new PrintStream(bo));
4125N/A try {
4125N/A Context.fromUserPass(OneKDC.USER, OneKDC.PASS, false);
5496N/A } catch (Exception e) {
5496N/A failed = true;
4125N/A } finally {
4125N/A System.setOut(oldout);
4125N/A }
2034N/A
2034N/A String[] lines = new String(bo.toByteArray()).split("\n");
5496N/A StringBuilder sb = new StringBuilder();
2034N/A for (String line: lines) {
2034N/A Matcher m = re.matcher(line);
2034N/A if (m.find()) {
2034N/A System.out.println(line);
5496N/A sb.append(m.group(1)).append(m.group(2));
2034N/A }
2034N/A }
5496N/A if (failed) sb.append('-');
5496N/A
5496N/A String output = sb.toString();
5496N/A System.out.println("Expected: " + expected + ", actual " + output);
5496N/A if (!output.matches(expected)) {
5496N/A throw new Exception("Does not match");
2034N/A }
2034N/A }
2034N/A}