1 // tofuproxy -- flexible HTTP/HTTPS proxy, TLS terminator, X.509 TOFU
2 // manager, WARC/geminispace browser
3 // Copyright (C) 2021-2024 Sergey Matveev <stargrave@stargrave.org>
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.
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.
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/>.
32 "go.cypherpunks.ru/ucspi"
33 "go.stargrave.org/tofuproxy/caches"
34 "go.stargrave.org/tofuproxy/fifos"
39 type ClientCertificateGetter struct {
44 func (g *ClientCertificateGetter) get(
45 cri *tls.CertificateRequestInfo,
46 ) (*tls.Certificate, error) {
47 caches.TLSAuthCacheM.RLock()
48 tlsCert := caches.TLSAuthCache[g.host]
49 caches.TLSAuthCacheM.RUnlock()
53 sigSchemes := make([]string, 0, len(cri.SignatureSchemes))
54 for _, ss := range cri.SignatureSchemes {
55 sigSchemes = append(sigSchemes, ss.String())
60 wm title . "TLS client authentication: %s"
72 button .login -text "Use" -command login
75 bind . <KeyPress> {switch -exact %%K {
76 q {exit 0} ; # reject once
77 n {puts "0:NONE" ; exit}
81 label .lTLSVersion -text "TLS version: %s"
85 foreach sigScheme {%s} {
86 label .lSignatureScheme$sigSchemeRow -text "Signature scheme: $sigScheme"
87 grid .lSignatureScheme$sigSchemeRow
93 ucspi.TLSVersion(cri.Version),
94 strings.Join(sigSchemes, " "),
97 ents, err := os.ReadDir(CCerts)
101 certs := make([]*x509.Certificate, 0, len(ents))
102 tlsCerts := make([]*tls.Certificate, 0, len(ents))
103 b.WriteString(".lb insert end \"0: NONE\"\n")
104 certs = append(certs, nil)
105 tlsCerts = append(tlsCerts, nil)
106 for i, ent := range ents {
107 p := filepath.Join(CCerts, ent.Name())
108 _, cert, err := ucspi.CertificateFromFile(p)
112 prv, err := ucspi.PrivateKeyFromFile(p)
116 certs = append(certs, cert)
117 tlsCerts = append(tlsCerts, &tls.Certificate{
118 Certificate: [][]byte{cert.Raw},
121 fmt.Fprintf(&b, ".lb insert end \"%d: %s\"\n", i+1, cert.Subject)
123 // os.WriteFile("/tmp/tls-auth-dialog.tcl", b.Bytes(), 0666)
124 cmd := exec.Command(CmdWish)
126 out, err := cmd.Output()
130 lines := strings.Split(string(out), "\n")
132 return nil, errors.New("invalid output from authentication form")
134 t := strings.Split(lines[0], ":")[0]
135 i, err := strconv.Atoi(t)
137 return &tls.Certificate{}, nil
140 dummy := tls.Certificate{}
141 caches.TLSAuthCacheM.Lock()
142 caches.TLSAuthCache[g.host] = &dummy
143 caches.TLSAuthCacheM.Unlock()
146 fifos.LogTLSAuth <- fmt.Sprintf("%s\t%s", g.host, certs[i].Subject)
147 caches.TLSAuthCacheM.Lock()
148 caches.TLSAuthCache[g.host] = tlsCerts[i]
149 caches.TLSAuthCacheM.Unlock()
151 return tlsCerts[i], nil