From: Igor Böhm 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);