9front - general discussion about 9front
 help / color / mirror / Atom feed
* [9front] [PATCH] libsec, pushtls, tlssrv: add support for Server Name Indication (SNI) extension
@ 2024-01-25 11:32 igor
  2024-01-25 12:11 ` cinap_lenrek
  0 siblings, 1 reply; 5+ messages in thread
From: igor @ 2024-01-25 11:32 UTC (permalink / raw)
  To: 9front; +Cc: igor

[-- Attachment #1: Type: text/plain, Size: 4923 bytes --]

This patch extends libsec to support the Server Name Indication (SNI)
extension.

Server Name Indication (SNI) is an extension to the Transport Layer
Security (TLS) computer networking protocol by which a client
indicates which hostname it is attempting to connect to at the start
of the handshaking process.  The extension allows a server to present
one of multiple possible certificates on the same IP address and TCP
port number and hence allows multiple secure (HTTPS) websites (or any
other service over TLS) to be served by the same IP address without
requiring all those sites to use the same certificate.

A real world example is outlined here:
https://9lab.org/plan9/tlssrv8-with-server-name-indication-sni-support/

Please let me know what you think and whether you would like additional
input on why this extension is useful after having glanced at the
example above.

One open item that is not addressed by the attached patch is where
and how to document this feature. The best place seems to be the
pushtls(2) man page with additional references from tlssrv(8) and
friends. I plan to address this once the core implementation of SNI
gets past the approval stage.

Cheers,
Igor

---
diff c2a290b8fe17f06370bc748552f2af1d58f50a71 eff99a21434474625fbdd60e68fb1b4cab389238
--- a/sys/src/libsec/port/tlshand.c
+++ b/sys/src/libsec/port/tlshand.c
@@ -98,6 +98,8 @@
 	char *digest;	// name of digest algorithm to use
 	char *enc;	// name of encryption algorithm to use
 
+	char *serverName;	// server name indication; extension
+
 	// for finished messages
 	HandshakeHash	handhash;
 	Finished	finished;
@@ -355,7 +357,7 @@
 };
 
 static TlsConnection *tlsServer2(int ctl, int hand,
-	uchar *cert, int certlen,
+	uchar **cert, int certlen,
 	char *pskid, uchar *psk, int psklen,
 	int (*trace)(char*fmt, ...), PEMChain *chain);
 static TlsConnection *tlsClient2(int ctl, int hand,
@@ -456,7 +458,7 @@
 	data = -1;
 	fprint(ctl, "fd %d 0x%x", fd, ProtocolVersion);
 	tls = tlsServer2(ctl, hand,
-		conn->cert, conn->certlen,
+		&(conn->cert), conn->certlen,
 		conn->pskID, conn->psk, conn->psklen,
 		conn->trace, conn->chain);
 	if(tls != nil){
@@ -659,9 +661,23 @@
 		if(e-p < 4)
 			goto Short;
 		p += 4;
-		if(e-p < (n = get16(p-2)))
+		if(e-p < (n = get16(p-2)))	/* Length */
 			goto Short;
-		switch(get16(p-4)){
+		switch(get16(p-4)){			/* Type */
+		case Extsni:
+			if(n < 4 || get16(p) != (n -= 2))
+				goto Short;
+			if(*(p+2) != 0)			/* Server Name Type: host_name */
+				break;
+			p += 2+1+2;
+			if(e-p < (n = get16(p-2)))
+				goto Short;
+			if(n > 255)				/* DNS name can not exceed 255 bytes RFC1035 */
+				break;
+			c->serverName = emalloc(n+1);
+			memmove(c->serverName, p, n);
+			c->serverName[n] = 0;
+			break;
 		case Extec:
 			if(n < 4 || n % 2 || get16(p) != (n -= 2))
 				goto Short;
@@ -717,7 +733,7 @@
 
 static TlsConnection *
 tlsServer2(int ctl, int hand,
-	uchar *cert, int certlen,
+	uchar **cert, int certlen,
 	char *pskid, uchar *psk, int psklen,
 	int (*trace)(char*fmt, ...), PEMChain *chp)
 {
@@ -765,9 +781,26 @@
 		c->sec->psk = psk;
 		c->sec->psklen = psklen;
 	}
+	if(checkClientExtensions(c, m.u.clientHello.extensions) < 0)
+		goto Err;
 	if(certlen > 0){
+		/* override default certificate using Server Name Indication (SNI) extension */
+		if(c->serverName){
+			char path[512];
+			PEMChain *chain;
+
+			snprint(path, sizeof(path), "/sys/lib/tls/acmed/%s.crt", c->serverName);
+			if(trace)
+				trace("ClientHello extension server name identifier selects %s\n", path);
+			chain = readcertchain(path);
+			if (chain){
+				free(*cert);
+				*cert = chain->pem;
+				certlen = chain->pemlen;
+			}
+		}
 		/* server certificate */
-		c->sec->rsapub = X509toRSApub(cert, certlen, nil, 0);
+		c->sec->rsapub = X509toRSApub(*cert, certlen, nil, 0);
 		if(c->sec->rsapub == nil){
 			tlsError(c, EHandshakeFailure, "invalid X509/rsa certificate");
 			goto Err;
@@ -780,8 +813,6 @@
 	}
 	if(lookupid(m.u.clientHello.ciphers, TLS_EMPTY_RENEGOTIATION_INFO_SCSV) >= 0)
 		c->sec->reneg = 1;
-	if(checkClientExtensions(c, m.u.clientHello.extensions) < 0)
-		goto Err;
 	cipher = okCipher(m.u.clientHello.ciphers, psklen > 0, c->sec->nc != nil);
 	if(cipher < 0 || !setAlgs(c, cipher)) {
 		tlsError(c, EHandshakeFailure, "no matching cipher suite");
@@ -813,7 +844,7 @@
 		numcerts = countchain(chp);
 		m.u.certificate.ncert = 1 + numcerts;
 		m.u.certificate.certs = emalloc(m.u.certificate.ncert * sizeof(Bytes*));
-		m.u.certificate.certs[0] = makebytes(cert, certlen);
+		m.u.certificate.certs[0] = makebytes(*cert, certlen);
 		for (i = 0; i < numcerts && chp; i++, chp = chp->next)
 			m.u.certificate.certs[i+1] = makebytes(chp->pem, chp->pemlen);
 		if(!msgSend(c, &m, AQueue))
@@ -2113,6 +2144,8 @@
 	factotum_rsa_close(c->sec->rpc);
 	rsapubfree(c->sec->rsapub);
 	freebytes(c->cert);
+
+	free(c->serverName);
 
 	memset(c, 0, sizeof(*c));
 	free(c);

[-- Attachment #2: tlssrv.sni.patch --]
[-- Type: text/plain, Size: 4544 bytes --]

From: Igor Böhm <igor@9lab.org>
Date: Thu, 25 Jan 2024 11:20:51 +0000
Subject: [PATCH] libsec, pushtls, tlssrv: add support for Server Name Indication (SNI) extension


Server Name Indication (SNI) is an extension to the Transport Layer
Security (TLS) computer networking protocol by which a client
indicates which hostname it is attempting to connect to at the start
of the handshaking process.  The extension allows a server to present
one of multiple possible certificates on the same IP address and TCP
port number and hence allows multiple secure (HTTPS) websites (or any
other service over TLS) to be served by the same IP address without
requiring all those sites to use the same certificate.

A real world example is outlined here:
https://9lab.org/plan9/tlssrv8-with-server-name-indication-sni-support/
---
diff c2a290b8fe17f06370bc748552f2af1d58f50a71 eff99a21434474625fbdd60e68fb1b4cab389238
--- a/sys/src/libsec/port/tlshand.c
+++ b/sys/src/libsec/port/tlshand.c
@@ -98,6 +98,8 @@
 	char *digest;	// name of digest algorithm to use
 	char *enc;	// name of encryption algorithm to use
 
+	char *serverName;	// server name indication; extension
+
 	// for finished messages
 	HandshakeHash	handhash;
 	Finished	finished;
@@ -355,7 +357,7 @@
 };
 
 static TlsConnection *tlsServer2(int ctl, int hand,
-	uchar *cert, int certlen,
+	uchar **cert, int certlen,
 	char *pskid, uchar *psk, int psklen,
 	int (*trace)(char*fmt, ...), PEMChain *chain);
 static TlsConnection *tlsClient2(int ctl, int hand,
@@ -456,7 +458,7 @@
 	data = -1;
 	fprint(ctl, "fd %d 0x%x", fd, ProtocolVersion);
 	tls = tlsServer2(ctl, hand,
-		conn->cert, conn->certlen,
+		&(conn->cert), conn->certlen,
 		conn->pskID, conn->psk, conn->psklen,
 		conn->trace, conn->chain);
 	if(tls != nil){
@@ -659,9 +661,23 @@
 		if(e-p < 4)
 			goto Short;
 		p += 4;
-		if(e-p < (n = get16(p-2)))
+		if(e-p < (n = get16(p-2)))	/* Length */
 			goto Short;
-		switch(get16(p-4)){
+		switch(get16(p-4)){			/* Type */
+		case Extsni:
+			if(n < 4 || get16(p) != (n -= 2))
+				goto Short;
+			if(*(p+2) != 0)			/* Server Name Type: host_name */
+				break;
+			p += 2+1+2;
+			if(e-p < (n = get16(p-2)))
+				goto Short;
+			if(n > 255)				/* DNS name can not exceed 255 bytes RFC1035 */
+				break;
+			c->serverName = emalloc(n+1);
+			memmove(c->serverName, p, n);
+			c->serverName[n] = 0;
+			break;
 		case Extec:
 			if(n < 4 || n % 2 || get16(p) != (n -= 2))
 				goto Short;
@@ -717,7 +733,7 @@
 
 static TlsConnection *
 tlsServer2(int ctl, int hand,
-	uchar *cert, int certlen,
+	uchar **cert, int certlen,
 	char *pskid, uchar *psk, int psklen,
 	int (*trace)(char*fmt, ...), PEMChain *chp)
 {
@@ -765,9 +781,26 @@
 		c->sec->psk = psk;
 		c->sec->psklen = psklen;
 	}
+	if(checkClientExtensions(c, m.u.clientHello.extensions) < 0)
+		goto Err;
 	if(certlen > 0){
+		/* override default certificate using Server Name Indication (SNI) extension */
+		if(c->serverName){
+			char path[512];
+			PEMChain *chain;
+
+			snprint(path, sizeof(path), "/sys/lib/tls/acmed/%s.crt", c->serverName);
+			if(trace)
+				trace("ClientHello extension server name identifier selects %s\n", path);
+			chain = readcertchain(path);
+			if (chain){
+				free(*cert);
+				*cert = chain->pem;
+				certlen = chain->pemlen;
+			}
+		}
 		/* server certificate */
-		c->sec->rsapub = X509toRSApub(cert, certlen, nil, 0);
+		c->sec->rsapub = X509toRSApub(*cert, certlen, nil, 0);
 		if(c->sec->rsapub == nil){
 			tlsError(c, EHandshakeFailure, "invalid X509/rsa certificate");
 			goto Err;
@@ -780,8 +813,6 @@
 	}
 	if(lookupid(m.u.clientHello.ciphers, TLS_EMPTY_RENEGOTIATION_INFO_SCSV) >= 0)
 		c->sec->reneg = 1;
-	if(checkClientExtensions(c, m.u.clientHello.extensions) < 0)
-		goto Err;
 	cipher = okCipher(m.u.clientHello.ciphers, psklen > 0, c->sec->nc != nil);
 	if(cipher < 0 || !setAlgs(c, cipher)) {
 		tlsError(c, EHandshakeFailure, "no matching cipher suite");
@@ -813,7 +844,7 @@
 		numcerts = countchain(chp);
 		m.u.certificate.ncert = 1 + numcerts;
 		m.u.certificate.certs = emalloc(m.u.certificate.ncert * sizeof(Bytes*));
-		m.u.certificate.certs[0] = makebytes(cert, certlen);
+		m.u.certificate.certs[0] = makebytes(*cert, certlen);
 		for (i = 0; i < numcerts && chp; i++, chp = chp->next)
 			m.u.certificate.certs[i+1] = makebytes(chp->pem, chp->pemlen);
 		if(!msgSend(c, &m, AQueue))
@@ -2113,6 +2144,8 @@
 	factotum_rsa_close(c->sec->rpc);
 	rsapubfree(c->sec->rsapub);
 	freebytes(c->cert);
+
+	free(c->serverName);
 
 	memset(c, 0, sizeof(*c));
 	free(c);

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2024-01-26  7:23 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-01-25 11:32 [9front] [PATCH] libsec, pushtls, tlssrv: add support for Server Name Indication (SNI) extension igor
2024-01-25 12:11 ` cinap_lenrek
2024-01-25 12:51   ` igor
2024-01-25 15:14     ` cinap_lenrek
2024-01-26  7:21       ` igor

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).