Hello Christopher and Tomcat crew

This TLS protocol violation has been seen to cause issues where a client will 
"linger" with the unclosed connection (usually by default for a minute for each 
webservice call)  until Tomcat timesout the connection and closes the socket 
(this "linger" occurs for clients who more strictly follow the TLS protocol 
than others). For clients that require making many webservice calls to a Tomcat 
configured with the Nio+OpenSSL connector, this can extremely slow down the 
client code. 

While a TCP dump is the most conclusive way to demonstrate the fact that the 
Tomcat server does not return the server's close_notify in response to the 
client's close_notify (when using Nio+OpenSSL), I have a Python script that can 
demonstrate the lack of a server close_notify which I have put after my email 
signature. When the ssl_socket_instance.unwrap() is called, the client sends 
its close_notify, and waits for the close_notify from the server, which for 
Nio+JSSE or APR+OpenSSL, is received promptly, but for Nio+OpenSSL, it never 
arrives and instead an EOF on the socket is received after a timeout is reached 
(on the Tomcat side).

Unfortunately, even an extremely verbose curl will not show enough of the ssl 
communication. You can see the different behaviors by sending "Q" to an openssl 
s_client (built with the ability to use the -debug flag).

openssl s_client -connect <host>:<port> -state -debug <<< "Q"

For the Nio+JSSE or Apr+OpenSSL connector, you see the client close_notify go 
out, followed by the client reading the server's close_notify:

---
DONE
write to 0x5b93da131c40 [0x5b93da13cee3] (31 bytes => 31 (0x1F))
0000 - 15 03 03 00 1a 83 0b b7-5a e0 00 ad f3 2c 8f 9c   ........Z....,..
0010 - 13 8c 04 8f 57 48 e9 7a-15 a6 ef 9c b0 16 b2      ....WH.z.......
SSL3 alert write:warning:close notify
read from 0x5b93da131c40 [0x5b93da089670] (8192 bytes => 31 (0x1F))
0000 - 15 03 03 00 1a a0 f4 7f-e5 65 84 ed df 3a a4 ec   .........e...:..
0010 - 76 42 bd c6 37 28 cf 21-03 ca 9a dc 9f 5c 23      vB..7(.!.....\#
read from 0x5b93da131c40 [0x5b93da089670] (8192 bytes => 0)

For the Nio+OpenSSL connector, you will not see the close_notify returned by 
the server.

---
DONE
write to 0x5714dc99ac40 [0x5714dc9a5ee3] (31 bytes => 31 (0x1F))
0000 - 15 03 03 00 1a f9 41 8b-50 31 2f 8b 6c 7a 77 c3   ......A.P1/.lzw.
0010 - 12 ca 2d 15 fc 7d 33 cd-ad ee 5c 3d 23 aa 11      ..-..}3...\=#..
SSL3 alert write:warning:close notify
read from 0x5714dc99ac40 [0x5714dc8f2670] (8192 bytes => 0)

You will not see the same "lingering" behavior though because openssl s_client 
will disconnect quickly if the server does not return a close_notify. 

The Python code after my email signature demonstrates the "lingering" behavior 
seen for some clients which wait for the server's close_notify where the client 
"unwraps" the SSL layer from the socket, but keeps the socket open. 

Please let me know if you'd like any more diagnostic information

Best,
Isaac Klickstein

##########################
##### PYTHON EXAMPLE #####

# USAGE:
# python3 tomcat_ssl_unwrap_example.py <tomcat host> <connector port> 
<username> <password>

import sys
import socket
import ssl
import os
import time
import errno
from urllib.parse import urlparse
import base64

def call_manager_app(url,username,password):

    ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
    ssl_context.verify_mode = ssl.CERT_NONE

    parsed_url = urlparse(url)
    hostname, port = parsed_url.netloc.split(":") if ":" in parsed_url.netloc 
else (parsed_url.netloc, 443)
    credentials = f"{username}:{password}"
    encoded_credentials = base64.b64encode(credentials.encode()).decode()

    socket_instance = socket.socket(socket.AF_INET, socket.SOCK_STREAM);
#    socket_instance.setblocking(True)
    ssl_socket_instance = ssl_context.wrap_socket(socket_instance, 
server_hostname=hostname)
    ssl_socket_instance.settimeout(1)
    ssl_socket_instance.connect((hostname,int(port)))

    request = f"GET {parsed_url.path} HTTP/1.1\r\nHost: {hostname}\r\n"
    request += "Accept: */*\r\n"
    request += f"Authorization: Basic {encoded_credentials}\r\n\r\n"

    ssl_socket_instance.send(request.encode())

    response_data = b""
    while True:
      try:
        data = ssl_socket_instance.recv(16384)
      except socket.timeout:
        break

      response_data += data

    response = response_data.decode()
    headers, body = response.split("\r\n\r\n", 1)

    print("Headers:")
    print(headers)
    print("Body:")
    print(body)
    
    # Unwrap the socket, and waiting for the server's close notify
    ssl_socket_instance.settimeout(120)
    socket_instance = ssl_socket_instance.unwrap()
    socket_instance.close()




host=sys.argv[1]
port=sys.argv[2]
username=sys.argv[3]
password=sys.argv[4]
# Supply the URL of the Tomcat manager page's /text/list endpoint
#url = "https://localhost:9015/manager/text/list";
#
#
url = "https://"; + host + ":" + port + "/manager/text/list"
print(url)

call_manager_app(url,username,password)

######## END OF PYTHON #############
####################################

Sent with Proton Mail secure email.

On Thursday, August 29th, 2024 at 9:05 AM, Christopher Schultz 
<ch...@christopherschultz.net> wrote:

> Isaac,
> 
> On 8/26/24 13:24, Isaac Klickstein wrote:
> 
> > > What is the "Tomcat Native Client"?
> > 
> > I am using the Tomcat Native software (maybe "client" was wrong) available 
> > here to build the OpenSSLImplementation:
> > https://tomcat.apache.org/download-native.cgi#2.0.8
> > 
> > I then link to this library using LD_LIBRARY_PATH in the Tomcat's setenv.sh 
> > script.
> 
> 
> Aah, okay. You are just using libtcnative on the server side. curl is
> the client.
> 
> > > How do you trigger this behavior? Just any request like "curl
> > 
> > I have been using the manager app, something like
> > 
> > curl --cacert </path/to/ca file> --url 'https://<tomcat host>:<tomcat https 
> > connector port>/manager/text/list' --user robot:robotpw
> > 
> > but any request to the ROOT/manager/other hosted would do.
> > 
> > I have been breaking down the behavior based on protocol=NIO/NIO2/APR and 
> > the sslImplementationName JSSE/OpenSSL
> > 
> > NIO/NIO2+JSSE = good
> > NIO/NIO2+OpenSSL = bad
> > APR+OpenSSL = good
> 
> 
> That's interesting. When using APR+OpenSSL, the Tomcat code is entirely
> responsible for the connection management (e.g. socket, buffers, etc.)
> and the crypto (using OpenSSL, of course).
> 
> When using NIO+OpenSSL, Java is responsible for the connection
> management AND the orchestration of the use of the cryptographic module.
> The use of OpenSSL versus some other cryptographic module (e.g. built-in
> JSSE) should not affect whether a close_notify is sent. :/
> 
> > I have TCP dumps for each of these configurations saved and could upload 
> > them as well as the configuration of the connectors.
> 
> 
> Is a TCP dump required to observe this behavior, or will e.g. curl -vvv
> show it as well?
> 
> Is this causing any actual issues in your environment, or are you more
> reporting a spec violation that needs to be cleaned-up. It seems to be
> that if the client asks the server to close the connection, if the
> connection is closed then it's closed whether or not this particular
> message is transmitted before termination of the connection.
> 
> -chris
> 
> > On Monday, August 26th, 2024 at 11:40 AM, Christopher Schultz 
> > ch...@christopherschultz.net wrote:
> > 
> > > Isaac,
> > > 
> > > On 8/25/24 13:27, Isaac Klickstein wrote:
> > > 
> > > > Hello Tomcat Users
> > > > 
> > > > Tomcat Version: 10.1.28
> > > > OpenSSL version: 3.0.14
> > > > Tomcat Native Client: 2.0.8
> > > 
> > > What is the "Tomcat Native Client"?
> > > 
> > > > I have configured an HTTPS connector with the 
> > > > org.apache.coyote.http11.Http11NioProtocol protocol and the 
> > > > org.apache.tomcat.util.net.openssl.OpenSSLImplementation 
> > > > sslImplementationName using TLSv1.2
> > > > 
> > > > When I tcpdump any request to this connector, Tomcat is not returning a 
> > > > "close_notify" in response to a client's close_notify, and I cannot 
> > > > figure out how to force Tomcat to return a close_notify. This seems to 
> > > > be a violation of the TLS protocol which demands both sides issue a 
> > > > close_notify.
> > > 
> > > Careful: both the client and the server are always allowed to be
> > > powered-off before they respond to any network stimulus. This is what
> > > timeouts are for. TLS cannot place any more requirements on the network
> > > peers than TCP has already done.
> > > 
> > > > Recreating this situation, as far as I can tell, only requires 
> > > > combining the Http11NioProtocol with the OpenSSLImplementation (Tomcat9 
> > > > or Tomcat10, TLSv1.2 or TLSv1.3, OpenSSL 3.0, 3.1, and 3.2, all exhibit 
> > > > this behavior).
> > > 
> > > How do you trigger this behavior? Just any request like "curl
> > > https://example.com/"; ?
> > > 
> > > > Other notes, switching the sslImplementationName to 
> > > > org.apache.tomcat.util.net.jsse.JSSEImplementation does return a 
> > > > close_notify by the server in response to the client's close_notify.
> > > > 
> > > > Also, switching back to Tomcat9, and using the 
> > > > org.apache.coyote.http11.Http11AprProtocol, Tomcat also returns a 
> > > > close_notify in response to a client's close_notify.
> > > > 
> > > > I have run out of ideas, googling this behavior has turned up nothing 
> > > > related to Tomcat (although there does appear to be a similar behavior 
> > > > noticed in Netty also using the OpenSSLEngine 
> > > > https://github.com/netty/netty/issues/6167)
> > > > 
> > > > Any help would be greatly appreciated, I am happy to send along any 
> > > > other information that would be informational for diagnostics
> > > 
> > > So...
> > > 
> > > Tomcat 10.1 + NIO/JSSE+OpenSSLImplementation+tcnative = bad
> > > Tomcat 9.0 + APR+tcnative = good
> > > Tomcat 9.0 + NIO/JSSE+OpenSSLImplementation+tcnative = ?
> > > 
> > > -chris
> > > 
> > > ---------------------------------------------------------------------
> > > To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org
> > > For additional commands, e-mail: users-h...@tomcat.apache.org
> > 
> > ---------------------------------------------------------------------
> > To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org
> > For additional commands, e-mail: users-h...@tomcat.apache.org
> 
> 
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org
> For additional commands, e-mail: users-h...@tomcat.apache.org

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org
For additional commands, e-mail: users-h...@tomcat.apache.org

Reply via email to