1 /*
   2  * Copyright (c) 2010, 2012, 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 /*
  25  * @test
  26  * @bug 6844193
  27  * @modules java.base/sun.net.spi.nameservice
  28  *          java.base/sun.security.util
  29  *          java.security.jgss/sun.security.krb5
  30  *          java.security.jgss/sun.security.krb5.internal
  31  *          java.security.jgss/sun.security.krb5.internal.ccache
  32  *          java.security.jgss/sun.security.krb5.internal.crypto
  33  *          java.security.jgss/sun.security.krb5.internal.ktab
  34  * @compile -XDignore.symbol.file MaxRetries.java
  35  * @run main/othervm/timeout=300 MaxRetries
  36  * @summary support max_retries in krb5.conf
  37  */
  38 
  39 import java.io.*;
  40 import java.net.DatagramSocket;
  41 import java.security.Security;
  42 
  43 public class MaxRetries {
  44     public static void main(String[] args)
  45             throws Exception {
  46 
  47         System.setProperty("sun.security.krb5.debug", "true");
  48         new OneKDC(null).writeJAASConf();
  49 
  50         // An idle UDP socket to revent PortUnreachableException
  51         DatagramSocket ds = new DatagramSocket(33333);
  52 
  53         System.setProperty("java.security.krb5.conf", "alternative-krb5.conf");
  54 
  55         // For tryLast
  56         Security.setProperty("krb5.kdc.bad.policy", "trylast");
  57         rewriteMaxRetries(4);
  58         test1(4000, 6);         // 1 1 1 1 2 2
  59         test1(4000, 2);         // 2 2
  60 
  61         rewriteMaxRetries(1);
  62         test1(1000, 3);         // 1 2 2
  63         test1(1000, 2);         // 2 2
  64 
  65         rewriteMaxRetries(-1);
  66         test1(5000, 4);         // 1 1 2 2
  67         test1(5000, 2);         // 2 2
  68 
  69         // For tryLess
  70         Security.setProperty("krb5.kdc.bad.policy", "tryless:1," + BadKdc.toReal(5000));
  71         rewriteMaxRetries(4);
  72         test1(4000, 7);         // 1 1 1 1 2 1 2
  73         test1(4000, 4);         // 1 2 1 2
  74 
  75         rewriteMaxRetries(1);
  76         test1(1000, 4);         // 1 2 1 2
  77         test1(1000, 4);         // 1 2 1 2
  78 
  79         rewriteMaxRetries(-1);
  80         test1(5000, 5);         // 1 1 2 1 2
  81         test1(5000, 4);         // 1 2 1 2
  82 
  83         rewriteUdpPrefLimit(-1, -1);    // default, no limit
  84         test2("UDP");
  85 
  86         rewriteUdpPrefLimit(10, -1);    // global rules
  87         test2("TCP");
  88 
  89         rewriteUdpPrefLimit(10, 10000); // realm rules
  90         test2("UDP");
  91 
  92         rewriteUdpPrefLimit(10000, 10); // realm rules
  93         test2("TCP");
  94 
  95         ds.close();
  96     }
  97 
  98     /**
  99      * One round of test for max_retries and timeout.
 100      * @param timeout the expected timeout
 101      * @param count the expected total try
 102      */
 103     private static void test1(int timeout, int count) throws Exception {
 104         String timeoutTag = "timeout=" + BadKdc.toReal(timeout);
 105         ByteArrayOutputStream bo = new ByteArrayOutputStream();
 106         PrintStream oldout = System.out;
 107         System.setOut(new PrintStream(bo));
 108         Context c = Context.fromJAAS("client");
 109         System.setOut(oldout);
 110 
 111         String[] lines = new String(bo.toByteArray()).split("\n");
 112         System.out.println("----------------- TEST (" + timeout + "," +
 113                 count + ") -----------------");
 114         for (String line: lines) {
 115             if (line.startsWith(">>> KDCCommunication")) {
 116                 System.out.println(line);
 117                 if (line.indexOf(timeoutTag) < 0) {
 118                     throw new Exception("Wrong timeout value" + timeoutTag);
 119                 }
 120                 count--;
 121             }
 122         }
 123         if (count != 0) {
 124             throw new Exception("Retry count is " + count + " less");
 125         }
 126     }
 127 
 128     /**
 129      * One round of test for udp_preference_limit.
 130      * @param proto the expected protocol used
 131      */
 132     private static void test2(String proto) throws Exception {
 133         ByteArrayOutputStream bo = new ByteArrayOutputStream();
 134         PrintStream oldout = System.out;
 135         System.setOut(new PrintStream(bo));
 136         Context c = Context.fromJAAS("client");
 137         System.setOut(oldout);
 138 
 139         int count = 2;
 140         String[] lines = new String(bo.toByteArray()).split("\n");
 141         System.out.println("----------------- TEST -----------------");
 142         for (String line: lines) {
 143             if (line.startsWith(">>> KDCCommunication")) {
 144                 System.out.println(line);
 145                 count--;
 146                 if (line.indexOf(proto) < 0) {
 147                     throw new Exception("Wrong timeout value");
 148                 }
 149             }
 150         }
 151         if (count != 0) {
 152             throw new Exception("Retry count is " + count + " less");
 153         }
 154     }
 155 
 156     /**
 157      * Set udp_preference_limit for global and realm
 158      */
 159     private static void rewriteUdpPrefLimit(int global, int realm)
 160             throws Exception {
 161         BufferedReader fr = new BufferedReader(new FileReader(OneKDC.KRB5_CONF));
 162         FileWriter fw = new FileWriter("alternative-krb5.conf");
 163         while (true) {
 164             String s = fr.readLine();
 165             if (s == null) {
 166                 break;
 167             }
 168             if (s.startsWith("[realms]")) {
 169                 // Reconfig global setting
 170                 if (global != -1) {
 171                     fw.write("udp_preference_limit = " + global + "\n");
 172                 }
 173             } else if (s.trim().startsWith("kdc = ")) {
 174                 if (realm != -1) {
 175                     // Reconfig for realm
 176                     fw.write("    udp_preference_limit = " + realm + "\n");
 177                 }
 178             }
 179             fw.write(s + "\n");
 180         }
 181         fr.close();
 182         fw.close();
 183         sun.security.krb5.Config.refresh();
 184     }
 185 
 186     /**
 187      * Set max_retries and timeout value for realm. The global value is always
 188      * 2 and 5000.
 189      * @param value max_retries and timeout/1000 for a realm, -1 means none.
 190      */
 191     private static void rewriteMaxRetries(int value) throws Exception {
 192         BufferedReader fr = new BufferedReader(new FileReader(OneKDC.KRB5_CONF));
 193         FileWriter fw = new FileWriter("alternative-krb5.conf");
 194         while (true) {
 195             String s = fr.readLine();
 196             if (s == null) {
 197                 break;
 198             }
 199             if (s.startsWith("[realms]")) {
 200                 // Reconfig global setting
 201                 fw.write("max_retries = 2\n");
 202                 fw.write("kdc_timeout = " + BadKdc.toReal(5000) + "\n");
 203             } else if (s.trim().startsWith("kdc = ")) {
 204                 if (value != -1) {
 205                     // Reconfig for realm
 206                     fw.write("    max_retries = " + value + "\n");
 207                     fw.write("    kdc_timeout = " + BadKdc.toReal(value*1000) + "\n");
 208                 }
 209                 // Add a bad KDC as the first candidate
 210                 fw.write("    kdc = localhost:33333\n");
 211             }
 212             fw.write(s + "\n");
 213         }
 214         fr.close();
 215         fw.close();
 216         sun.security.krb5.Config.refresh();
 217     }
 218 }