| OLD | NEW |
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package webpagereplay | 5 package webpagereplay |
| 6 | 6 |
| 7 import ( | 7 import ( |
| 8 "crypto/rand" | 8 "crypto/rand" |
| 9 "crypto/tls" | 9 "crypto/tls" |
| 10 "crypto/x509" | 10 "crypto/x509" |
| 11 "fmt" | 11 "fmt" |
| 12 "io" | 12 "io" |
| 13 "time" | 13 "time" |
| 14 ) | 14 ) |
| 15 | 15 |
| 16 // Returns a TLS configuration that serves a recorded server leaf cert signed by | 16 // Returns a TLS configuration that serves a recorded server leaf cert signed by |
| 17 // root CA. | 17 // root CA. |
| 18 func ReplayTLSConfig(root tls.Certificate, a *Archive) (*tls.Config, error) { | 18 func ReplayTLSConfig(root tls.Certificate, a *Archive) (*tls.Config, error) { |
| 19 root_cert, err := getRootCert(root) | 19 root_cert, err := getRootCert(root) |
| 20 if err != nil { | 20 if err != nil { |
| 21 return nil, fmt.Errorf("bad local cert: %v", err) | 21 return nil, fmt.Errorf("bad local cert: %v", err) |
| 22 } | 22 } |
| 23 tp := &tlsProxy{&root, root_cert, a, nil} | 23 tp := &tlsProxy{&root, root_cert, a, nil} |
| 24 return &tls.Config{ | 24 return &tls.Config{ |
| 25 » » Certificates: []tls.Certificate{*tp.root}, | 25 » » GetConfigForClient: tp.getReplayConfigForClient, |
| 26 » » GetCertificate: tp.getReplayCertificate, | |
| 27 }, nil | 26 }, nil |
| 28 } | 27 } |
| 29 | 28 |
| 30 // Returns a TLS configuration that serves a server leaf cert fetched over the | 29 // Returns a TLS configuration that serves a server leaf cert fetched over the |
| 31 // network on demand. | 30 // network on demand. |
| 32 func RecordTLSConfig(root tls.Certificate, w *WritableArchive) (*tls.Config, err
or) { | 31 func RecordTLSConfig(root tls.Certificate, w *WritableArchive) (*tls.Config, err
or) { |
| 33 root_cert, err := getRootCert(root) | 32 root_cert, err := getRootCert(root) |
| 34 if err != nil { | 33 if err != nil { |
| 35 return nil, fmt.Errorf("bad local cert: %v", err) | 34 return nil, fmt.Errorf("bad local cert: %v", err) |
| 36 } | 35 } |
| 37 tp := &tlsProxy{&root, root_cert, nil, w} | 36 tp := &tlsProxy{&root, root_cert, nil, w} |
| 38 return &tls.Config{ | 37 return &tls.Config{ |
| 39 » » Certificates: []tls.Certificate{*tp.root}, | 38 » » GetConfigForClient: tp.getRecordConfigForClient, |
| 40 » » GetCertificate: tp.getCertificate, | |
| 41 }, nil | 39 }, nil |
| 42 } | 40 } |
| 43 | 41 |
| 44 func getRootCert(root tls.Certificate) (*x509.Certificate, error) { | 42 func getRootCert(root tls.Certificate) (*x509.Certificate, error) { |
| 45 root_cert, err := x509.ParseCertificate(root.Certificate[0]) | 43 root_cert, err := x509.ParseCertificate(root.Certificate[0]) |
| 46 if err != nil { | 44 if err != nil { |
| 47 return nil, err | 45 return nil, err |
| 48 } | 46 } |
| 49 root_cert.IsCA = true | 47 root_cert.IsCA = true |
| 50 root_cert.BasicConstraintsValid = true | 48 root_cert.BasicConstraintsValid = true |
| 51 return root_cert, nil | 49 return root_cert, nil |
| 52 } | 50 } |
| 53 | 51 |
| 54 type tlsProxy struct { | 52 type tlsProxy struct { |
| 55 root *tls.Certificate | 53 root *tls.Certificate |
| 56 root_cert *x509.Certificate | 54 root_cert *x509.Certificate |
| 57 archive *Archive | 55 archive *Archive |
| 58 writable_archive *WritableArchive | 56 writable_archive *WritableArchive |
| 59 } | 57 } |
| 60 | 58 |
| 61 // TODO: For now, this just returns a self-signed cert using the given ServerNam
e. | 59 // TODO: For now, this just returns a self-signed cert using the given ServerNam
e. |
| 62 // In the future, for better HTTP/2 support, we may want to record host equivale
nce | 60 // In the future, for better HTTP/2 support, we may want to record host equivale
nce |
| 63 // classes in the archive, where an equivalence class contains all hosts that ca
n be | 61 // classes in the archive, where an equivalence class contains all hosts that ca
n be |
| 64 // served by the same IP. We can then run a DNS proxy that maps all hostnames in
the | 62 // served by the same IP. We can then run a DNS proxy that maps all hostnames in
the |
| 65 // same equivalence class to the same local port, which models the possibility t
hat | 63 // same equivalence class to the same local port, which models the possibility t
hat |
| 66 // every equivalence class of hostnames can be served over the same HTTP/2 conne
ction. | 64 // every equivalence class of hostnames can be served over the same HTTP/2 conne
ction. |
| 67 // | 65 func (tp *tlsProxy) getReplayConfigForClient(clientHello *tls.ClientHelloInfo) (
*tls.Config, error) { |
| 68 // getCertificate implements a callback for tls.Config.GetCertificate. | |
| 69 func (tp *tlsProxy) getReplayCertificate(clientHello *tls.ClientHelloInfo) (*tls
.Certificate, error) { | |
| 70 h := clientHello.ServerName | 66 h := clientHello.ServerName |
| 71 if h == "" { | 67 if h == "" { |
| 72 » » return tp.root, nil | 68 » » return &tls.Config{ |
| 69 » » » Certificates: []tls.Certificate{*tp.root}, |
| 70 » » }, nil |
| 73 } | 71 } |
| 74 | 72 |
| 75 » der_bytes, err := tp.archive.FindHostCert(h) | 73 » der_bytes, negotiatedProtocol, err := tp.archive.FindHostTlsConfig(h) |
| 76 if err != nil || der_bytes == nil { | 74 if err != nil || der_bytes == nil { |
| 77 return nil, fmt.Errorf("No archived cert for %s", h) | 75 return nil, fmt.Errorf("No archived cert for %s", h) |
| 78 } | 76 } |
| 79 » return &tls.Certificate{ | 77 » return &tls.Config{ |
| 80 » » Certificate: [][]byte{der_bytes}, | 78 » » Certificates: []tls.Certificate{ |
| 81 » » PrivateKey: tp.root.PrivateKey, | 79 » » » tls.Certificate{ |
| 80 » » » » Certificate: [][]byte{der_bytes}, |
| 81 » » » » PrivateKey: tp.root.PrivateKey, |
| 82 » » » }}, |
| 83 » » NextProtos: []string{negotiatedProtocol}, |
| 82 }, nil | 84 }, nil |
| 83 } | 85 } |
| 84 | 86 |
| 85 func (tp *tlsProxy) getCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certi
ficate, error) { | 87 func (tp *tlsProxy) getRecordConfigForClient(clientHello *tls.ClientHelloInfo) (
*tls.Config, error) { |
| 86 h := clientHello.ServerName | 88 h := clientHello.ServerName |
| 87 if h == "" { | 89 if h == "" { |
| 88 » » return tp.root, nil | 90 » » return &tls.Config{ |
| 91 » » » Certificates: []tls.Certificate{*tp.root}, |
| 92 » » }, nil |
| 89 } | 93 } |
| 90 | 94 » der_bytes, negotiatedProtocol, err := tp.writable_archive.Archive.FindHo
stTlsConfig(h) |
| 91 » der_bytes, err := tp.writable_archive.FindHostCert(h) | |
| 92 if err == nil && der_bytes != nil { | 95 if err == nil && der_bytes != nil { |
| 93 » » return &tls.Certificate{ | 96 » » return &tls.Config{ |
| 94 » » » Certificate: [][]byte{der_bytes}, | 97 » » » Certificates: []tls.Certificate{ |
| 95 » » » PrivateKey: tp.root.PrivateKey, | 98 » » » » tls.Certificate{ |
| 99 » » » » » Certificate: [][]byte{der_bytes}, |
| 100 » » » » » PrivateKey: tp.root.PrivateKey, |
| 101 » » » » }}, |
| 102 » » » NextProtos: []string{negotiatedProtocol}, |
| 96 }, nil | 103 }, nil |
| 97 } | 104 } |
| 98 | 105 |
| 99 » conn, err := tls.Dial("tcp", fmt.Sprintf("%s:443", h), nil) | 106 » conn, err := tls.Dial("tcp", fmt.Sprintf("%s:443", h), &tls.Config{ |
| 107 » » NextProtos: []string{"h2", "http/1.1"}, |
| 108 » }) |
| 100 if err != nil { | 109 if err != nil { |
| 101 return nil, fmt.Errorf("Couldn't reach host %s: %v", h, err) | 110 return nil, fmt.Errorf("Couldn't reach host %s: %v", h, err) |
| 102 } | 111 } |
| 103 defer conn.Close() | 112 defer conn.Close() |
| 104 conn.Handshake() | 113 conn.Handshake() |
| 105 template := conn.ConnectionState().PeerCertificates[0] | 114 template := conn.ConnectionState().PeerCertificates[0] |
| 106 | 115 |
| 107 template.Subject.CommonName = h | 116 template.Subject.CommonName = h |
| 108 template.NotBefore = time.Now() | 117 template.NotBefore = time.Now() |
| 109 template.NotAfter = template.NotBefore.Add(87658 * time.Hour) | 118 template.NotAfter = template.NotBefore.Add(87658 * time.Hour) |
| 110 template.PublicKey = tp.root_cert.PublicKey | 119 template.PublicKey = tp.root_cert.PublicKey |
| 111 var buf [20]byte | 120 var buf [20]byte |
| 112 if _, err := io.ReadFull(rand.Reader, buf[:]); err != nil { | 121 if _, err := io.ReadFull(rand.Reader, buf[:]); err != nil { |
| 113 return nil, err | 122 return nil, err |
| 114 } | 123 } |
| 115 template.SerialNumber.SetBytes(buf[:]) | 124 template.SerialNumber.SetBytes(buf[:]) |
| 116 template.Issuer = tp.root_cert.Subject | 125 template.Issuer = tp.root_cert.Subject |
| 117 template.KeyUsage = x509.KeyUsageCertSign | x509.KeyUsageKeyEncipherment
| x509.KeyUsageDigitalSignature | x509.KeyUsageCRLSign | 126 template.KeyUsage = x509.KeyUsageCertSign | x509.KeyUsageKeyEncipherment
| x509.KeyUsageDigitalSignature | x509.KeyUsageCRLSign |
| 118 template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x5
09.ExtKeyUsageServerAuth} | 127 template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x5
09.ExtKeyUsageServerAuth} |
| 119 | 128 |
| 120 der_bytes, err = x509.CreateCertificate(rand.Reader, template, tp.root_c
ert, template.PublicKey, tp.root.PrivateKey) | 129 der_bytes, err = x509.CreateCertificate(rand.Reader, template, tp.root_c
ert, template.PublicKey, tp.root.PrivateKey) |
| 121 if err != nil { | 130 if err != nil { |
| 122 return nil, fmt.Errorf("create cert failed: %v", err) | 131 return nil, fmt.Errorf("create cert failed: %v", err) |
| 123 } | 132 } |
| 124 » tp.writable_archive.RecordCert(h, der_bytes) | 133 |
| 125 » return &tls.Certificate{ | 134 » negotiatedProtocol = conn.ConnectionState().NegotiatedProtocol |
| 126 » » Certificate: [][]byte{der_bytes}, | 135 » tp.writable_archive.RecordTlsConfig(h, der_bytes, negotiatedProtocol) |
| 127 » » PrivateKey: tp.root.PrivateKey, | 136 |
| 137 » return &tls.Config{ |
| 138 » » Certificates: []tls.Certificate{ |
| 139 » » » tls.Certificate{ |
| 140 » » » » Certificate: [][]byte{der_bytes}, |
| 141 » » » » PrivateKey: tp.root.PrivateKey}}, |
| 142 » » NextProtos: []string{negotiatedProtocol}, |
| 128 }, nil | 143 }, nil |
| 129 } | 144 } |
| OLD | NEW |