1 /*
   2  * Copyright (c) 2019, 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 import java.net.InetAddress;
  25 import java.net.InetSocketAddress;
  26 import java.net.ServerSocket;
  27 import java.net.SocketAddress;
  28 import java.net.SocketPermission;
  29 import java.nio.channels.ServerSocketChannel;
  30 import java.security.AccessControlContext;
  31 import java.security.AllPermission;
  32 import java.security.Permission;
  33 import java.security.Permissions;
  34 import java.security.Policy;
  35 import java.security.PrivilegedAction;
  36 import java.security.PrivilegedExceptionAction;
  37 import java.security.ProtectionDomain;
  38 import org.testng.annotations.BeforeTest;
  39 import org.testng.annotations.Test;
  40 import static java.lang.System.out;
  41 import static java.security.AccessController.*;
  42 import static org.testng.Assert.*;
  43 
  44 /*
  45  * @test
  46  * @bug 8224730
  47  * @summary Check local address access with a security manager
  48  * @run testng/othervm TestLocalAddress
  49  */
  50 
  51 public class TestLocalAddress {
  52 
  53     InetAddress localHost;
  54     ExposedSecurityManager exposedSecurityManager;
  55 
  56     @BeforeTest
  57     public void setup() throws Exception {
  58         localHost = InetAddress.getLocalHost();
  59         out.println("localHost: " + localHost);
  60 
  61         Policy.setPolicy(new AllPermissionsPolicy());
  62         exposedSecurityManager = new ExposedSecurityManager();
  63         System.setSecurityManager(exposedSecurityManager);
  64         out.println("Security manager set");
  65     }
  66 
  67     @Test
  68     public void serverSocketNoSecurityManager() throws Exception {
  69         out.println("\n\n--- serverSocketNoSecurityManager ---");
  70         try (ServerSocket ss = new ServerSocket()) {
  71             testWithNoSecurityManager(ss);
  72         }
  73     }
  74 
  75     @Test
  76     public void socketAdapterNoSecurityManager() throws Exception {
  77         out.println("\n\n--- socketAdapterNoSecurityManager ---");
  78         try (ServerSocket ss = ServerSocketChannel.open().socket()) {
  79             testWithNoSecurityManager(ss);
  80         }
  81     }
  82 
  83     void testWithNoSecurityManager(ServerSocket ss) throws Exception {
  84         final SecurityManager sm = System.getSecurityManager();
  85         System.setSecurityManager(null);
  86         try {
  87             ss.bind(new InetSocketAddress(localHost, 0));
  88 
  89             var localSocketAddr = ((InetSocketAddress)ss.getLocalSocketAddress());
  90             var localInetAddress = ss.getInetAddress();
  91             assertEquals(localInetAddress, localSocketAddr.getAddress());
  92             if (!(localHost.equals(InetAddress.getLoopbackAddress())))
  93                 assertNotEquals(localInetAddress, InetAddress.getLoopbackAddress());
  94 
  95             // toString
  96             String s = ss.toString();
  97             out.println("toString returned:" + s);
  98             assertTrue(s.contains(localInetAddress.toString()),
  99                     "Expected [" + localInetAddress + "] in " + s);
 100 
 101         } finally {
 102             System.setSecurityManager(sm);
 103         }
 104     }
 105 
 106     @Test
 107     public void serverSocketNoPermissions() throws Exception {
 108         out.println("\n\n--- serverSocketNoPermissions ---");
 109         try (ServerSocket ss = new ServerSocket()) {
 110             testWithNoPermissions(ss);
 111         }
 112     }
 113 
 114     @Test
 115     public void socketAdapterNoPermissions() throws Exception {
 116         out.println("\n\n--- socketAdapterNoPermissions ---");
 117         try (ServerSocket ss = ServerSocketChannel.open().socket()) {
 118             testWithNoPermissions(ss);
 119         }
 120     }
 121 
 122     void testWithNoPermissions(ServerSocket ss) throws Exception {
 123         ss.bind(new InetSocketAddress(localHost, 0));
 124 
 125         PrivilegedExceptionAction<SocketAddress> pa = ss::getLocalSocketAddress;
 126         var localSocketAddr = (InetSocketAddress) doPrivileged(pa, noPermissions());
 127         assertSecurityManagerCalled();
 128         PrivilegedExceptionAction<InetAddress> pa1 = ss::getInetAddress;
 129         var localInetAddress = doPrivileged(pa1, noPermissions());
 130         assertSecurityManagerCalled();
 131 
 132         assertEquals(localInetAddress, localSocketAddr.getAddress());
 133         assertEquals(localInetAddress, InetAddress.getLoopbackAddress());
 134 
 135         // toString
 136         PrivilegedExceptionAction<String> pa2 = ss::toString;
 137         String s = doPrivileged(pa2, noPermissions());
 138         assertSecurityManagerCalled();
 139         out.println("toString returned:" + s);
 140         assertTrue(s.contains(localInetAddress.toString()),
 141                 "Expected [" + localInetAddress + "] in " + s);
 142     }
 143 
 144 
 145     @Test
 146     public void serverSocketFineGrainPermissions() throws Exception {
 147         out.println("\n\n--- serverSocketFineGrainPermissions ---");
 148         try (ServerSocket ss = new ServerSocket()) {
 149             testWithFineGrainPermissions(ss);
 150         }
 151     }
 152 
 153     @Test
 154     public void socketAdapterFineGrainPermissions() throws Exception {
 155         out.println("\n\n--- socketAdapterFineGrainPermissions ---");
 156         try (ServerSocket ss = ServerSocketChannel.open().socket()) {
 157             testWithFineGrainPermissions(ss);
 158         }
 159     }
 160 
 161     void testWithFineGrainPermissions(ServerSocket ss) throws Exception {
 162         AccessControlContext connectPermission = withPermissions(
 163                 new SocketPermission(localHost.getHostName(), "connect")
 164         );
 165         ss.bind(new InetSocketAddress(localHost, 0));
 166 
 167         PrivilegedExceptionAction<SocketAddress> pa = ss::getLocalSocketAddress;
 168         var localSocketAddr = (InetSocketAddress) doPrivileged(pa, connectPermission);
 169         assertSecurityManagerCalled();
 170         PrivilegedExceptionAction<InetAddress> pa1 = ss::getInetAddress;
 171         var localInetAddress = doPrivileged(pa1, connectPermission);
 172         assertSecurityManagerCalled();
 173 
 174         assertEquals(localInetAddress, localSocketAddr.getAddress());
 175         assertEquals(localInetAddress, localHost);
 176 
 177         // toString
 178         PrivilegedExceptionAction<String> pa2 = ss::toString;
 179         String s = doPrivileged(pa2, connectPermission);
 180         assertSecurityManagerCalled();
 181         out.println("toString returned:" + s);
 182         assertTrue(s.contains(localInetAddress.toString()),
 183                 "Expected [" + localInetAddress + "] in " + s);
 184     }
 185 
 186 
 187     @Test
 188     public void serverSocketUnbound() throws Exception {
 189         out.println("\n\n--- serverSocketUnbound ---");
 190         try (ServerSocket ss = new ServerSocket()) {
 191             testUnbound(ss);
 192         }
 193     }
 194 
 195     @Test
 196     public void socketAdapterUnbound() throws Exception {
 197         out.println("\n\n--- socketAdapterUnbound ---");
 198         try (ServerSocket ss = ServerSocketChannel.open().socket()) {
 199             testUnbound(ss);
 200         }
 201     }
 202 
 203     void testUnbound(ServerSocket ss) {
 204         assert !ss.isBound();
 205         exposedSecurityManager.reset();
 206         assertEquals(ss.getLocalSocketAddress(), null);
 207         assertEquals(exposedSecurityManager.port, -999);
 208         assertEquals(ss.getInetAddress(), null);
 209         assertEquals(exposedSecurityManager.port, -999);
 210         String s = ss.toString();
 211         assertEquals(exposedSecurityManager.port, -999);
 212         out.println("toString returned:" + s);
 213         assertTrue(s.contains("unbound"), "Expected [unbound] in " + s);
 214     }
 215 
 216     // A security manager that allows inspection of checkConnect's host/port.
 217     static class ExposedSecurityManager extends SecurityManager {
 218         volatile String host;
 219         volatile int port;
 220         ExposedSecurityManager() {
 221             reset();
 222         }
 223         @Override
 224         public void checkConnect(String host, int port) {
 225             this.host = host;
 226             this.port = port;
 227             super.checkConnect(host, port);
 228         }
 229         void reset() {
 230             host = "reset";
 231             port = -999;
 232         }
 233     }
 234 
 235     void assertSecurityManagerCalled() {
 236         assertEquals(exposedSecurityManager.port, -1);
 237         assertEquals(exposedSecurityManager.host, localHost.getHostAddress());
 238         exposedSecurityManager.reset();
 239     }
 240 
 241     @Test
 242     // Ensures that the test machinery is operating as expected.
 243     public void sanity() {
 244         PrivilegedAction<?> connectAction = () -> {
 245             System.getSecurityManager().checkConnect("example.com", 80);
 246             return null;
 247         };
 248 
 249         try {
 250             doPrivileged(connectAction, allPermissions());
 251         } catch (SecurityException unexpected) {
 252             throw unexpected;
 253         }
 254         try {
 255             doPrivileged(connectAction, noPermissions());
 256             fail("Expected exception not thrown");
 257         } catch (SecurityException expected) { }
 258         try {
 259             doPrivileged(connectAction,
 260                     withPermissions(new SocketPermission("example.com:80", "connect")));
 261         } catch (SecurityException unexpected) {
 262             throw unexpected;
 263         }
 264     }
 265 
 266     static AccessControlContext withPermissions(Permission... perms) {
 267         Permissions p = new Permissions();
 268         for (Permission perm : perms) {
 269             p.add(perm);
 270         }
 271         ProtectionDomain pd = new ProtectionDomain(null, p);
 272         return new AccessControlContext(new ProtectionDomain[]{ pd });
 273     }
 274 
 275     static AccessControlContext allPermissions() {
 276         return withPermissions(new AllPermission());
 277     }
 278 
 279     static AccessControlContext noPermissions() {
 280         return withPermissions(/*empty*/);
 281     }
 282 
 283     // A Policy that implies all permissions.
 284     static class AllPermissionsPolicy extends Policy {
 285         public boolean implies(ProtectionDomain domain, Permission permission) {
 286             return true;
 287         }
 288     }
 289 }