[Bug 2019094] Re: [SRU] Focal: TLS 1.3 doesn't work on strict firewall/middlebox

Mauricio Faria de Oliveira 2019094 at bugs.launchpad.net
Fri Jun 2 15:40:10 UTC 2023


Some details and code analysis path for the comment above.
...

The verification consists of checking the TLS handshake traffic
('host landscape' is a local/test landscape server).

	$ sudo tshark -i eth0 -lV -Y 'tls.handshake and tls.handshake.type == 1' host landscape \
	  | grep -e 'Handshake Type' -e 'Version: TLS' -e 'Session ID'

Example output with TLS 1.3 only, and TLS 1.2 only:

        $ gnutls-cli --priority NORMAL:-VERS-ALL:+VERS-TLS1.3 landscape
</dev/null

        Version: TLS 1.0 (0x0301)
            Handshake Type: Client Hello (1)
            Version: TLS 1.2 (0x0303)
            Session ID Length: 0
                Supported Version: TLS 1.3 (0x0304)

        $ gnutls-cli --priority NORMAL:-VERS-ALL:+VERS-TLS1.2 landscape
</dev/null

        Version: TLS 1.2 (0x0303)
            Handshake Type: Client Hello (1)
            Version: TLS 1.2 (0x0303)
            Session ID Length: 0

The tests for /etc/gnutls/config overrides for default priority string
[2]:

Before:

        $ gnutls-cli landscape </dev/null

        Version: TLS 1.2 (0x0303)
            Handshake Type: Client Hello (1)
            Version: TLS 1.2 (0x0303)
            Session ID Length: 0
                Supported Version: TLS 1.3 (0x0304)
                Supported Version: TLS 1.2 (0x0303)

After:

        $ gnutls-cli landscape </dev/null

	Version: TLS 1.2 (0x0303)
	    Handshake Type: Client Hello (1)
	    Version: TLS 1.2 (0x0303)
	    Session ID Length: 0

	$ cat /etc/gnutls/config
	# Override the build-time default priority string to disable TLS1.3 (s/+VERS-TLS1.3/-VERS-TLS1.3)
	# https://git.launchpad.net/ubuntu/+source/gnutls28/tree/debian/rules?h=ubuntu/focal-updates#n38

	[overrides]
	default-priority-string = NORMAL:-VERS-ALL:-VERS-TLS1.3:+VERS-TLS1.2:+VERS-DTLS1.2:%PROFILE_MEDIUM

However, that (nor the regular blocklist/disabled-versions override)
had any effect on the landscape client.

        $ sudo landscape-config --silent <...>

        Version: TLS 1.0 (0x0301)
            Handshake Type: Client Hello (1)
            Version: TLS 1.2 (0x0303)
            Session ID Length: 0
                Supported Version: TLS 1.3 (0x0304)
                Supported Version: TLS 1.2 (0x0303)
                Supported Version: TLS 1.1 (0x0302)
                Supported Version: TLS 1.0 (0x0301)

I verified that it consumed /etc/gnutls/config with strace:

Before:

	$ grep -i gnutls lds-config.strace.* | grep -v lib
	lds-config.strace.5192:stat("/etc/gnutls/config", 0x7ffe0b549410) = -1 ENOENT (No such file or directory)
	lds-config.strace.5272:stat("/etc/gnutls/config", 0x7ffdd7337810) = -1 ENOENT (No such file or directory)
	lds-config.strace.5456:stat("/etc/gnutls/config", 0x7ffd8d5982c0) = -1 ENOENT (No such file or directory)

After:

	$ grep -i gnutls lds-config.strace.* | grep -v lib
	lds-config.strace.5543:stat("/etc/gnutls/config", {st_mode=S_IFREG|0644, st_size=78, ...}) = 0
	lds-config.strace.5543:openat(AT_FDCWD, "/etc/gnutls/config", O_RDONLY) = 6

It still offered `Supported Version: TLS 1.3` in the handshake (above).

So, the system-wide config [3] seemed to be sufferring an application
override.

The code path is 1) landscape client -> 2) pycurl -> 3) libcurl -> 4)
gnutls:

1) landscape client

@ /usr/lib/python3/dist-packages/landscape/client/broker/transport.py
+55


    def _curl(self, payload, computer_id, exchange_token, message_api):
    ...
        curl = pycurl.Curl()
        return (curl, fetch(self._url, post=True, data=payload,
                            headers=headers, cainfo=self._pubkey, curl=curl))

2) pycurl
3) libcurl

@ curl:lib/vtls/gtls.c

  76 #  if (GNUTLS_VERSION_NUMBER >= 0x020c00)
  ...
  79 #    define USE_GNUTLS_PRIORITY_SET_DIRECT 1
  80 #  endif

  (set based on /usr/include/gnutls/gnutls.h)

  #define GNUTLS_VERSION_NUMBER 0x03060d

Thus, this function version is used, which sets
its own priority string based on CURL_SSLVERSION options.

 438 set_ssl_version_min_max(const char **prioritylist, struct connectdata *conn)
...
 447   switch(ssl_version | ssl_version_max) {
...
 456     case CURL_SSLVERSION_TLSv1_0 | CURL_SSLVERSION_MAX_TLSv1_2:
 457       *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:"
 458                       "+VERS-TLS1.0:+VERS-TLS1.1:+VERS-TLS1.2";
...


 521 gtls_connect_step1(struct connectdata *conn,
...
 694   /* Use default priorities */
 695   rc = gnutls_set_default_priority(session);
...
 758   switch(SSL_CONN_CONFIG(version)) {
...
 770     case CURL_SSLVERSION_TLSv1_0:
 771     case CURL_SSLVERSION_TLSv1_1:
 772     case CURL_SSLVERSION_TLSv1_2:
 773     case CURL_SSLVERSION_TLSv1_3:
 774       {
 775         CURLcode result = set_ssl_version_min_max(&prioritylist, conn);
...
 809     rc = gnutls_priority_set_direct(session, prioritylist, &err);

And the gnutls_priority_set_direct() call overrides the default priority
string/system-wide overrides from the /etc/gnutls/config file, obtained
with the gnutls_set_default_priority() call, per the gnutls docs [5, 6]:

	.. applications using the default settings (c.f.
	gnutls_set_default_priority or 
	gnutls_set_default_priority_append), and provide
	the user with access to priority strings for
	overriding the default behavior, on configuration
	files, or other UI. 

and,

	After the session initialization details on the
	allowed ciphersuites and protocol versions 
	should be set using the priority functions such as
	gnutls_priority_set and gnutls_priority_set_direct.

The doc for gnutls_priority_set_direct() is [7].

...

Therefore, the possibility with existing landscape source code
(used as a workaround) is to tell pycurl, to tell libcurl, to
use different SSL version options, which it tells to gnutls.

And this leads to the workaround mentioned above, to disable
TLS 1.3 by setting SSL_VERSION to 1.0 w/ MAX 1.2 (ie, no 1.3).

@ /usr/lib/python3/dist-packages/landscape/client/broker/transport.py

     def _curl(self, payload, computer_id, exchange_token, message_api):
     ...
         curl = pycurl.Curl()
+        curl.setopt(pycurl.SSLVERSION, pycurl.SSLVERSION_TLSv1_0 | pycurl.SSLVERSION_MAX_TLSv1_2)
         return (curl, fetch(self._url, post=True, data=payload,
                             headers=headers, cainfo=self._pubkey, curl=curl))

Per [1]:

	It is also possible to OR one of the CURL_SSLVERSION_ macros 
	with one of the CURL_SSLVERSION_MAX_ macros.

Links:

[1] https://curl.se/libcurl/c/CURLOPT_SSLVERSION.html
[2] https://gnutls.org/manual/gnutls.html#Overriding-the-default-priority-string
[3] https://gnutls.org/manual/gnutls.html#System_002dwide-configuration-of-the-library
[4] https://gnutls.org/manual/gnutls.html#Application_002dspecific-priority-strings
[5] https://gnutls.org/manual/gnutls.html#Priority-Strings
[6] https://gnutls.org/manual/gnutls.html#Session-initialization
[7] https://gnutls.org/manual/gnutls.html#gnutls_005fpriority_005fset_005fdirect
[8] https://curl.se/libcurl/c/libcurl-env.html

-- 
You received this bug notification because you are a member of Ubuntu
Foundations Bugs, which is subscribed to gnutls28 in Ubuntu.
https://bugs.launchpad.net/bugs/2019094

Title:
  [SRU] Focal: TLS 1.3 doesn't work on strict firewall/middlebox

Status in gnutls28 package in Ubuntu:
  Invalid
Status in gnutls28 source package in Focal:
  In Progress

Bug description:
  [ Impact ]

   * On Focal, the TLS 1.3 handshake might fail on strict
     (or misbehaving) proprietary firewall/middlebox that
     requires a non-empty Session ID (as TLS 1.2) per RFC.

   * The RFC specifies the ClientHello should always have
     a non-empty session ID, but this _is_ empty in Focal.

   * RFC 8446, Appendix D.4. Middlebox Compatibility Mode [1]
     """
     ... a significant number of middleboxes misbehave
     when a TLS client/server pair negotiates TLS 1.3.
     ... handshake look more like a TLS 1.2 handshake:

     -  The client always provides a non-empty session ID
        in the ClientHello, ...
     """

   * Reverse build dependencies that link against the
     static libraries in libgnutls28-dev
     would need No-Change Rebuilds to pick up this fix.
     (see `reverse-depends -b -r focal libgnutls28-dev`)

     However, none were found (details in comment #8).

  [ Test Plan ]

   * Check whether TLS 1.3 handshake has `Session ID:`

     - Focal (no):
        $ gnutls-cli --priority NORMAL:-VERS-ALL:+VERS-TLS1.3 ubuntu.com </dev/null
        ...
        - Description: (TLS1.3-X.509)-...
        - Options:
        - Handshake was completed
        ...

     - Jammy (yes):
        $ gnutls-cli --priority NORMAL:-VERS-ALL:+VERS-TLS1.3 ubuntu.com </dev/null
        ...
        - Description: (TLS1.3-X.509)-...
        - Session ID: CB:7D:DF:...
        - Options:
        - Handshake was completed
        ...

   * Check tests run at build time (`Testsuite summary for GnuTLS`).

     Tests passed per the build log from PPA with test packages:

        ===================================
        Testsuite summary for GnuTLS 3.6.13
        ===================================

   * Check autopkgtests from gnutls28 against PPA/SRU [4,6].

     Tests passed against PPA with test packages:

        autopkgtest [12:40:02]: @@@@@@@@@@@@@@@@@@@@ summary
        run-upstream-testsuite PASS

   * Check autopkgtests from reverse test triggers against PPA/SRU
     (see comment #12).

        $ reverse-depends -b -r focal src:gnutls28
        Reverse-Testsuite-Triggers
        * ...

   * (Internal) Verify the original reporter's proprietary
     firewall/middlebox now works with TLS 1.3 from GnuTLS.

  There is a test package available in the following ppa:

  https://launchpad.net/~mruffell/+archive/ubuntu/sf359157-test

  If you install the test package, the session ID is set
  correctly.

  [ Regression Potential ]

   * TLS 1.3 handshake now includes non-empty Session ID
     in ClientHello, so there's a behavior change in the
     Client side-only, but it does affect how particular
     Servers handle the client, depending on Session ID.

   * Thus, theoretically, if issues were to occur, that
     likely would manifest as client connection errors
     with TLS 1.3 (failures would be realized early and
     fast), and a workaround available is using TLS 1.2.

   * Even though changes to TLS handshake understandably
     may be scary (considering the impact of regressions),
     the proposed change is specified by the RFC (and is
     there to help w/ wider compatibility) and is already
     implemented in later versions (3.7.1 in Hirsute [5]).

  [ Other Info ]

   * Bionic is not impacted (TLS 1.2 only)
   * Jammy and later already fixed (TLS 1.3 on GnuTLS 3.7+)

  The fixes required are:

  commit e0bb98e1f71f94691f600839ff748d3a9f469d3e
  Author: Norbert Pocs <npocs at redhat.com>
  Date:   Fri Oct 30 17:18:30 2020 +0100
  Subject: Fix non-empty session id (TLS13_APPENDIX_D4)
  Link: https://gitlab.com/gnutls/gnutls/-/commit/e0bb98e1f71f94691f600839ff748d3a9f469d3e

  commit 5416fdc259d8df9b797d249f3e5d58789b2e2cf9
  Author: Daiki Ueno <ueno at gnu.org>
  Date:   Wed Feb 3 15:50:08 2021 +0100
  Subject: gnutls_session_is_resumed: don't check session ID in TLS 1.3
  Link: https://gitlab.com/gnutls/gnutls/-/commit/5416fdc259d8df9b797d249f3e5d58789b2e2cf9

  commit 05ee0d49fe93d8812ef220c7b830c4b3553ac4fd
  Author: Daiki Ueno <ueno at gnu.org>
  Date:   Sun Jan 24 07:34:24 2021 +0100
  Subject: handshake: TLS 1.3: don't generate session ID in resumption mode
  Link: https://gitlab.com/gnutls/gnutls/-/commit/05ee0d49fe93d8812ef220c7b830c4b3553ac4fd

  commit 24c9a24640c137b47bb1e8cc5fee2315f57219ad
  Author: Daiki Ueno <ueno at gnu.org>
  Date: Thu, 22 Apr 2021 16:42:01 +0200
  Subject: handshake: don't regenerate legacy_session_id in second CH after HRR
  Link: https://gitlab.com/gnutls/gnutls/-/commit/24c9a24640c137b47bb1e8cc5fee2315f57219ad

  [ Links ]

  [1] https://www.rfc-editor.org/rfc/rfc8446#appendix-D.4
  [4] https://autopkgtest.ubuntu.com/packages/g/gnutls28
  [5] https://launchpad.net/ubuntu/+source/gnutls28/3.7.1-3ubuntu1
  [6] https://autopkgtest.ubuntu.com/results/autopkgtest-focal-mruffell-sf359157-test/focal/amd64/g/gnutls28/20230524_124015_b6884@/log.gz

To manage notifications about this bug go to:
https://bugs.launchpad.net/ubuntu/+source/gnutls28/+bug/2019094/+subscriptions




More information about the foundations-bugs mailing list