JDK-8231305 : Clarify security manager behaviour of connected DatagramSocket and DatagramChannel
  • Type: CSR
  • Component: core-libs
  • Sub-Component: java.net
  • Priority: P3
  • Status: Closed
  • Resolution: Approved
  • Fix Versions: 14
  • Submitted: 2019-09-20
  • Updated: 2019-10-01
  • Resolved: 2019-10-01
Related Reports
CSR :  
Description
Summary
-------

The longstanding security manger behaviour of DatagramSocket::receive and DatagramChannel::receive depends on its connection state. If connected, the security check being performed against the remote address is performed once, when connect() is called initially. If not connected, this security check is performed within the receive() method itself, each time it is called.

This difference in security manager behaviour is not apparent in the javadoc for either method, and as a result can be misleading to the user. 

The proposed change is to clarify the spec for both DatagramSocket::receive and DatagramChannel::receive to ensure that this behaviour is explicit. 

Problem
-------

The specification for both DatagramSocket::receive and DatagramChannel::receive  incorrectly implies that
the security manager check is always performed. Or at best, it is
ambiguous about the connected behavior. 

Solution
--------

Update the API documentation to make the user aware that security manager checks will only be performed in receive if the socket is not connected to a remote address.

Specification
-------------

src/java.base/share/classes/java/nio/channels/DatagramChannel.java

DatagramChannel::connect
     
         /**
          * Connects this channel's socket.
          *
          * <p> The channel's socket is configured so that it only receives
          * datagrams from, and sends datagrams to, the given remote <i>peer</i>
          * address.  Once connected, datagrams may not be received from or sent to
          * any other address.  A datagram socket remains connected until it is
          * explicitly disconnected or until it is closed.
          *
          * <p> This method performs exactly the same security checks as the {@link
          * java.net.DatagramSocket#connect connect} method of the {@link
          * java.net.DatagramSocket} class.  That is, if a security manager has been
          * installed then this method verifies that its {@link
          * java.lang.SecurityManager#checkAccept checkAccept} and {@link
          * java.lang.SecurityManager#checkConnect checkConnect} methods permit
          * datagrams to be received from and sent to, respectively, the given
    -     * remote address.
    +     * remote address. Once connected, no further security checks are performed
    +     * for datagrams received from, or sent to, the given remote address. Care
    +     * should be taken to ensure that a connected datagram channel is not shared
    +     * with untrusted code.
          *
          * <p> This method may be invoked at any time.  It will not have any effect
          * on read or write operations that are already in progress at the moment
          * that it is invoked. If this channel's socket is not bound then this method
          * will first cause the socket to be bound to an address that is assigned
          * automatically, as if invoking the {@link #bind bind} method with a
          * parameter of {@code null}. </p>
          *
          * @param  remote
          *         The remote address to which this channel is to be connected
          *
          * @return  This datagram channel
          *
          * @throws ....
         public abstract DatagramChannel connect(SocketAddress remote)
             throws IOException { ... }

DatagramChannel::receive


     
         /**
          * Receives a datagram via this channel.
          *
          * <p> If a datagram is immediately available, or if this channel is in
          * blocking mode and one eventually becomes available, then the datagram is
          * copied into the given byte buffer and its source address is returned.
          * If this channel is in non-blocking mode and a datagram is not
          * immediately available then this method immediately returns
          * {@code null}.
          *
          * <p> The datagram is transferred into the given byte buffer starting at
          * its current position, as if by a regular {@link
          * ReadableByteChannel#read(java.nio.ByteBuffer) read} operation.  If there
          * are fewer bytes remaining in the buffer than are required to hold the
          * datagram then the remainder of the datagram is silently discarded.
          *
          * <p> This method performs exactly the same security checks as the {@link
          * java.net.DatagramSocket#receive receive} method of the {@link
          * java.net.DatagramSocket} class.  That is, if the socket is not connected
          * to a specific remote address and a security manager has been installed
          * then for each datagram received this method verifies that the source's
          * address and port number are permitted by the security manager's {@link
    -     * java.lang.SecurityManager#checkAccept checkAccept} method.  The overhead
    -     * of this security check can be avoided by first connecting the socket via
    -     * the {@link #connect connect} method.
    +     * java.lang.SecurityManager#checkAccept checkAccept} method. Datagrams 
    +     * that are not permitted by the security manager are silently discarded. 
    +     * The overhead of this security check can be avoided by first connecting 
    +     * the socket via the {@link #connect connect} method.
          *
          * <p> This method may be invoked at any time.  If another thread has
          * already initiated a read operation upon this channel, however, then an
          * invocation of this method will block until the first operation is
          * complete. If this channel's socket is not bound then this method will
          * first cause the socket to be bound to an address that is assigned
          * automatically, as if invoking the {@link #bind bind} method with a
          * parameter of {@code null}. </p>
          *
          * @param  dst
          *         The buffer into which the datagram is to be transferred
          *
          * @return  The datagram's source address,
          *          or {@code null} if this channel is in non-blocking mode
          *          and no datagram was immediately available
          *
          * @throws  ClosedChannelException
          *          If this channel is closed
          *
          * @throws  AsynchronousCloseException
          *          If another thread closes this channel
          *          while the read operation is in progress
          *
          * @throws  ClosedByInterruptException
          *          If another thread interrupts the current thread
          *          while the read operation is in progress, thereby
          *          closing the channel and setting the current thread's
          *          interrupt status
          *
    -     * @throws  SecurityException
    -     *          If a security manager has been installed
    -     *          and it does not permit datagrams to be accepted
    -     *          from the datagram's sender
    -     *
          * @throws  IOException
          *          If some other I/O error occurs
          */
         public abstract SocketAddress receive(ByteBuffer dst) throws IOException { ...}


src/java.base/share/classes/java/net/DatagramSocket.java

DatagramSocket::connect

         /**
          * Connects the socket to a remote address for this socket. When a
          * socket is connected to a remote address, packets may only be
          * sent to or received from that address. By default a datagram
          * socket is not connected.
          *
          * <p>If the remote destination to which the socket is connected does not
          * exist, or is otherwise unreachable, and if an ICMP destination unreachable
          * packet has been received for that address, then a subsequent call to
          * send or receive may throw a PortUnreachableException. Note, there is no
          * guarantee that the exception will be thrown.
          *
          * <p> If a security manager has been installed then it is invoked to check
          * access to the remote address. Specifically, if the given {@code address}
          * is a {@link InetAddress#isMulticastAddress multicast address},
          * the security manager's {@link
          * java.lang.SecurityManager#checkMulticast(InetAddress)
          * checkMulticast} method is invoked with the given {@code address}.
          * Otherwise, the security manager's {@link
          * java.lang.SecurityManager#checkConnect(String,int) checkConnect}
          * and {@link java.lang.SecurityManager#checkAccept checkAccept} methods
          * are invoked, with the given {@code address} and {@code port}, to
          * verify that datagrams are permitted to be sent and received
          * respectively.
          *
    -     * <p> When a socket is connected, {@link #receive receive} and
    +     * <p> Care should be taken to ensure that a connected datagram socket
    +     * is not shared with untrusted code.
    +     * When a socket is connected, {@link #receive receive} and
          * {@link #send send} <b>will not perform any security checks</b>
          * on incoming and outgoing packets, other than matching the packet's
          * and the socket's address and port. On a send operation, if the
          * packet's address is set and the packet's address and the socket's
          * address do not match, an {@code IllegalArgumentException} will be
          * thrown. A socket connected to a multicast address may only be used
          * to send packets.
          *
          * @param address the remote address for the socket
          *
          * @param port the remote port for the socket.
          *
          * @throws IllegalArgumentException
          *         if the address is null, or the port is out of range.
          *
          * @throws SecurityException
          *         if a security manager has been installed and it does
          *         not permit access to the given remote address
          *
          * @see #disconnect
          */
         public void connect(InetAddress address, int port) { ... }

DatagramSocket::receive

         /**
          * Receives a datagram packet from this socket. When this method
          * returns, the {@code DatagramPacket}'s buffer is filled with
          * the data received. The datagram packet also contains the sender's
          * IP address, and the port number on the sender's machine.
          * <p>
          * This method blocks until a datagram is received. The
          * {@code length} field of the datagram packet object contains
          * the length of the received message. If the message is longer than
          * the packet's length, the message is truncated.
          * <p>
    -     * If there is a security manager, a packet cannot be received if the
    -     * security manager's {@code checkAccept} method
    -     * does not allow it.
    +     * If there is a security manager, and the socket is not currently
    +     * connected to a remote address, a packet cannot be received if the
    +     * security manager's {@code checkAccept} method does not allow it.
    +     * Datagrams that are not permitted by the security manager are silently
    +     * discarded.
          *
          * @param      p   the {@code DatagramPacket} into which to place
          *                 the incoming data.
          * @throws     IOException  if an I/O error occurs.
          * @throws     SocketTimeoutException  if setSoTimeout was previously called
          *                 and the timeout has expired.
          * @throws     PortUnreachableException may be thrown if the socket is connected
          *             to a currently unreachable destination. Note, there is no guarantee that the
          *             exception will be thrown.
          * @throws     java.nio.channels.IllegalBlockingModeException
          *             if this socket has an associated channel,
          *             and the channel is in non-blocking mode.
          * @see        java.net.DatagramPacket
          * @see        java.net.DatagramSocket
          * @revised 1.4
          * @spec JSR-51
          */
         public synchronized void receive(DatagramPacket p) throws IOException { ...}
Comments
Moving revised CSR to Approved.
01-10-2019

A few questions: So in DatagramSocket::connect, the intended spec change is just adding the informative sentence "Care should be taken to ensure that a connected datagram socket is not shared with untrusted code." For DatagramSocket::receive, what about local addresses, checkAccept is not called?
01-10-2019