1 /*
   2  * Copyright (c) 2017, 2018, 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  * @summary Smoke test for JDWP hardening
  27  * @library /test/lib
  28  * @run driver BasicJDWPConnectionTest
  29  */
  30 
  31 import java.io.IOException;
  32 
  33 import java.net.Socket;
  34 import java.net.SocketException;
  35 
  36 import jdk.test.lib.apps.LingeredApp;
  37 
  38 import java.util.ArrayList;
  39 import java.util.regex.Matcher;
  40 import java.util.regex.Pattern;
  41 
  42 
  43 public class BasicJDWPConnectionTest {
  44 
  45     public static int handshake(int port) throws IOException {
  46         // Connect to the debuggee and handshake
  47         int res = -1;
  48         Socket s = null;
  49         try {
  50             s = new Socket("localhost", port);
  51             s.getOutputStream().write("JDWP-Handshake".getBytes("UTF-8"));
  52             byte[] buffer = new byte[24];
  53             res = s.getInputStream().read(buffer);
  54         }
  55         catch (SocketException ex) {
  56             // pass
  57         } finally {
  58             if (s != null) {
  59                 s.close();
  60             }
  61         }
  62         return res;
  63     }
  64 
  65     public static ArrayList<String> prepareCmd(String allowOpt) {
  66          ArrayList<String> cmd = new ArrayList<>();
  67 
  68          String jdwpArgs = "-agentlib:jdwp=transport=dt_socket,server=y," +
  69                            "suspend=n,address=*:0" + allowOpt;
  70          cmd.add(jdwpArgs);
  71          return cmd;
  72     }
  73 
  74     private static Pattern listenRegexp = Pattern.compile("Listening for transport \\b(.+)\\b at address: \\b(\\d+)\\b");
  75     private static int detectPort(String s) {
  76         Matcher m = listenRegexp.matcher(s);
  77         if (!m.find()) {
  78             throw new RuntimeException("Could not detect port from '" + s + "'");
  79         }
  80         // m.group(1) is transport, m.group(2) is port
  81         return Integer.parseInt(m.group(2));
  82     }
  83 
  84     public static void positiveTest(String testName, String allowOpt)
  85         throws InterruptedException, IOException {
  86         System.err.println("\nStarting " + testName);
  87         ArrayList<String> cmd = prepareCmd(allowOpt);
  88 
  89         LingeredApp a = LingeredApp.startApp(cmd);
  90         int res;
  91         try {
  92             res = handshake(detectPort(a.getProcessStdout()));
  93         } finally {
  94             a.stopApp();
  95         }
  96         if (res < 0) {
  97             throw new RuntimeException(testName + " FAILED");
  98         }
  99         System.err.println(testName + " PASSED");
 100     }
 101 
 102     public static void negativeTest(String testName, String allowOpt)
 103         throws InterruptedException, IOException {
 104         System.err.println("\nStarting " + testName);
 105         ArrayList<String> cmd = prepareCmd(allowOpt);
 106 
 107         LingeredApp a = LingeredApp.startApp(cmd);
 108         int res;
 109         try {
 110             res = handshake(detectPort(a.getProcessStdout()));
 111         } finally {
 112             a.stopApp();
 113         }
 114         if (res > 0) {
 115             System.err.println(testName + ": res=" + res);
 116             throw new RuntimeException(testName + " FAILED");
 117         }
 118         System.err.println(testName + ": returned a negative code as expected: " + res);
 119         System.err.println(testName + " PASSED");
 120     }
 121 
 122     public static void badAllowOptionTest(String testName, String allowOpt)
 123         throws InterruptedException, IOException {
 124         System.err.println("\nStarting " + testName);
 125         ArrayList<String> cmd = prepareCmd(allowOpt);
 126 
 127         LingeredApp a;
 128         try {
 129             a = LingeredApp.startApp(cmd);
 130         } catch (IOException ex) {
 131             System.err.println(testName + ": caught expected IOException");
 132             System.err.println(testName + " PASSED");
 133             return;
 134         }
 135         // LingeredApp.startApp is expected to fail, but if not, terminate the app
 136         a.stopApp();
 137         throw new RuntimeException(testName + " FAILED");
 138     }
 139 
 140     public static void DefaultTest() throws InterruptedException, IOException {
 141         // No allow option is the same as the allow option ',allow=*' is passed
 142         String allowOpt = "";
 143         positiveTest("DefaultTest", allowOpt);
 144     }
 145 
 146     static void ExplicitDefaultTest() throws InterruptedException, IOException {
 147         // Explicit permission for connections from everywhere
 148         String allowOpt = ",allow=*";
 149         positiveTest("ExplicitDefaultTest" ,allowOpt);
 150     }
 151 
 152     public static void AllowTest() throws InterruptedException, IOException {
 153         String allowOpt = ",allow=127.0.0.1";
 154         positiveTest("AllowTest", allowOpt);
 155     }
 156 
 157     public static void MultiAllowTest() throws InterruptedException, IOException {
 158         String allowOpt = ",allow=127.0.0.1+10.0.0.0/8+172.16.0.0/12+192.168.0.0/24";
 159         positiveTest("MultiAllowTest", allowOpt);
 160     }
 161 
 162     public static void DenyTest() throws InterruptedException, IOException {
 163         // Bad allow address
 164         String allowOpt = ",allow=0.0.0.0";
 165         negativeTest("DenyTest", allowOpt);
 166     }
 167 
 168     public static void MultiDenyTest() throws InterruptedException, IOException {
 169         // Wrong separator ';' is used for allow option
 170         String allowOpt = ",allow=127.0.0.1;192.168.0.0/24";
 171         badAllowOptionTest("MultiDenyTest", allowOpt);
 172     }
 173 
 174     public static void EmptyAllowOptionTest() throws InterruptedException, IOException {
 175         // Empty allow option
 176         String allowOpt = ",allow=";
 177         badAllowOptionTest("EmptyAllowOptionTest", allowOpt);
 178     }
 179 
 180     public static void ExplicitMultiDefault1Test() throws InterruptedException, IOException {
 181         // Bad mix of allow option '*' with address value
 182         String allowOpt = ",allow=*+allow=127.0.0.1";
 183         badAllowOptionTest("ExplicitMultiDefault1Test", allowOpt);
 184     }
 185 
 186     public static void ExplicitMultiDefault2Test() throws InterruptedException, IOException {
 187         // Bad mix of allow address value with '*'
 188         String allowOpt = ",allow=allow=127.0.0.1+*";
 189         badAllowOptionTest("ExplicitMultiDefault2Test", allowOpt);
 190     }
 191 
 192     public static void main(String[] args) throws Exception {
 193         DefaultTest();
 194         ExplicitDefaultTest();
 195         AllowTest();
 196         MultiAllowTest();
 197         DenyTest();
 198         MultiDenyTest();
 199         EmptyAllowOptionTest();
 200         ExplicitMultiDefault1Test();
 201         ExplicitMultiDefault2Test();
 202         System.err.println("\nTest PASSED");
 203     }
 204 }