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