2 tofuproxy -- flexible HTTP/HTTPS proxy, TLS terminator, X.509 TOFU
3 manager, WARC/geminispace browser
4 Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, version 3 of the License.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
34 "go.cypherpunks.ru/ucspi"
35 "go.stargrave.org/tofuproxy/caches"
36 "go.stargrave.org/tofuproxy/fifos"
41 type ClientCertificateGetter struct {
46 func (g *ClientCertificateGetter) get(
47 cri *tls.CertificateRequestInfo,
48 ) (*tls.Certificate, error) {
49 caches.TLSAuthCacheM.RLock()
50 tlsCert := caches.TLSAuthCache[g.host]
51 caches.TLSAuthCacheM.RUnlock()
55 sigSchemes := make([]string, 0, len(cri.SignatureSchemes))
56 for _, ss := range cri.SignatureSchemes {
57 sigSchemes = append(sigSchemes, ss.String())
60 b.WriteString(fmt.Sprintf(`
62 wm title . "TLS client authentication: %s"
74 button .login -text "Use" -command login
77 bind . <KeyPress> {switch -exact %%K {
78 q {exit 0} ; # reject once
79 n {puts "0:NONE" ; exit}
83 label .lTLSVersion -text "TLS version: %s"
87 foreach sigScheme {%s} {
88 label .lSignatureScheme$sigSchemeRow -text "Signature scheme: $sigScheme"
89 grid .lSignatureScheme$sigSchemeRow
95 ucspi.TLSVersion(cri.Version),
96 strings.Join(sigSchemes, " "),
99 ents, err := os.ReadDir(CCerts)
103 certs := make([]*x509.Certificate, 0, len(ents))
104 tlsCerts := make([]*tls.Certificate, 0, len(ents))
105 b.WriteString(".lb insert end \"0: NONE\"\n")
106 certs = append(certs, nil)
107 tlsCerts = append(tlsCerts, nil)
108 for i, ent := range ents {
109 p := filepath.Join(CCerts, ent.Name())
110 _, cert, err := ucspi.CertificateFromFile(p)
114 prv, err := ucspi.PrivateKeyFromFile(p)
118 certs = append(certs, cert)
119 tlsCerts = append(tlsCerts, &tls.Certificate{
120 Certificate: [][]byte{cert.Raw},
123 b.WriteString(fmt.Sprintf(".lb insert end \"%d: %s\"\n", i+1, cert.Subject))
125 // ioutil.WriteFile("/tmp/tls-auth-dialog.tcl", b.Bytes(), 0666)
126 cmd := exec.Command(CmdWish)
128 out, err := cmd.Output()
132 lines := strings.Split(string(out), "\n")
134 return nil, errors.New("invalid output from authentication form")
136 t := strings.Split(lines[0], ":")[0]
137 i, err := strconv.Atoi(t)
139 return &tls.Certificate{}, nil
142 dummy := tls.Certificate{}
143 caches.TLSAuthCacheM.Lock()
144 caches.TLSAuthCache[g.host] = &dummy
145 caches.TLSAuthCacheM.Unlock()
148 fifos.LogTLSAuth <- fmt.Sprintf("%s\t%s", g.host, certs[i].Subject)
149 caches.TLSAuthCacheM.Lock()
150 caches.TLSAuthCache[g.host] = tlsCerts[i]
151 caches.TLSAuthCacheM.Unlock()
153 return tlsCerts[i], nil