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