Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8323517

SSLSocketImpl should not use user_canceled workaround with TLS 1.3

XMLWordPrintable

      ADDITIONAL SYSTEM INFORMATION :
      ❯ java -version
      openjdk version "21.0.1" 2023-10-17 LTS
      OpenJDK Runtime Environment Zulu21.30+15-CA (build 21.0.1+12-LTS)
      OpenJDK 64-Bit Server VM Zulu21.30+15-CA (build 21.0.1+12-LTS, mixed mode, sharing)

      A DESCRIPTION OF THE PROBLEM :
      This is essentially the same issue as JDK-8282600.

      When a TLS connection is closed from a server implemented with Java, a user_cancelled might be sent before the close_notify; apparently as a workaround for some older clients. However, as far as I understand, this is non-compliant behavior as user_cancelled is only allowed during the TLS handshake. Some clients choke on this. In JDK-8282600 this problem was reported for gnutls. However, openssl started to dislike this starting with version 3.2 too.

      In response to JDK-8282600 the code was modified, to only send the user_cancelled when the client side hasn't closed it's end yet. However, I think, that user_cancelled should never be sent for TLS 1.3 as it is not always possible to have the client close its side first.

      Concretely, I'm experiencing this issue with dmarc-metrics-exporter [1] and Greenmail [2]. Greenmail provides an IMAP server for development purposes and is implemented in Java. dmarc-metrics-exporter, implemented in Python, runs test against this server and connects via TLS. When dmarc-metrics-exporter sends an IMAP "LOGOUT" command, the Greenmail server will end the TLS connection with user_cancelled and close_notify. This causes the Python implementation based on openssl of dmarc-metrics-exporter to throw an exception (if using openssl 3.2). I cannot/don't want to close the connection immediately after sending "LOGOUT" to wait for the servers response confirming the logout.

      [1]: https://github.com/jgosmann/dmarc-metrics-exporter
      [2]: https://greenmail-mail-test.github.io/greenmail/

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      1. Create a self-signed certificate:

      keytool -genkey -keyalg RSA -alias selfsigned -keystore server_keystore.jks -storepass your_keystore_password -validity 365 -keysize 2048

      2. Compile and run the following Java code provided as "Source code for an executable test case".

      3. Using openssl 3.2 run the following command:

      openssl s_client -msg localhost:9999

      4. Type some arbitrary input and press enter. This will cause the server to close the connection.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      OpenSSL will exit with the following line indicating a proper termination of the connection:

      closed
      ACTUAL -
      OpenSSL exits with the following line indicating an improper termination of the connection/error:

      read:errno=0

      ---------- BEGIN SOURCE ----------
      import javax.net.ssl.*;
      import java.io.*;
      import java.security.*;
      import java.security.cert.*;

      public class TLSServer {

          public static void main(String[] args) {
              int port = 9999; // Change this to your desired port number
              String keystoreFile = "server_keystore.jks"; // Change this to your keystore file
              String keystorePassword = "your_keystore_password"; // Change this to your keystore password

              try {
                  KeyStore keyStore = KeyStore.getInstance("JKS");
                  keyStore.load(new FileInputStream(keystoreFile), keystorePassword.toCharArray());

                  KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
                  keyManagerFactory.init(keyStore, keystorePassword.toCharArray());

                  SSLContext sslContext = SSLContext.getInstance("TLS");
                  sslContext.init(keyManagerFactory.getKeyManagers(), null, null);

                  SSLServerSocketFactory sslServerSocketFactory = sslContext.getServerSocketFactory();
                  SSLServerSocket serverSocket = (SSLServerSocket) sslServerSocketFactory.createServerSocket(port);

                  System.out.println("Server started. Listening on port " + port);

                  while (true) {
                      SSLSocket clientSocket = (SSLSocket) serverSocket.accept();
                      System.out.println("Client connected");

                      BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
                      String line = in.readLine();
                      System.out.println("Received from client: " + line);

                      in.close();
                      clientSocket.close();
                      break; // Terminate connection after receiving a line from the client
                  }
              } catch (IOException | KeyStoreException | NoSuchAlgorithmException | CertificateException | UnrecoverableKeyException | KeyManagementException e) {
                  e.printStackTrace();
              }
          }
      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      Either of the following works as a workaround:

      - Use OpenSSL 3.1 (or earlier).
      - Disable TLS 1.3 on the client.

      FREQUENCY : always


            Unassigned Unassigned
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            6 Start watching this issue

              Created:
              Updated: