- Package:
- libgnutls30
- Source:
- libgnutls30
- Description:
- GNU TLS library - main runtime library
- Submitter:
- Elliott Mitchell
- Date:
- 2024-05-18 20:18:05 UTC
- Severity:
- normal
Long story to finding this one. Trying to get LDAP setup on this
network. As a recent deployment it seemed appropriate to use IPv6.
From `nslcd` on clients I was getting the message:
nslcd[12345]: [1a2b3c] <group/member="root"> failed to bind to LDAP server ldaps://[fd12:3456:7890:abcd::3]/: Can't contact LDAP server: The TLS connection was non-properly terminated.: Resource temporarily unavailable
Running `nslcd` in debug mode failed to yield any additional useful
information.
Once I finally figured out `slapd`'s debug mode ('-h ldaps:/// ldapi:///'
is two arguments, the ldaps and ldapi are a single argument). I got
traces from `slapd`: (serial numbers filed off)
tls_read: want=5, got=5
0000: 16 03 01 01 8f
tls_read: want=399, got=399
0160: ............fd12
0170: :3456:7890:abcd:
0180: :3.-.........@.
TLS: can't accept: A disallowed SNI server name has been received..
connection_read(13): TLS accept failure error=-1 id=1005, closing
Further tracing of the error message appears to point to the function
`_gnutls_dnsname_is_valid()` in gnutls/lib/str.h. Seems libgnutls30 is
incompatible with numeric IPv6 addresses.
While IPv6-only hosts are presently uncommon, there is now quite a bit of
IPv6 traffic in many places. I think this is worthy of having a severity
of "critical" as "bookworm" may remain as "stable" past when there is
more IPv6 traffic than IPv4 traffic. For "trixie" this seems very
likely.
[...] Good morning, I guess you used the IPv6 address as either CN or Subject Alternative Name. Both take names, not IP addresses. There is a different field for IP addresses. gnutls-cli --port 636 fd12:3456:7890:abcd::3 will probably give more info. FWIW I have just generated a local test certificate with "IPAddress:" set to '::1' and things work for me as expected. cu Andreas
successfully established and I was left being able to type to `slapd`. Unfortunately that causes there to be 3 packages which could be the one responsible for the problem. Could be libgnutls30 as I originally suspected. Yet `slapd` and `nslcd` could also be responsible for the problem. The string "A disallowed SNI server name has been received." is found in `libgnutls.so.30`. The string "connection_read(%d): input error=%d id=%lu, closing." is found in `/usr/sbin/slapd`. Anything further is purely guesswork.
[...] [...] [...] [...] Hello, well you could post the complete output of gnutls-cli --port 636 fd12:3456:7890:abcd::3 perhaps even with -d10? I would reassign to openldap then if there are no obvious clues. cu Andreas
affects 1070033 nslcd quit `gnutls-cli` doesn't yield anything obvious. Problem is there are at least 3 packages where the bug could lurk: libgnutls30's API could indicate numeric addresses are legal somewhere, but not accept IPv6 addresses (something gets fed to _gnutls_dnsname_is_valid() which shouldn't be). I notice the libgnutls30 function _gnutls_dnsname_is_valid() will return true for "127.0.0.1". This function is almost certainly wrong as it accepts IPv4 addresses (which are not valid in DNS), but rejects IPv6 addresses. nslcd could be passing something which could be an IP address to the wrong part of the libgnutls30 API. nslcd might also be sending an IP address in LDAP somewhere it is required to send a hostname. slapd could be passing something which could be an IP address to the wrong part of the libgnutls30 API. slapd might also be assuming something in LDAP is a hostname when it is valid to be an IP address. Right now _gnutls_dnsname_is_valid() seems highly suspect.
[...] [...] Hello, Could you please post the requested output, although there are no obvious clues there to your eyes? TIA, cu Andreas
Problem is that provides rather a lot of data about this network setup. The quantity of information is enough for me to be rather uncomfortable with providing it via public channel. I did get the connection to proceed further than before though. If I add the IPv6 address of the LDAP server to /etc/hosts, and then use the hostname instead of IPv6 address for the uri line of /etc/nslcd.conf things get further (I believe over IPv6, but I haven't satisfactorily verified this). This suggests #1070033 is either in libgnutls30 or slapd. The issue could be slapd is passing an IPv6 address to a portion of libgnutls30's API which requires a hostname. The issue could be libgnutls30 rejects IPv6 addresses in some place(s) where they should be valid by the API. I notice the `_gnutls_dnsname_is_valid()` function in gnutls28-3.8.5/lib/str.h accepts IPv4 addresses (which are NOT valid in DNS), but rejects IPv6 addresses.
Then I look deeper and find RFC 6066 (https://www.rfc-editor.org/rfc/rfc6066), page 7: Literal IPv4 and IPv6 addresses are not permitted in "HostName". This suggests there are at least 2, possibly 3 or more bugs. #1 RFC 6066 says neither are legal, yet _gnutls_dnsname_is_valid() accepts IPv4 addresses (including the 32-bit integer version), but rejects IPv6 addresses. This sort of inconsistency leads to security breaches. #2 The gnutls library uses the SNI extension without checking whether it was passed a literal addresses. #3 nslcd always passes the host string provided to its "uri" configuration setting to the gnutls API without checking whether it is a literal address. #1 is definitely a bug present in the libgnutls30 package. At least one of #2 and #3 is definitely a bug, but both may very well be bugs. Seems better to check in the library as it could effect multiple programs using the library.
[...] [...] Hello, At a very bare level an IPv4 address is a valid DNS name (alnum, dashes, and dots), an IPv6 adress is not. That is what gnutls is checking here. Afaict it is a short-cut to save more expensive processing for obvious errors. gnutls_session_get_verify_cert_status() (with gnutls_session_set_verify_cert() set correctly) or gnutls_x509_crt_check_hostname()/gnutls_certificate_verify_peers3() does more elaborate stuff on the data, gnutls_certificate_verify_peers2() requires a separate gnutls_x509_crt_check_hostname(). cu Andreas
No, there isn't any IPv4 address which is a valid DNS name. No top-level domain consists purely of decimal digits, whereas IPv4 addresses consist of purely decimal digits. In fact I don't believe there are any top-level domains which have even a single decimal digit in them. Which seems to argue the more urgent issue is _gnutls_server_name_send_params() needs to do checking of the provided server hostname before sending it as SNI. I've got an initial implementation of this here, but I'm left wondering how far verification should go.
[...] Hello, which is totally irrelevant if my reading (quoted below) that this is not a policy check but a performance optimization is correct. Why is this urgent or even relevant? Certificate checking (client-side) will not accept IP adresses as SNI field. cu Andreas
Most recent change to the line, commit 71d921edc4:
Add GNUTLS_E_RECEIVED_DISALLOWED_NAME for illegal SNI names
An illegal/disallowed SNI server name previously generated
the misleading message "An illegal parameter has been received.".
This commit changes it to
"A disallowed SNI server name has been received.".
That commit message clearly indicates the author was thinking of it as a
policy check.
Not relevant. If the certificate comes from a local file, it is assumed
trusted. If the certificate comes from the server, then it is only
available *after* connection and the SNI has already been sent.
The issue is libgnutls's API requires providing the library with the
server being connected to. libgnutls then assumes the provided server
can be used for SNI, which is untrue (in this case IP addresses violate
RFC 6066).
[...] [...] Hello, You seem to argue that it is major problem for a gnutls client to *send* e.g. "127.0.0.1" as SNI. My point is that this is not a problem but at most uncomely since client-side certificate verification will fail. Even for a trusted certificate name checking is done (if gnutls is correctly used). And this will not succeed if the CN or SAN is an IP address. (I have tried with test certificates and gnutls-cli/-serv. My testing might be flawed of course.) cu Andreas
This is purely hypothetical since this case isn't being observed. What #1070033 is about is, a program was configured to directly connect to a server via IPv6. This address was provided to libgnutls. libgnutls sent the provided address to the server as SNI without verifying it was valid for SNI. The usual approach is be conservative in what you send, but liberal in what you accept. This means libgnutls needs to check whether what is provided is acceptable before sending it, but the server side could allow an IP address which violates RFC 6066. `gnutls-cli` is a very poor simulcrum for this case. `gnutls-cli` does lots of checking which specialized clients may skip. `gnutls-cli` also assumes name service is fully available. Whereas `nslcd` cannot rely on name service being operational as it may provide name service.
[...] Hello, Let's assume a) _gnutls_server_name_send_params() was changed to reject e.g. "127.0.0.1"[1] and b) this stopped libgnutls from sending "127.0.0.1" to the server as SNI. How would this help you, or how is this related to this bug report? In this bug report perhaps an IPv6 address was used which is already rejected by _gnutls_server_name_send_params(). cu Andreas
This is not something I proposed and indeed this wouldn't help me. _gnutls_server_name_recv_params() does some rough filtering which catches IPv6 addresses, but not IPv4 addresses. _gnutls_server_name_send_params() does NO filtering and thus sends both IPv4 and IPv6 addresses. libgnutls is being conservative in what it accepts, but liberal in what it sends. This breaks interoperability.