Index | Thread | Search

From:
Theo Buehler <tb@theobuehler.org>
Subject:
Re: nc: Add ALPN TLS option
To:
David Leadbeater <dgl@dgl.cx>
Cc:
tech@openbsd.org, jsing@openbsd.org, beck@openbsd.org
Date:
Tue, 24 Jun 2025 14:57:29 +0200

Download raw body.

Thread
On Wed, May 28, 2025 at 06:26:34AM +0200, Theo Buehler wrote:
> On Wed, May 28, 2025 at 01:34:34PM +1000, David Leadbeater wrote:
> > The only way to test alpn in base is with openssl s_client, which has
> > some quirks. Here's a diff adding -T alpn=value to nc.
> 
> Thanks. This makes sense to me. I think it would be helpful to print the
> selected ALPN in report_tls(): if tls_alpn was set via -T, display what
> tls_conn_alpn_selected() returns (if it is non-NULL).
> 
> I believe there's incorrect behavior in libtls (present since ALPN
> support was added): if there's no protocol overlap, per RFC 7301, 3.2,
> a server should abort the handshake with a no_application_protocol alert.
> See tlsext_alpn_server_process() for the corresponding logic in libssl:

That bug has been fixed a while back.

Regarding report_tls() I simply meant something like this:

$ nc -Talpn="http/1.0,http/1.1" -cvz archive.org 443
Connection to archive.org (207.241.224.2) 443 port [tcp/https] succeeded!
TLS handshake negotiated TLSv1.3/TLS_AES_256_GCM_SHA384 with host archive.org
Peer name: archive.org
Subject: /CN=*.archive.org
Issuer: /C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./OU=http://certs.godaddy.com/repository//CN=Go Daddy Secure Certificate Authority - G2
Valid From: Mon Dec 23 15:18:32 2024
Valid Until: Sat Jan 24 15:18:32 2026
Cert Hash: SHA256:ec16a4ca76fc33bc14b714b16aa2255aa12939c567f680c25268722473bbc50e
OCSP URL: http://ocsp.godaddy.com/
OCSP Stapling: good
  response_status=0 cert_status=0 crl_reason=0
  this update: Mon Jun 23 21:39:51 2025
  next update: Fri Jun 27 21:38:51 2025
  revocation:
Selected application layer protocol: http/1.1

I'm going to commit this in a few days unless I hear objections.

Index: nc.1
===================================================================
RCS file: /cvs/src/usr.bin/nc/nc.1,v
diff -u -p -r1.100 nc.1
--- nc.1	28 May 2025 03:57:13 -0000	1.100
+++ nc.1	24 Jun 2025 12:38:34 -0000
@@ -265,6 +265,10 @@ for further details);
 which allows the supported TLS protocols to be specified (see
 .Xr tls_config_parse_protocols 3
 for further details).
+.Cm alpn ,
+which allows the TLS ALPN to be specified (see
+.Xr tls_config_set_alpn 3
+for further details).
 Specifying TLS options requires
 .Fl c .
 .Pp
Index: netcat.c
===================================================================
RCS file: /cvs/src/usr.bin/nc/netcat.c,v
diff -u -p -r1.232 netcat.c
--- netcat.c	21 May 2025 08:46:42 -0000	1.232
+++ netcat.c	24 Jun 2025 12:52:41 -0000
@@ -108,6 +108,7 @@ char	*tls_expectname;			/* required name
 char	*tls_expecthash;			/* required hash of peer cert */
 char	*tls_ciphers;				/* TLS ciphers */
 char	*tls_protocols;				/* TLS protocols */
+char	*tls_alpn;				/* TLS ALPN */
 FILE	*Zflag;					/* file to save peer cert */
 
 int recvcount, recvlimit;
@@ -534,6 +535,8 @@ main(int argc, char *argv[])
 			errx(1, "%s", tls_config_error(tls_cfg));
 		if (tls_config_set_ciphers(tls_cfg, tls_ciphers) == -1)
 			errx(1, "%s", tls_config_error(tls_cfg));
+		if (tls_alpn != NULL && tls_config_set_alpn(tls_cfg, tls_alpn) == -1)
+			errx(1, "%s", tls_config_error(tls_cfg));
 		if (!lflag && (TLSopt & TLS_CCERT))
 			errx(1, "clientcert is only valid with -l");
 		if (TLSopt & TLS_NONAME)
@@ -1677,6 +1680,7 @@ process_tls_opt(char *s, int *flags)
 		{ "noverify",		TLS_NOVERIFY,		NULL },
 		{ "noname",		TLS_NONAME,		NULL },
 		{ "protocols",		-1,			&tls_protocols },
+		{ "alpn",		-1,			&tls_alpn },
 		{ NULL,			-1,			NULL },
 	};
 
@@ -1722,7 +1726,7 @@ void
 report_tls(struct tls *tls_ctx, char *host)
 {
 	time_t t;
-	const char *ocsp_url;
+	const char *alpn_proto, *ocsp_url;
 
 	fprintf(stderr, "TLS handshake negotiated %s/%s with host %s\n",
 	    tls_conn_version(tls_ctx), tls_conn_cipher(tls_ctx), host);
@@ -1774,6 +1778,9 @@ report_tls(struct tls *tls_ctx, char *ho
 		    tls_peer_ocsp_result(tls_ctx));
 		break;
 	}
+	if ((alpn_proto = tls_conn_alpn_selected(tls_ctx)) != NULL)
+		fprintf(stderr, "Selected application layer protocol: %s\n",
+		    alpn_proto);
 }
 
 void