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