Discussion:
nc(1) fails the tls handshake when destination ends with a full stop
Daniel Jakots
2021-05-30 02:37:18 UTC
Permalink
Hi,

$ nc -zvc openbsd.org 443 # works as expected
Connection to openbsd.org (129.128.5.194) 443 port [tcp/https] succeeded!
TLS handshake negotiated TLSv1.3/AEAD-AES256-GCM-SHA384 with host openbsd.org
[...]

$ nc -zvc openbsd.org. 443 # fails
Connection to openbsd.org. (129.128.5.194) 443 port [tcp/https] succeeded!
nc: tls handshake failed (handshake failed: error:1404B42E:SSL routines:ST_CONNECT:tlsv1 alert protocol version)


And FWIW I get a different error when the destination runs nginx:

$ nc -zvc px.chown.me. 443
Connection to px.chown.me. (198.48.202.221) 443 port [tcp/https] succeeded!
nc: tls handshake failed (handshake failed: error:1404B417:SSL routines:ST_CONNECT:sslv3 alert illegal parameter)

I checked with -Tnoname to be sure, and it didn't change anything.

Is that normal?

Cheers,
Daniel
Theo Buehler
2021-05-30 11:56:08 UTC
Permalink
Post by Daniel Jakots
Hi,
$ nc -zvc openbsd.org 443 # works as expected
Connection to openbsd.org (129.128.5.194) 443 port [tcp/https] succeeded!
TLS handshake negotiated TLSv1.3/AEAD-AES256-GCM-SHA384 with host openbsd.org
[...]
$ nc -zvc openbsd.org. 443 # fails
Connection to openbsd.org. (129.128.5.194) 443 port [tcp/https] succeeded!
nc: tls handshake failed (handshake failed: error:1404B42E:SSL routines:ST_CONNECT:tlsv1 alert protocol version)
$ nc -cvz -e openbsd.org openbsd.org. 443 # works

Unless -e is given, nc uses 'destination' in its server name indication
(SNI) extension. By its specification, (RFC 6066, section 3) the SNI
does not contain the trailing dot.

The server will treat the name with a trailing dot illegal and send an
illegal_parameter alert. For some reason this alert is sent with a
record version of TLS1_VERSION instead of TLS1_2_VERSION as required by
the TLSv1.3 spec (that may be a bug), so while processing the illegal
parameter alert, the client's record layer throws a protocol version
error.
Post by Daniel Jakots
$ nc -zvc px.chown.me. 443
Connection to px.chown.me. (198.48.202.221) 443 port [tcp/https] succeeded!
nc: tls handshake failed (handshake failed: error:1404B417:SSL routines:ST_CONNECT:sslv3 alert illegal parameter)
This has less to do with the fact that it's nginx and more with the fact
that you configured it to use TLSv1.2, so the illegal_parameter alert is
received without error.
Post by Daniel Jakots
I checked with -Tnoname to be sure, and it didn't change anything.
This disables a check whether the server's certificate matches the
server name sent in the SNI extension which comes later in the handshake.
Post by Daniel Jakots
Is that normal?
I think so.
Stuart Henderson
2021-05-30 14:31:55 UTC
Permalink
Post by Theo Buehler
Post by Daniel Jakots
Hi,
$ nc -zvc openbsd.org 443 # works as expected
Connection to openbsd.org (129.128.5.194) 443 port [tcp/https] succeeded!
TLS handshake negotiated TLSv1.3/AEAD-AES256-GCM-SHA384 with host openbsd.org
[...]
$ nc -zvc openbsd.org. 443 # fails
Connection to openbsd.org. (129.128.5.194) 443 port [tcp/https] succeeded!
nc: tls handshake failed (handshake failed: error:1404B42E:SSL routines:ST_CONNECT:tlsv1 alert protocol version)
$ nc -cvz -e openbsd.org openbsd.org. 443 # works
Unless -e is given, nc uses 'destination' in its server name indication
(SNI) extension. By its specification, (RFC 6066, section 3) the SNI
does not contain the trailing dot.
Should something (libtls perhaps; ftp(1) is affected too) strip the dot?
curl does handle this.
Theo Buehler
2021-05-30 15:45:22 UTC
Permalink
Post by Stuart Henderson
Post by Theo Buehler
Post by Daniel Jakots
Hi,
$ nc -zvc openbsd.org 443 # works as expected
Connection to openbsd.org (129.128.5.194) 443 port [tcp/https] succeeded!
TLS handshake negotiated TLSv1.3/AEAD-AES256-GCM-SHA384 with host openbsd.org
[...]
$ nc -zvc openbsd.org. 443 # fails
Connection to openbsd.org. (129.128.5.194) 443 port [tcp/https] succeeded!
nc: tls handshake failed (handshake failed: error:1404B42E:SSL routines:ST_CONNECT:tlsv1 alert protocol version)
$ nc -cvz -e openbsd.org openbsd.org. 443 # works
Unless -e is given, nc uses 'destination' in its server name indication
(SNI) extension. By its specification, (RFC 6066, section 3) the SNI
does not contain the trailing dot.
Should something (libtls perhaps; ftp(1) is affected too) strip the dot?
curl does handle this.
Unsure. If people really think this is useful and necessary, I can be
convinced. It's easy enough to do. And you're right, curl strips the
trailing dot after resolving a host name for SNI and HTTP host header.

I would be against doing it server side since contrary to sending IP
addresses it doesn't seem to be common, but doing this client side seems
innocuous.

Index: tls_client.c
===================================================================
RCS file: /cvs/src/lib/libtls/tls_client.c,v
retrieving revision 1.45
diff -u -p -r1.45 tls_client.c
--- tls_client.c 19 Mar 2018 16:34:47 -0000 1.45
+++ tls_client.c 30 May 2021 15:26:54 -0000
@@ -279,6 +279,7 @@ static int
tls_connect_common(struct tls *ctx, const char *servername)
{
union tls_addr addrbuf;
+ size_t servername_len;
int rv = -1;

if ((ctx->flags & TLS_CLIENT) == 0) {
@@ -291,6 +292,12 @@ tls_connect_common(struct tls *ctx, cons
tls_set_errorx(ctx, "out of memory");
goto err;
}
+
+ /* If there's a trailing dot, strip it. */
+ servername_len = strlen(servername);
+ if (servername_len > 0 &&
+ ctx->servername[servername_len - 1] == '.')
+ ctx->servername[servername_len - 1] = '\0';
}

if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) {
@@ -306,7 +313,7 @@ tls_connect_common(struct tls *ctx, cons
goto err;

if (ctx->config->verify_name) {
- if (servername == NULL) {
+ if (ctx->servername == NULL) {
tls_set_errorx(ctx, "server name not specified");
goto err;
}
@@ -353,10 +360,11 @@ tls_connect_common(struct tls *ctx, cons
* RFC4366 (SNI): Literal IPv4 and IPv6 addresses are not
* permitted in "HostName".
*/
- if (servername != NULL &&
- inet_pton(AF_INET, servername, &addrbuf) != 1 &&
- inet_pton(AF_INET6, servername, &addrbuf) != 1) {
- if (SSL_set_tlsext_host_name(ctx->ssl_conn, servername) == 0) {
+ if (ctx->servername != NULL &&
+ inet_pton(AF_INET, ctx->servername, &addrbuf) != 1 &&
+ inet_pton(AF_INET6, ctx->servername, &addrbuf) != 1) {
+ if (SSL_set_tlsext_host_name(ctx->ssl_conn,
+ ctx->servername) == 0) {
tls_set_errorx(ctx, "server name indication failure");
goto err;
}
Index: tls_server.c
===================================================================
RCS file: /cvs/src/lib/libtls/tls_server.c,v
retrieving revision 1.45
diff -u -p -r1.45 tls_server.c
--- tls_server.c 13 May 2019 22:36:01 -0000 1.45
+++ tls_server.c 30 May 2021 15:28:10 -0000
@@ -109,7 +109,7 @@ tls_servername_cb(SSL *ssl, int *al, voi
inet_pton(AF_INET6, name, &addrbuf) == 1)
return (SSL_TLSEXT_ERR_NOACK);

- free((char *)conn_ctx->servername);
+ free(conn_ctx->servername);
if ((conn_ctx->servername = strdup(name)) == NULL)
goto err;
Daniel Jakots
2021-05-30 17:43:54 UTC
Permalink
Post by Theo Buehler
Unsure. If people really think this is useful and necessary, I can be
convinced. It's easy enough to do. And you're right, curl strips the
trailing dot after resolving a host name for SNI and HTTP host header.
Given the current error message makes it hard to understand what the
problem is, I think it's nicer to fix the user error like curl(1) does.

Thanks,
Daniel
Theo Buehler
2021-05-30 17:55:42 UTC
Permalink
Post by Daniel Jakots
Post by Theo Buehler
Unsure. If people really think this is useful and necessary, I can be
convinced. It's easy enough to do. And you're right, curl strips the
trailing dot after resolving a host name for SNI and HTTP host header.
Given the current error message makes it hard to understand what the
problem is, I think it's nicer to fix the user error like curl(1) does.
What I do not quite see is why you would want or expect to be able to
have a trailing dot there. None of nc's examples have it and in ftp/curl
it seems even weirder.
Daniel Jakots
2021-05-30 18:19:18 UTC
Permalink
This post might be inappropriate. Click to display it.
Florian Obser
2021-05-31 06:31:28 UTC
Permalink
Post by Theo Buehler
Post by Daniel Jakots
Post by Theo Buehler
Unsure. If people really think this is useful and necessary, I can be
convinced. It's easy enough to do. And you're right, curl strips the
trailing dot after resolving a host name for SNI and HTTP host header.
Given the current error message makes it hard to understand what the
problem is, I think it's nicer to fix the user error like curl(1) does.
What I do not quite see is why you would want or expect to be able to
have a trailing dot there. None of nc's examples have it and in ftp/curl
it seems even weirder.
It's the name of the thing (RFC 8499):
Fully-Qualified Domain Name (FQDN): This is often just a clear way
of saying the same thing as "domain name of a node", as outlined
above. However, the term is ambiguous. Strictly speaking, a
fully-qualified domain name would include every label, including
the zero-length label of the root: such a name would be written
"www.example.net." (note the terminating dot). But, because every
name eventually shares the common root, names are often written
relative to the root (such as "www.example.net") and are still
called "fully qualified". This term first appeared in [RFC819].

In practical terms, if one adds the trailing dot, the stub resolver in
libc (asr) will not try the search list if the original name does not
resolve.
www.openbsd.org. is really only www.openbsd.org and not maybe
www.openbsd.org.home. or www.openbsd.org.lan. or some such.
--
I'm not entirely sure you are real.
Loading...