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" | 8 "crypto" |
9 "crypto/rand" | 9 "crypto/rand" |
10 "crypto/tls" | 10 "crypto/tls" |
11 "crypto/x509" | 11 "crypto/x509" |
12 "fmt" | 12 "fmt" |
13 "io" | 13 "io" |
| 14 "net" |
14 "time" | 15 "time" |
15 ) | 16 ) |
16 | 17 |
17 // Returns a TLS configuration that serves a recorded server leaf cert signed by | 18 // Returns a TLS configuration that serves a recorded server leaf cert signed by |
18 // root CA. | 19 // root CA. |
19 func ReplayTLSConfig(root tls.Certificate, a *Archive) (*tls.Config, error) { | 20 func ReplayTLSConfig(root tls.Certificate, a *Archive) (*tls.Config, error) { |
20 root_cert, err := getRootCert(root) | 21 root_cert, err := getRootCert(root) |
21 if err != nil { | 22 if err != nil { |
22 return nil, fmt.Errorf("bad local cert: %v", err) | 23 return nil, fmt.Errorf("bad local cert: %v", err) |
23 } | 24 } |
24 » tp := &tlsProxy{&root, root_cert, a, nil} | 25 » tp := &tlsProxy{&root, root_cert, a, nil, make(map[string][]byte)} |
25 return &tls.Config{ | 26 return &tls.Config{ |
26 GetConfigForClient: tp.getReplayConfigForClient, | 27 GetConfigForClient: tp.getReplayConfigForClient, |
27 }, nil | 28 }, nil |
28 } | 29 } |
29 | 30 |
30 // Returns a TLS configuration that serves a server leaf cert fetched over the | 31 // Returns a TLS configuration that serves a server leaf cert fetched over the |
31 // network on demand. | 32 // network on demand. |
32 func RecordTLSConfig(root tls.Certificate, w *WritableArchive) (*tls.Config, err
or) { | 33 func RecordTLSConfig(root tls.Certificate, w *WritableArchive) (*tls.Config, err
or) { |
33 root_cert, err := getRootCert(root) | 34 root_cert, err := getRootCert(root) |
34 if err != nil { | 35 if err != nil { |
35 return nil, fmt.Errorf("bad local cert: %v", err) | 36 return nil, fmt.Errorf("bad local cert: %v", err) |
36 } | 37 } |
37 » tp := &tlsProxy{&root, root_cert, nil, w} | 38 » tp := &tlsProxy{&root, root_cert, nil, w, nil} |
38 return &tls.Config{ | 39 return &tls.Config{ |
39 GetConfigForClient: tp.getRecordConfigForClient, | 40 GetConfigForClient: tp.getRecordConfigForClient, |
40 }, nil | 41 }, nil |
41 } | 42 } |
42 | 43 |
43 func getRootCert(root tls.Certificate) (*x509.Certificate, error) { | 44 func getRootCert(root tls.Certificate) (*x509.Certificate, error) { |
44 root_cert, err := x509.ParseCertificate(root.Certificate[0]) | 45 root_cert, err := x509.ParseCertificate(root.Certificate[0]) |
45 if err != nil { | 46 if err != nil { |
46 return nil, err | 47 return nil, err |
47 } | 48 } |
48 root_cert.IsCA = true | 49 root_cert.IsCA = true |
49 root_cert.BasicConstraintsValid = true | 50 root_cert.BasicConstraintsValid = true |
50 return root_cert, nil | 51 return root_cert, nil |
51 } | 52 } |
52 | 53 |
| 54 // Mints a dummy server cert when the real one is not recorded. |
| 55 func MintDummyCertificate(serverName string, rootCert *x509.Certificate, rootKey
crypto.PrivateKey) ([]byte, string, error) { |
| 56 template := rootCert |
| 57 if ip := net.ParseIP(serverName); ip != nil { |
| 58 template.IPAddresses = []net.IP{ip} |
| 59 } else { |
| 60 template.DNSNames = []string{serverName} |
| 61 } |
| 62 var buf [20]byte |
| 63 if _, err := io.ReadFull(rand.Reader, buf[:]); err != nil { |
| 64 return nil, "", fmt.Errorf("create cert failed: %v", err) |
| 65 } |
| 66 template.SerialNumber.SetBytes(buf[:]) |
| 67 template.Issuer = template.Subject |
| 68 derBytes, err := x509.CreateCertificate(rand.Reader, template, template,
template.PublicKey, rootKey) |
| 69 if err != nil { |
| 70 return nil, "", fmt.Errorf("create cert failed: %v", err) |
| 71 } |
| 72 return derBytes, "", err |
| 73 } |
| 74 |
53 // Returns DER encoded server cert. | 75 // Returns DER encoded server cert. |
54 func MintServerCert(serverName string, rootCert *x509.Certificate, rootKey crypt
o.PrivateKey) ([]byte, string, error) { | 76 func MintServerCert(serverName string, rootCert *x509.Certificate, rootKey crypt
o.PrivateKey) ([]byte, string, error) { |
55 conn, err := tls.Dial("tcp", fmt.Sprintf("%s:443", serverName), &tls.Con
fig{ | 77 conn, err := tls.Dial("tcp", fmt.Sprintf("%s:443", serverName), &tls.Con
fig{ |
56 NextProtos: []string{"h2", "http/1.1"}, | 78 NextProtos: []string{"h2", "http/1.1"}, |
57 }) | 79 }) |
58 if err != nil { | 80 if err != nil { |
59 return nil, "", fmt.Errorf("Couldn't reach host %s: %v", serverN
ame, err) | 81 return nil, "", fmt.Errorf("Couldn't reach host %s: %v", serverN
ame, err) |
60 } | 82 } |
61 defer conn.Close() | 83 defer conn.Close() |
62 conn.Handshake() | 84 conn.Handshake() |
(...skipping 15 matching lines...) Expand all Loading... |
78 negotiatedProtocol := conn.ConnectionState().NegotiatedProtocol | 100 negotiatedProtocol := conn.ConnectionState().NegotiatedProtocol |
79 derBytes, err := x509.CreateCertificate(rand.Reader, template, rootCert,
template.PublicKey, rootKey) | 101 derBytes, err := x509.CreateCertificate(rand.Reader, template, rootCert,
template.PublicKey, rootKey) |
80 return derBytes, negotiatedProtocol, err | 102 return derBytes, negotiatedProtocol, err |
81 } | 103 } |
82 | 104 |
83 type tlsProxy struct { | 105 type tlsProxy struct { |
84 root *tls.Certificate | 106 root *tls.Certificate |
85 root_cert *x509.Certificate | 107 root_cert *x509.Certificate |
86 archive *Archive | 108 archive *Archive |
87 writable_archive *WritableArchive | 109 writable_archive *WritableArchive |
| 110 dummy_certs_map map[string][]byte |
88 } | 111 } |
89 | 112 |
90 // TODO: For now, this just returns a self-signed cert using the given ServerNam
e. | 113 // TODO: For now, this just returns a self-signed cert using the given ServerNam
e. |
91 // In the future, for better HTTP/2 support, we may want to record host equivale
nce | 114 // In the future, for better HTTP/2 support, we may want to record host equivale
nce |
92 // classes in the archive, where an equivalence class contains all hosts that ca
n be | 115 // classes in the archive, where an equivalence class contains all hosts that ca
n be |
93 // served by the same IP. We can then run a DNS proxy that maps all hostnames in
the | 116 // served by the same IP. We can then run a DNS proxy that maps all hostnames in
the |
94 // same equivalence class to the same local port, which models the possibility t
hat | 117 // same equivalence class to the same local port, which models the possibility t
hat |
95 // every equivalence class of hostnames can be served over the same HTTP/2 conne
ction. | 118 // every equivalence class of hostnames can be served over the same HTTP/2 conne
ction. |
96 func (tp *tlsProxy) getReplayConfigForClient(clientHello *tls.ClientHelloInfo) (
*tls.Config, error) { | 119 func (tp *tlsProxy) getReplayConfigForClient(clientHello *tls.ClientHelloInfo) (
*tls.Config, error) { |
97 h := clientHello.ServerName | 120 h := clientHello.ServerName |
98 if h == "" { | 121 if h == "" { |
99 return &tls.Config{ | 122 return &tls.Config{ |
100 Certificates: []tls.Certificate{*tp.root}, | 123 Certificates: []tls.Certificate{*tp.root}, |
101 }, nil | 124 }, nil |
102 } | 125 } |
103 | 126 |
104 derBytes, negotiatedProtocol, err := tp.archive.FindHostTlsConfig(h) | 127 derBytes, negotiatedProtocol, err := tp.archive.FindHostTlsConfig(h) |
105 if err != nil || derBytes == nil { | 128 if err != nil || derBytes == nil { |
106 » » return nil, fmt.Errorf("No archived cert for %s", h) | 129 » » if _, ok := tp.dummy_certs_map[h]; !ok { |
| 130 » » » derBytes, negotiatedProtocol, err = MintDummyCertificate
(h, tp.root_cert, tp.root.PrivateKey) |
| 131 » » » if err != nil { |
| 132 » » » » return nil, err |
| 133 » » » } |
| 134 » » » tp.dummy_certs_map[h] = derBytes |
| 135 » » } |
| 136 » » derBytes = tp.dummy_certs_map[h] |
107 } | 137 } |
108 return &tls.Config{ | 138 return &tls.Config{ |
109 Certificates: []tls.Certificate{ | 139 Certificates: []tls.Certificate{ |
110 tls.Certificate{ | 140 tls.Certificate{ |
111 Certificate: [][]byte{derBytes}, | 141 Certificate: [][]byte{derBytes}, |
112 PrivateKey: tp.root.PrivateKey, | 142 PrivateKey: tp.root.PrivateKey, |
113 }}, | 143 }}, |
114 » » NextProtos: []string{negotiatedProtocol}, | 144 » » NextProtos: buildNextProtos(negotiatedProtocol), |
115 }, nil | 145 }, nil |
116 } | 146 } |
117 | 147 |
118 func buildNextProtos(negotiatedProtocol string) []string { | 148 func buildNextProtos(negotiatedProtocol string) []string { |
119 if negotiatedProtocol == "h2" { | 149 if negotiatedProtocol == "h2" { |
120 return []string{"h2", "http/1.1"} | 150 return []string{"h2", "http/1.1"} |
121 } | 151 } |
122 return []string{"http/1.1"} | 152 return []string{"http/1.1"} |
123 } | 153 } |
124 | 154 |
(...skipping 24 matching lines...) Expand all Loading... |
149 tp.writable_archive.RecordTlsConfig(h, derBytes, negotiatedProtocol) | 179 tp.writable_archive.RecordTlsConfig(h, derBytes, negotiatedProtocol) |
150 | 180 |
151 return &tls.Config{ | 181 return &tls.Config{ |
152 Certificates: []tls.Certificate{ | 182 Certificates: []tls.Certificate{ |
153 tls.Certificate{ | 183 tls.Certificate{ |
154 Certificate: [][]byte{derBytes}, | 184 Certificate: [][]byte{derBytes}, |
155 PrivateKey: tp.root.PrivateKey}}, | 185 PrivateKey: tp.root.PrivateKey}}, |
156 NextProtos: buildNextProtos(negotiatedProtocol), | 186 NextProtos: buildNextProtos(negotiatedProtocol), |
157 }, nil | 187 }, nil |
158 } | 188 } |
OLD | NEW |