RHEL Engineering is moving the tracking of its product development work on RHEL 6 through RHEL 9 to Red Hat Jira (issues.redhat.com). If you're a Red Hat customer, please continue to file support cases via the Red Hat customer portal. If you're not, please head to the "RHEL project" in Red Hat Jira and file new tickets here. Individual Bugzilla bugs in the statuses "NEW", "ASSIGNED", and "POST" are being migrated throughout September 2023. Bugs of Red Hat partners with an assigned Engineering Partner Manager (EPM) are migrated in late September as per pre-agreed dates. Bugs against components "kernel", "kernel-rt", and "kpatch" are only migrated if still in "NEW" or "ASSIGNED". If you cannot log in to RH Jira, please consult article #7032570. That failing, please send an e-mail to the RH Jira admins at rh-issues@redhat.com to troubleshoot your issue as a user management inquiry. The email creates a ServiceNow ticket with Red Hat. Individual Bugzilla bugs that are migrated will be moved to status "CLOSED", resolution "MIGRATED", and set with "MigratedToJIRA" in "Keywords". The link to the successor Jira issue will be found under "Links", have a little "two-footprint" icon next to it, and direct you to the "RHEL project" in Red Hat Jira (issue links are of type "https://issues.redhat.com/browse/RHEL-XXXX", where "X" is a digit). This same link will be available in a blue banner at the top of the page informing you that that bug has been migrated.
Bug 1918473 - java-11-openjdk / rhel-8: TLSv1.3: gnutls-cli: Received alert [90]: User canceled
Summary: java-11-openjdk / rhel-8: TLSv1.3: gnutls-cli: Received alert [90]: User canc...
Keywords:
Status: CLOSED WONTFIX
Alias: None
Product: Red Hat Enterprise Linux 8
Classification: Red Hat
Component: java-11-openjdk
Version: 8.3
Hardware: Unspecified
OS: Unspecified
unspecified
unspecified
Target Milestone: rc
: 8.0
Assignee: Martin Balao
QA Contact: OpenJDK QA
URL:
Whiteboard:
Depends On:
Blocks:
TreeView+ depends on / blocked
 
Reported: 2021-01-20 20:06 UTC by zzambers
Modified: 2022-07-20 07:27 UTC (History)
5 users (show)

Fixed In Version:
Doc Type: If docs needed, set a value
Doc Text:
Clone Of:
Environment:
Last Closed: 2022-07-20 07:27:56 UTC
Type: Bug
Target Upstream Version:
Embargoed:


Attachments (Terms of Use)
ssl-tests-ojdk11-gnutls-user-canceled.txt (3.24 KB, text/plain)
2021-01-20 20:14 UTC, zzambers
no flags Details
tls-1-3-user-canceled-conservative.patch (1.49 KB, application/mbox)
2021-01-26 20:26 UTC, zzambers
no flags Details
sslsocket-javadoc-fix.patch (1.55 KB, application/mbox)
2021-01-26 20:33 UTC, zzambers
no flags Details

Description zzambers 2021-01-20 20:06:22 UTC
The problem was discovered by ssl-tests when gnutls-cli is used as a client and happens when TLSv1.3 is used.

As far as I can tell, all data has been transferred correctly, but problem happens when connection is being closed (and client then exits with non-zero value).

From gnutls-cli log:
...
- Handshake was completed
 
- Simple Client Mode:

*** Received alert [90]: User canceled

From gnutls-cli stderr:
*** Fatal error: A TLS fatal alert has been received.
*** Server has terminated the connection abnormally.


By quick search on the internet I have found this Filezilla bug [2], which looks like same(?) problem, but affecting real-world application. Filezilla developer seems to blame Openjdk's implementation of TLSv1.3 for that issue.


Steps to reproduce (using ssl-tests):
# clone ssl-tests [1] and install gnutls-utils
export JAVA_HOME=...
# test everything
make clean && make SSLTESTS_USE_GNUTLS_CLIENT=1 SSLTESTS_CUSTOM_JAVA_PARAMS='-Dssltests.serverShutdownOutput=0'
# or test specific combination
make clean && make SSLTESTS_USE_GNUTLS_CLIENT=1 SSLTESTS_SSL_CONFIG_FILTER='SunJSSE,TLSv1.3,TLSv1.3,TLS_AES_256_GCM_SHA384' SSLTESTS_CUSTOM_JAVA_PARAMS='-Dssltests.serverShutdownOutput=0'


Openjdk versions tested (all affected):
java-1.8.0-openjdk-1.8.0.275.b01-1.el8_3.x86_64
java-11-openjdk-11.0.9.11-3.el8_3.x86_64
java-latest-openjdk-15.0.1.9-2.rolling.el8.x86_64 (epel)

GnuTLS version tested:
gnutls-3.6.14-7.el8_3.x86_64
gnutls-utils-3.6.14-7.el8_3.x86_64

[1] https://github.com/zzambers/ssl-tests
[2] https://trac.filezilla-project.org/ticket/12099

Comment 1 zzambers 2021-01-20 20:14:02 UTC
Created attachment 1749156 [details]
ssl-tests-ojdk11-gnutls-user-canceled.txt

Comment 2 zzambers 2021-01-20 22:01:50 UTC
I have tried to add this call which is suggested as workaround in Filezilla bug [3]:
socket.shutdownOutput();

I added it to server code here [4] (before end of try block is reached), then it passes.

Interesting however, that closing outputstream at the very same point does not fix the issue.
( Nor does closing both inputstream and outputstream (in any order))

[3] https://trac.filezilla-project.org/ticket/12099#comment:10
[4] https://github.com/zzambers/ssl-tests/blob/574608989efb8bad93e61cb1140698bf934ad841/ssl-tests/src/SSLSocketServer.java#L135

Comment 3 zzambers 2021-01-20 22:21:29 UTC
Javadoc does not give very clear instructions:

"API Note" in SSLSocket javadoc says [5]:
"When the connection is no longer needed, the client and server applications should each close both sides of their respective connection."

Than it continues, that it can be done in one of following ways:
- calling Socket.shutdownOutput()  (Socket.shutdownInput())
  works in my case
- OutputStream.close() (OutputStream.close())
  does not work in my case

But javadoc also says, that calling Socket.close() should have done this implicitly [6],
so closing streams explicitly should not be needed, as I understand it.

[5] https://docs.oracle.com/en/java/javase/11/docs/api/java.base/javax/net/ssl/SSLSocket.html
[6] https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/net/Socket.html#close()

Comment 4 Alicja Kario 2021-01-21 12:31:14 UTC
Java sending user_cancelled alert after handshake completed is a bug: https://tools.ietf.org/html/rfc8446#section-6.1 this alert is applicable only during a handshake.

I think the confusion between shutdowns is because TCP allows for a half-close, TLS before 1.3 does not, TLS 1.3 introduced that behaviour.
So for TLS 1.3, the implementation should call OutputStream.close(), and still attempt reads from the other side. Same with receiving EOF on InputStream, it still needs to explicitly call OutputStream.close() (possibly only after finishing writing queued data); if the API is consistent with protocol support (see section 6.1).

Comment 5 zzambers 2021-01-26 17:58:28 UTC
I have studied OpenJDK code a bit and also have done some reading of TLS RFCs, and as I understand it problem is, that with TLSv1.3 is that connection can be half-closed unlike in prior versions, where other side was required to immediately close the connection after receiving close_notify.

New behaviour of TLSv1.3 reportedly shown problematic for some java programs, expecting old behaviour. This was addressed by [1][2] and some code changes were made. This includes that duplexCloseOutput method [3], which sends user cancelled alert in case of TLSv1.3 as workaround (seems like trying to push the other side to immediately close the connection, in case it was not intending to do so. Also notice this change, which seem to be reacting by immediate close after close notify in case user cancelled was received prior to that [4]). Other than that "API Note" was added to javadoc recommending client and server applications to "close both sides of their respective connection". As stated in the comment section of that openjdk bug [1], these methods then performed half-close:
  InputStream.close()
  OutputStream.close()
  Socket.shutdownInput()
  Socket.shutdownOutput()
while this method performed duplex-close (this does user alert workaround, in case output is still open)
  Socket.close()

Behaviour however changed again by this bug [4] which made InputStream and OutputStream closure equivalent to Socket.close() (duplex-close). But "API note" was not updated and is therefore currently incorrect. This also explains, why closing OutputStream did not solve the issue for me, but Socket.shutdownOutput() did.

[1] https://bugs.openjdk.java.net/browse/JDK-8208526
[2] http://hg.openjdk.java.net/jdk/jdk11/rev/d0e2e34eec65
[3] http://hg.openjdk.java.net/jdk/jdk11/file/1ddf9a99e4ad/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java#l501
[4] http://hg.openjdk.java.net/jdk/jdk11/diff/d0e2e34eec65/src/java.base/share/classes/sun/security/ssl/Alert.java
[5] https://bugs.openjdk.java.net/browse/JDK-8216326

Comment 6 zzambers 2021-01-26 19:32:24 UTC
One other observation is that user_canceled workaround is used even in case when close_notify was received and connection is therfore half-closed (other side has already closed it's output), which is my scenario.

So conservative (safe) approach to fix this bug could be not using user_canceled workaround, when it is not necessary (connection is already half-closed by the other side), as it can actually be harmful as demonstrated. This should be safe and fixes my scenario with gnutls-cli, where server does not explicitly call Socket.shutdownOutput() prior to Socket.close(), but it has already reached the end of input. But there would still likely be a scenario with error on server side when java tls-1.3 client duplex-closes the connection (without first calling Socket.shutdownOutput()) and where tls server does not tolerate user_canceled workaround.

Less conservative approach  (but probably correct one, when it comes to TLS 1.3 RFC) would be to disable/remove user_canceled workaround altogether, but this could reintroduce some issues addressed by JDK-8208526 (See [1] higher).

Comment 7 zzambers 2021-01-26 20:24:33 UTC
I have also made patch for latest jdk (upstream) implementing conservative approach. I have built patched jdk and tested it locally and it indeed fixes bug for my scenario.

Comment 8 zzambers 2021-01-26 20:26:37 UTC
Created attachment 1750999 [details]
tls-1-3-user-canceled-conservative.patch

Comment 9 zzambers 2021-01-26 20:33:48 UTC
Created attachment 1751000 [details]
sslsocket-javadoc-fix.patch

Javadoc fix (API Note) for SSLSocket class to reflect changes by JDK-8216326.

Comment 10 Severin Gehwolf 2021-01-27 17:40:44 UTC
This seems to be related to:
https://bugs.openjdk.java.net/browse/JDK-8253368

which is in latest jdk11u-dev/jdk8u-dev trees. Are you able to reproduce with those trees?

Comment 11 zzambers 2021-01-27 20:52:41 UTC
I reproduced this even on latest jdk built locally, so my build included JDK-82553368. However I think JDK-82553368 is little different issue, as I do not see relation to problem of user_cancelled alert being sent by duplexCloseOutput method for TLSv1.3.

Comment 12 Severin Gehwolf 2021-01-28 09:41:06 UTC
(In reply to zzambers from comment #11)
> I reproduced this even on latest jdk built locally, so my build included
> JDK-82553368. However I think JDK-82553368 is little different issue, as I
> do not see relation to problem of user_cancelled alert being sent by
> duplexCloseOutput method for TLSv1.3.

OK. Sorry for the noise then.

Comment 13 zzambers 2021-03-02 18:33:18 UTC
Btw: As it turned out workaround with explicit call to Socket.shutdownOutput() is also not that great,
as older JDK versions throw this exception (seen on java-1.8.0-openjdk-1.8.0.265.b01-4.el8.x86_64):

java.lang.UnsupportedOperationException: The method shutdownOutput() is not supported in SSLSocket
	at sun.security.ssl.BaseSSLSocketImpl.shutdownOutput(BaseSSLSocketImpl.java:228)
	at SSLSocketServer.serverLoop(SSLSocketServer.java:138)
	at SSLSocketServer$1.run(SSLSocketServer.java:75)
	at java.lang.Thread.run(Thread.java:748)

I guess same would happen on any version JDK7.

So current situation would force server developers to use really ugly workarounds
to be both portable and tls1.3 (+ gnutls) compatible:

try (Socket s = ...) {
    ...
    try {
        s.shutdownOutput();
    } catch (UnsupportedOperationException ex) {
        // ignored
    }
}

Comment 14 Andrew John Hughes 2021-07-01 00:01:09 UTC
zzambers, is this issue still present? Is your patch at a stage we should look at taking it upstream?

Comment 15 zzambers 2021-07-01 19:07:21 UTC
I can still reproduce it with current rpms:
java-1.8.0-openjdk-1.8.0.292.b10-1.el8_4.x86_64
java-11-openjdk-11.0.11.0.9-2.el8_4.x86_64
java-latest-openjdk-16.0.1.0.9-1.rolling.el8.x86_64
+
gnutls-3.6.14-8.el8_3.x86_64

(I can later try on upstream)

tls-1-3-user-canceled-conservative.patch:
I don't plan any changes to this one (as it is already very simple). I believe it should be safe. But I have done only simple testing using ssl-tests.

sslsocket-javadoc-fix.patch:
This one is only about javadoc, as earlier it was possible to do half-close connection (TLSv1.3), by calling InputStream/OutputStream close method, but this behaviour was changed by JDK-8216326. However api note in javadoc was not updated.

Comment 16 zzambers 2022-03-02 19:07:42 UTC
I have proposed fix for this to upstream:
https://github.com/openjdk/jdk/pull/7664

Comment 18 RHEL Program Management 2022-07-20 07:27:56 UTC
After evaluating this issue, there are no plans to address it further or fix it in an upcoming release.  Therefore, it is being closed.  If plans change such that this issue will be fixed in an upcoming release, then the bug can be reopened.


Note You need to log in before you can comment on or make changes to this bug.