From: Theo Buehler Subject: Re: nc: Add ALPN TLS option To: David Leadbeater Cc: tech@openbsd.org, jsing@openbsd.org, beck@openbsd.org Date: Tue, 24 Jun 2025 14:57:29 +0200 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