changeset 9132:a147b2084bc3

8011786: Better applet networking Reviewed-by: alanb, chegar
author michaelm
date Thu, 24 Oct 2013 20:39:21 +0100
parents fe1707a836b4
children a0b6e5895464
files src/share/classes/com/sun/nio/sctp/SctpChannel.java src/share/classes/java/lang/SecurityManager.java src/share/classes/java/net/Socket.java src/share/classes/java/net/SocketPermission.java src/share/classes/java/nio/channels/AsynchronousSocketChannel.java src/share/classes/java/nio/channels/SocketChannel.java src/share/classes/sun/nio/ch/AsynchronousSocketChannelImpl.java src/share/classes/sun/nio/ch/SocketChannelImpl.java src/share/classes/sun/rmi/registry/RegistryImpl.java src/share/classes/sun/security/util/SecurityConstants.java src/share/lib/security/java.policy src/share/lib/security/java.security-linux src/share/lib/security/java.security-macosx src/share/lib/security/java.security-solaris src/share/lib/security/java.security-windows src/solaris/classes/sun/nio/ch/sctp/SctpChannelImpl.java
diffstat 16 files changed, 276 insertions(+), 52 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/com/sun/nio/sctp/SctpChannel.java	Thu Oct 24 10:02:26 2013 -0700
+++ b/src/share/classes/com/sun/nio/sctp/SctpChannel.java	Thu Oct 24 20:39:21 2013 +0100
@@ -276,6 +276,11 @@
      *
      * @throws  IOException
      *          If some other I/O error occurs
+     *
+     * @throws  SecurityException
+     *          If a security manager has been installed and its
+     *          {@link SecurityManager#checkListen checkListen} method denies
+     *          the operation
      */
     public abstract SctpChannel bind(SocketAddress local)
         throws IOException;
--- a/src/share/classes/java/lang/SecurityManager.java	Thu Oct 24 10:02:26 2013 -0700
+++ b/src/share/classes/java/lang/SecurityManager.java	Thu Oct 24 20:39:21 2013 +0100
@@ -1114,11 +1114,8 @@
      * calling thread is not allowed to wait for a connection request on
      * the specified local port number.
      * <p>
-     * If port is not 0, this method calls
-     * <code>checkPermission</code> with the
+     * This method calls <code>checkPermission</code> with the
      * <code>SocketPermission("localhost:"+port,"listen")</code>.
-     * If port is zero, this method calls <code>checkPermission</code>
-     * with <code>SocketPermission("localhost:1024-","listen").</code>
      * <p>
      * If you override this method, then you should make a call to
      * <code>super.checkListen</code>
@@ -1131,12 +1128,8 @@
      * @see        #checkPermission(java.security.Permission) checkPermission
      */
     public void checkListen(int port) {
-        if (port == 0) {
-            checkPermission(SecurityConstants.LOCAL_LISTEN_PERMISSION);
-        } else {
-            checkPermission(new SocketPermission("localhost:"+port,
-                SecurityConstants.SOCKET_LISTEN_ACTION));
-        }
+        checkPermission(new SocketPermission("localhost:"+port,
+            SecurityConstants.SOCKET_LISTEN_ACTION));
     }
 
     /**
--- a/src/share/classes/java/net/Socket.java	Thu Oct 24 10:02:26 2013 -0700
+++ b/src/share/classes/java/net/Socket.java	Thu Oct 24 20:39:21 2013 +0100
@@ -272,7 +272,9 @@
      *        {@code zero} for a system selected free port.
      * @exception  IOException  if an I/O error occurs when creating the socket.
      * @exception  SecurityException  if a security manager exists and its
-     *             {@code checkConnect} method doesn't allow the operation.
+     *             {@code checkConnect} method doesn't allow the connection
+     *             to the destination, or if its {@code checkListen} method
+     *             doesn't allow the bind to the local port.
      * @exception  IllegalArgumentException if the port parameter or localPort
      *             parameter is outside the specified range of valid port values,
      *             which is between 0 and 65535, inclusive.
@@ -311,7 +313,9 @@
      *        {@code zero} for a system selected free port.
      * @exception  IOException  if an I/O error occurs when creating the socket.
      * @exception  SecurityException  if a security manager exists and its
-     *             {@code checkConnect} method doesn't allow the operation.
+     *             {@code checkConnect} method doesn't allow the connection
+     *             to the destination, or if its {@code checkListen} method
+     *             doesn't allow the bind to the local port.
      * @exception  IllegalArgumentException if the port parameter or localPort
      *             parameter is outside the specified range of valid port values,
      *             which is between 0 and 65535, inclusive.
@@ -609,6 +613,9 @@
      *                     is already bound.
      * @throws  IllegalArgumentException if bindpoint is a
      *          SocketAddress subclass not supported by this socket
+     * @throws  SecurityException  if a security manager exists and its
+     *          {@code checkListen} method doesn't allow the bind
+     *          to the local port.
      *
      * @since   1.4
      * @see #isBound
@@ -630,6 +637,10 @@
         InetAddress addr = epoint.getAddress();
         int port = epoint.getPort();
         checkAddress (addr, "bind");
+        SecurityManager security = System.getSecurityManager();
+        if (security != null) {
+            security.checkListen(port);
+        }
         getImpl().bind (addr, port);
         bound = true;
     }
--- a/src/share/classes/java/net/SocketPermission.java	Thu Oct 24 10:02:26 2013 -0700
+++ b/src/share/classes/java/net/SocketPermission.java	Thu Oct 24 20:39:21 2013 +0100
@@ -34,6 +34,9 @@
 import java.net.InetAddress;
 import java.security.Permission;
 import java.security.PermissionCollection;
+import java.security.PrivilegedAction;
+import java.security.AccessController;
+import java.security.Security;
 import java.io.Serializable;
 import java.io.ObjectStreamField;
 import java.io.ObjectOutputStream;
@@ -89,6 +92,9 @@
  * form "N-", where <i>N</i> is a port number, signifies all ports
  * numbered <i>N</i> and above, while a specification of the
  * form "-N" indicates all ports numbered <i>N</i> and below.
+ * The special port value {@code 0} refers to the entire <i>ephemeral</i>
+ * port range. This is a fixed range of ports a system may use to
+ * allocate dynamic ports from. The actual range may be system dependent.
  * <p>
  * The possible ways to connect to the host are
  * <pre>
@@ -97,7 +103,8 @@
  * listen
  * resolve
  * </pre>
- * The "listen" action is only meaningful when used with "localhost".
+ * The "listen" action is only meaningful when used with "localhost" and
+ * means the ability to bind to a specified port.
  * The "resolve" action is implied when any of the other actions are present.
  * The action "resolve" refers to host/ip name service lookups.
  * <P>
@@ -176,6 +183,7 @@
     private static final int PORT_MIN = 0;
     private static final int PORT_MAX = 65535;
     private static final int PRIV_PORT_MAX = 1023;
+    private static final int DEF_EPH_LOW = 49152;
 
     // the actions mask
     private transient int mask;
@@ -226,6 +234,14 @@
     private static Debug debug = null;
     private static boolean debugInit = false;
 
+    // ephemeral port range for this system
+    private static final int ephemeralLow = initEphemeralPorts(
+        "low", DEF_EPH_LOW
+    );
+    private static final int ephemeralHigh = initEphemeralPorts(
+        "high", PORT_MAX
+    );
+
     static {
         Boolean tmp = java.security.AccessController.doPrivileged(
                 new sun.security.action.GetBooleanAction("sun.net.trustNameService"));
@@ -360,6 +376,14 @@
     }
 
     /**
+     * Returns true if the permission has specified zero
+     * as its value (or lower bound) signifying the ephemeral range
+     */
+    private boolean includesEphemerals() {
+        return portrange[0] == 0;
+    }
+
+    /**
      * Initialize the SocketPermission object. We don't do any DNS lookups
      * as this point, instead we hold off until the implies method is
      * called.
@@ -850,10 +874,21 @@
         int i,j;
 
         if ((that.mask & RESOLVE) != that.mask) {
-            // check port range
+
+            // check simple port range
             if ((that.portrange[0] < this.portrange[0]) ||
                     (that.portrange[1] > this.portrange[1])) {
+
+                // if either includes the ephemeral range, do full check
+                if (this.includesEphemerals() || that.includesEphemerals()) {
+                    if (!inRange(this.portrange[0], this.portrange[1],
+                                     that.portrange[0], that.portrange[1]))
+                    {
+                                return false;
+                    }
+                } else {
                     return false;
+                }
             }
         }
 
@@ -1168,6 +1203,83 @@
         init(getName(),getMask(actions));
     }
 
+    /**
+     * Check the system/security property for the ephemeral port range
+     * for this system. The suffix is either "high" or "low"
+     */
+    private static int initEphemeralPorts(String suffix, int defval) {
+        return AccessController.doPrivileged(
+            new PrivilegedAction<Integer>(){
+                public Integer run() {
+                    int val = Integer.getInteger(
+                            "jdk.net.ephemeralPortRange."+suffix, -1
+                    );
+                    if (val != -1) {
+                        return val;
+                    } else {
+                        String prop = Security.getProperty(
+                            "network.ephemeralPortRange."+suffix
+                        );
+                        try {
+                                val = Integer.parseInt(prop);
+                        } catch (NumberFormatException e) {
+                            // shouldn't happen
+                            return defval;
+                        }
+                    }
+                    return val;
+                }
+            }
+        );
+    }
+
+    /**
+     * Check if the target range is within the policy range
+     * together with the ephemeral range for this platform
+     * (if policy includes ephemeral range)
+     */
+    private static boolean inRange(
+        int policyLow, int policyHigh, int targetLow, int targetHigh
+    )
+    {
+        if (targetLow == 0) {
+            // check policy includes ephemeral range
+            if (!inRange(policyLow, policyHigh, ephemeralLow, ephemeralHigh)) {
+                return false;
+            }
+            if (targetHigh == 0) {
+                // nothing left to do
+                return true;
+            }
+            // continue check with first real port number
+            targetLow = 1;
+        }
+
+        if (policyLow == 0 && policyHigh == 0) {
+            // ephemeral range only
+            return targetLow >= ephemeralLow && targetHigh <= ephemeralHigh;
+        }
+
+        if (policyLow != 0) {
+            // simple check of policy only
+            return targetLow >= policyLow && targetHigh <= policyHigh;
+        }
+
+        // policyLow == 0 which means possibly two ranges to check
+
+        // first check if policy and ephem range overlap/contiguous
+
+        if (policyHigh >= ephemeralLow - 1) {
+            return targetHigh <= ephemeralHigh;
+        }
+
+        // policy and ephem range do not overlap
+
+        // target range must lie entirely inside policy range or eph range
+
+        return  (targetLow <= policyHigh && targetHigh <= policyHigh) ||
+                (targetLow >= ephemeralLow && targetHigh <= ephemeralHigh);
+    }
     /*
     public String toString()
     {
--- a/src/share/classes/java/nio/channels/AsynchronousSocketChannel.java	Thu Oct 24 10:02:26 2013 -0700
+++ b/src/share/classes/java/nio/channels/AsynchronousSocketChannel.java	Thu Oct 24 20:39:21 2013 +0100
@@ -200,6 +200,10 @@
      * @throws  UnsupportedAddressTypeException     {@inheritDoc}
      * @throws  ClosedChannelException              {@inheritDoc}
      * @throws  IOException                         {@inheritDoc}
+     * @throws  SecurityException
+     *          If a security manager has been installed and its
+     *          {@link SecurityManager#checkListen checkListen} method denies
+     *          the operation
      */
     @Override
     public abstract AsynchronousSocketChannel bind(SocketAddress local)
--- a/src/share/classes/java/nio/channels/SocketChannel.java	Thu Oct 24 10:02:26 2013 -0700
+++ b/src/share/classes/java/nio/channels/SocketChannel.java	Thu Oct 24 20:39:21 2013 +0100
@@ -227,6 +227,10 @@
      * @throws  UnsupportedAddressTypeException     {@inheritDoc}
      * @throws  ClosedChannelException              {@inheritDoc}
      * @throws  IOException                         {@inheritDoc}
+     * @throws  SecurityException
+     *          If a security manager has been installed and its
+     *          {@link SecurityManager#checkListen checkListen} method denies
+     *          the operation
      *
      * @since 1.7
      */
--- a/src/share/classes/sun/nio/ch/AsynchronousSocketChannelImpl.java	Thu Oct 24 10:02:26 2013 -0700
+++ b/src/share/classes/sun/nio/ch/AsynchronousSocketChannelImpl.java	Thu Oct 24 20:39:21 2013 +0100
@@ -428,6 +428,10 @@
                     throw new AlreadyBoundException();
                 InetSocketAddress isa = (local == null) ?
                     new InetSocketAddress(0) : Net.checkAddress(local);
+                SecurityManager sm = System.getSecurityManager();
+                if (sm != null) {
+                    sm.checkListen(isa.getPort());
+                }
                 NetHooks.beforeTcpBind(fd, isa.getAddress(), isa.getPort());
                 Net.bind(fd, isa.getAddress(), isa.getPort());
                 localAddress = Net.localAddress(fd);
--- a/src/share/classes/sun/nio/ch/SocketChannelImpl.java	Thu Oct 24 10:02:26 2013 -0700
+++ b/src/share/classes/sun/nio/ch/SocketChannelImpl.java	Thu Oct 24 20:39:21 2013 +0100
@@ -572,6 +572,10 @@
                         throw new AlreadyBoundException();
                     InetSocketAddress isa = (local == null) ?
                         new InetSocketAddress(0) : Net.checkAddress(local);
+                    SecurityManager sm = System.getSecurityManager();
+                    if (sm != null) {
+                        sm.checkListen(isa.getPort());
+                    }
                     NetHooks.beforeTcpBind(fd, isa.getAddress(), isa.getPort());
                     Net.bind(fd, isa.getAddress(), isa.getPort());
                     localAddress = Net.localAddress(fd);
--- a/src/share/classes/sun/rmi/registry/RegistryImpl.java	Thu Oct 24 10:02:26 2013 -0700
+++ b/src/share/classes/sun/rmi/registry/RegistryImpl.java	Thu Oct 24 20:39:21 2013 +0100
@@ -94,8 +94,23 @@
                         RMIServerSocketFactory ssf)
         throws RemoteException
     {
-        LiveRef lref = new LiveRef(id, port, csf, ssf);
-        setup(new UnicastServerRef2(lref));
+        if (port == Registry.REGISTRY_PORT && System.getSecurityManager() != null) {
+            // grant permission for default port only.
+            try {
+                AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
+                    public Void run() throws RemoteException {
+                        LiveRef lref = new LiveRef(id, port, csf, ssf);
+                        setup(new UnicastServerRef2(lref));
+                        return null;
+                    }
+                }, null, new SocketPermission("localhost:"+port, "listen,accept"));
+            } catch (PrivilegedActionException pae) {
+                throw (RemoteException)pae.getException();
+            }
+        } else {
+            LiveRef lref = new LiveRef(id, port, csf, ssf);
+            setup(new UnicastServerRef2(lref));
+        }
     }
 
     /**
@@ -352,7 +367,7 @@
                         public RegistryImpl run() throws RemoteException {
                             return new RegistryImpl(regPort);
                         }
-                    }, getAccessControlContext());
+                    }, getAccessControlContext(regPort));
             } catch (PrivilegedActionException ex) {
                 throw (RemoteException) ex.getException();
             }
@@ -382,7 +397,7 @@
      * The approach used here is taken from the similar method
      * getAccessControlContext() in the sun.applet.AppletPanel class.
      */
-    private static AccessControlContext getAccessControlContext() {
+    private static AccessControlContext getAccessControlContext(int port) {
         // begin with permissions granted to all code in current policy
         PermissionCollection perms = AccessController.doPrivileged(
             new java.security.PrivilegedAction<PermissionCollection>() {
@@ -404,6 +419,7 @@
          * related classes themselves are more tightly limited by RMI.
          */
         perms.add(new SocketPermission("*", "connect,accept"));
+            perms.add(new SocketPermission("localhost:"+port, "listen,accept"));
 
         perms.add(new RuntimePermission("accessClassInPackage.sun.jvmstat.*"));
         perms.add(new RuntimePermission("accessClassInPackage.sun.jvm.hotspot.*"));
--- a/src/share/classes/sun/security/util/SecurityConstants.java	Thu Oct 24 10:02:26 2013 -0700
+++ b/src/share/classes/sun/security/util/SecurityConstants.java	Thu Oct 24 20:39:21 2013 +0100
@@ -222,5 +222,5 @@
 
     // java.lang.SecurityManager
     public static final SocketPermission LOCAL_LISTEN_PERMISSION =
-        new SocketPermission("localhost:1024-", SOCKET_LISTEN_ACTION);
+        new SocketPermission("localhost:0", SOCKET_LISTEN_ACTION);
 }
--- a/src/share/lib/security/java.policy	Thu Oct 24 10:02:26 2013 -0700
+++ b/src/share/lib/security/java.policy	Thu Oct 24 20:39:21 2013 +0100
@@ -2,48 +2,48 @@
 // Standard extensions get all permissions by default
 
 grant codeBase "file:${{java.ext.dirs}}/*" {
-	permission java.security.AllPermission;
+        permission java.security.AllPermission;
 };
 
 // default permissions granted to all domains
 
-grant { 
-	// Allows any thread to stop itself using the java.lang.Thread.stop()
-	// method that takes no argument.
-	// Note that this permission is granted by default only to remain
-	// backwards compatible.
-	// It is strongly recommended that you either remove this permission
-	// from this policy file or further restrict it to code sources
-	// that you specify, because Thread.stop() is potentially unsafe.
-	// See the API specification of java.lang.Thread.stop() for more 
+grant {
+        // Allows any thread to stop itself using the java.lang.Thread.stop()
+        // method that takes no argument.
+        // Note that this permission is granted by default only to remain
+        // backwards compatible.
+        // It is strongly recommended that you either remove this permission
+        // from this policy file or further restrict it to code sources
+        // that you specify, because Thread.stop() is potentially unsafe.
+        // See the API specification of java.lang.Thread.stop() for more
         // information.
-	permission java.lang.RuntimePermission "stopThread";
+        permission java.lang.RuntimePermission "stopThread";
 
-	// allows anyone to listen on un-privileged ports
-	permission java.net.SocketPermission "localhost:1024-", "listen";
+        // allows anyone to listen on dynamic ports
+        permission java.net.SocketPermission "localhost:0", "listen";
 
-	// "standard" properies that can be read by anyone
+        // "standard" properies that can be read by anyone
 
-	permission java.util.PropertyPermission "java.version", "read";
-	permission java.util.PropertyPermission "java.vendor", "read";
-	permission java.util.PropertyPermission "java.vendor.url", "read";
-	permission java.util.PropertyPermission "java.class.version", "read";
-	permission java.util.PropertyPermission "os.name", "read";
-	permission java.util.PropertyPermission "os.version", "read";
-	permission java.util.PropertyPermission "os.arch", "read";
-	permission java.util.PropertyPermission "file.separator", "read";
-	permission java.util.PropertyPermission "path.separator", "read";
-	permission java.util.PropertyPermission "line.separator", "read";
+        permission java.util.PropertyPermission "java.version", "read";
+        permission java.util.PropertyPermission "java.vendor", "read";
+        permission java.util.PropertyPermission "java.vendor.url", "read";
+        permission java.util.PropertyPermission "java.class.version", "read";
+        permission java.util.PropertyPermission "os.name", "read";
+        permission java.util.PropertyPermission "os.version", "read";
+        permission java.util.PropertyPermission "os.arch", "read";
+        permission java.util.PropertyPermission "file.separator", "read";
+        permission java.util.PropertyPermission "path.separator", "read";
+        permission java.util.PropertyPermission "line.separator", "read";
 
-	permission java.util.PropertyPermission "java.specification.version", "read";
-	permission java.util.PropertyPermission "java.specification.vendor", "read";
-	permission java.util.PropertyPermission "java.specification.name", "read";
+        permission java.util.PropertyPermission "java.specification.version", "read";
+        permission java.util.PropertyPermission "java.specification.vendor", "read";
+        permission java.util.PropertyPermission "java.specification.name", "read";
 
-	permission java.util.PropertyPermission "java.vm.specification.version", "read";
-	permission java.util.PropertyPermission "java.vm.specification.vendor", "read";
-	permission java.util.PropertyPermission "java.vm.specification.name", "read";
-	permission java.util.PropertyPermission "java.vm.version", "read";
-	permission java.util.PropertyPermission "java.vm.vendor", "read";
-	permission java.util.PropertyPermission "java.vm.name", "read";
+        permission java.util.PropertyPermission "java.vm.specification.version", "read";
+        permission java.util.PropertyPermission "java.vm.specification.vendor", "read";
+        permission java.util.PropertyPermission "java.vm.specification.name", "read";
+        permission java.util.PropertyPermission "java.vm.version", "read";
+        permission java.util.PropertyPermission "java.vm.vendor", "read";
+        permission java.util.PropertyPermission "java.vm.name", "read";
 };
 
--- a/src/share/lib/security/java.security-linux	Thu Oct 24 10:02:26 2013 -0700
+++ b/src/share/lib/security/java.security-linux	Thu Oct 24 20:39:21 2013 +0100
@@ -497,3 +497,19 @@
 # Example:
 #   jdk.tls.disabledAlgorithms=MD5, SHA1, DSA, RSA keySize < 2048
 
+#
+# Default ephemeral port ranges (operating system specific)
+# used by java.net.SocketPermission to interpret the meaning of the special
+# port value zero, as in the following example:
+#
+#       SocketPermission("localhost:0" , "listen");
+#
+# These can be overridden by the system properties:
+#
+#       jdk.net.ephemeralPortRange.low and
+#       jdk.net.ephemeralPortRange.high
+#
+# respectively.
+#
+network.ephemeralPortRange.low=32768
+network.ephemeralPortRange.high=65535
--- a/src/share/lib/security/java.security-macosx	Thu Oct 24 10:02:26 2013 -0700
+++ b/src/share/lib/security/java.security-macosx	Thu Oct 24 20:39:21 2013 +0100
@@ -498,3 +498,21 @@
 # Example:
 #   jdk.tls.disabledAlgorithms=MD5, SHA1, DSA, RSA keySize < 2048
 
+
+#
+# Default ephemeral port ranges (operating system specific)
+# used by java.net.SocketPermission to interpret the meaning of the special
+# port value zero, as in the following example:
+#
+#        SocketPermission("localhost:0" , "listen");
+#
+# These can be overridden by the system properties:
+#
+#       jdk.net.ephemeralPortRange.low and
+#       jdk.net.ephemeralPortRange.high
+#
+# respectively.
+#
+network.ephemeralPortRange.low=49152
+network.ephemeralPortRange.high=65535
+
--- a/src/share/lib/security/java.security-solaris	Thu Oct 24 10:02:26 2013 -0700
+++ b/src/share/lib/security/java.security-solaris	Thu Oct 24 20:39:21 2013 +0100
@@ -497,3 +497,20 @@
 # Example:
 #   jdk.tls.disabledAlgorithms=MD5, SHA1, DSA, RSA keySize < 2048
 
+
+#
+# Default ephemeral port ranges (operating system specific)
+# used by java.net.SocketPermission to interpret the meaning of the special
+# port value zero, as in the following example:
+#
+#        SocketPermission("localhost:0" , "listen");
+#
+# These can be overridden by the system properties:
+#
+#       jdk.net.ephemeralPortRange.low and
+#       jdk.net.ephemeralPortRange.high
+#
+# respectively.
+#
+network.ephemeralPortRange.low=32768
+network.ephemeralPortRange.high=65535
--- a/src/share/lib/security/java.security-windows	Thu Oct 24 10:02:26 2013 -0700
+++ b/src/share/lib/security/java.security-windows	Thu Oct 24 20:39:21 2013 +0100
@@ -498,3 +498,19 @@
 # Example:
 #   jdk.tls.disabledAlgorithms=MD5, SHA1, DSA, RSA keySize < 2048
 
+#
+# Default ephemeral port ranges (operating system specific)
+# used by java.net.SocketPermission to interpret the meaning of the special
+# port value zero, as in the following example:
+#
+#        SocketPermission("localhost:0" , "listen");
+#
+# These can be overridden by the system properties:
+#
+#       jdk.net.ephemeralPortRange.low and
+#       jdk.net.ephemeralPortRange.high
+#
+# respectively.
+#
+network.ephemeralPortRange.low=49152
+network.ephemeralPortRange.high=65535
--- a/src/solaris/classes/sun/nio/ch/sctp/SctpChannelImpl.java	Thu Oct 24 10:02:26 2013 -0700
+++ b/src/solaris/classes/sun/nio/ch/sctp/SctpChannelImpl.java	Thu Oct 24 20:39:21 2013 +0100
@@ -187,6 +187,10 @@
                         SctpNet.throwAlreadyBoundException();
                     InetSocketAddress isa = (local == null) ?
                         new InetSocketAddress(0) : Net.checkAddress(local);
+                    SecurityManager sm = System.getSecurityManager();
+                    if (sm != null) {
+                        sm.checkListen(isa.getPort());
+                    }
                     Net.bind(fd, isa.getAddress(), isa.getPort());
                     InetSocketAddress boundIsa = Net.localAddress(fd);
                     port = boundIsa.getPort();