]> Sergey Matveev's repositories - tofuproxy.git/blob - tls.go
Log certificate's signature algorithm
[tofuproxy.git] / tls.go
1 /*
2 tofuproxy -- HTTP proxy with TLS certificates management
3 Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, version 3 of the License.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 package tofuproxy
19
20 import (
21         "context"
22         "crypto"
23         "crypto/tls"
24         "crypto/x509"
25         "fmt"
26         "log"
27         "net"
28         "net/http"
29         "strings"
30         "time"
31
32         "go.cypherpunks.ru/ucspi"
33         "go.stargrave.org/tofuproxy/fifos"
34 )
35
36 var (
37         TLSNextProtoS = make(map[string]func(*http.Server, *tls.Conn, http.Handler))
38         CACert        *x509.Certificate
39         CAPrv         crypto.PrivateKey
40         sessionCache  = tls.NewLRUClientSessionCache(1024)
41 )
42
43 type Handler struct{}
44
45 func (h *Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
46         if req.Method != http.MethodConnect {
47                 roundTrip(w, req)
48                 return
49         }
50         hj, ok := w.(http.Hijacker)
51         if !ok {
52                 log.Fatalln("no hijacking")
53         }
54         conn, _, err := hj.Hijack()
55         if err != nil {
56                 log.Fatalln(err)
57         }
58         defer conn.Close()
59         conn.Write([]byte(fmt.Sprintf(
60                 "%s %d %s\r\n\r\n",
61                 req.Proto,
62                 http.StatusOK, http.StatusText(http.StatusOK),
63         )))
64         host := strings.Split(req.Host, ":")[0]
65         hostCertsM.Lock()
66         keypair, ok := hostCerts[host]
67         if !ok || !keypair.cert.NotAfter.After(time.Now().Add(time.Hour)) {
68                 keypair = newKeypair(host, CACert, CAPrv)
69                 hostCerts[host] = keypair
70         }
71         hostCertsM.Unlock()
72         tlsConn := tls.Server(conn, &tls.Config{
73                 Certificates: []tls.Certificate{{
74                         Certificate: [][]byte{keypair.cert.Raw},
75                         PrivateKey:  keypair.prv,
76                 }},
77         })
78         if err = tlsConn.Handshake(); err != nil {
79                 log.Printf("TLS error %s: %+v\n", host, err)
80                 return
81         }
82         srv := http.Server{
83                 Handler:      &HTTPSHandler{host: req.Host},
84                 TLSNextProto: TLSNextProtoS,
85         }
86         err = srv.Serve(&SingleListener{conn: tlsConn})
87         if err != nil {
88                 if _, ok := err.(AlreadyAccepted); !ok {
89                         log.Printf("TLS serve error %s: %+v\n", host, err)
90                         return
91                 }
92         }
93 }
94
95 type HTTPSHandler struct {
96         host string
97 }
98
99 func (h *HTTPSHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
100         req.URL.Scheme = "https"
101         req.URL.Host = h.host
102         roundTrip(w, req)
103 }
104
105 func dialTLS(ctx context.Context, network, addr string) (net.Conn, error) {
106         host := strings.Split(addr, ":")[0]
107         ccg := ClientCertificateGetter{host: host}
108         cfg := tls.Config{
109                 VerifyPeerCertificate: func(
110                         rawCerts [][]byte,
111                         verifiedChains [][]*x509.Certificate,
112                 ) error {
113                         return verifyCert(host, nil, rawCerts, verifiedChains)
114                 },
115                 ClientSessionCache:   sessionCache,
116                 NextProtos:           []string{"h2", "http/1.1"},
117                 GetClientCertificate: ccg.get,
118         }
119         conn, dialErr := tls.Dial(network, addr, &cfg)
120         if dialErr != nil {
121                 if _, ok := dialErr.(ErrRejected); ok {
122                         return nil, dialErr
123                 }
124                 cfg.InsecureSkipVerify = true
125                 cfg.VerifyPeerCertificate = func(
126                         rawCerts [][]byte,
127                         verifiedChains [][]*x509.Certificate,
128                 ) error {
129                         return verifyCert(host, dialErr, rawCerts, verifiedChains)
130                 }
131                 var err error
132                 conn, err = tls.Dial(network, addr, &cfg)
133                 if err != nil {
134                         fifos.LogErr <- fmt.Sprintf("%s\t%s", addr, dialErr.Error())
135                         return nil, err
136                 }
137         }
138         connState := conn.ConnectionState()
139         if !connState.DidResume {
140                 fifos.LogTLS <- fmt.Sprintf(
141                         "%s\t%s %s %s\t%s\t%s",
142                         addr,
143                         ucspi.TLSVersion(connState.Version),
144                         tls.CipherSuiteName(connState.CipherSuite),
145                         connState.PeerCertificates[0].SignatureAlgorithm,
146                         spkiHash(connState.PeerCertificates[0]),
147                         connState.NegotiatedProtocol,
148                 )
149         }
150         return conn, nil
151 }