--- /dev/null
+/*
+tofuproxy -- HTTP proxy with TLS certificates management
+Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 3 of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package tofuproxy
+
+import (
+ "bytes"
+ "crypto/tls"
+ "crypto/x509"
+ "errors"
+ "fmt"
+ "log"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strconv"
+ "strings"
+
+ "go.cypherpunks.ru/ucspi"
+ "go.stargrave.org/tofuproxy/fifos"
+)
+
+var CCerts string
+
+type ClientCertificateGetter struct {
+ host string
+}
+
+func (g *ClientCertificateGetter) get(
+ cri *tls.CertificateRequestInfo,
+) (*tls.Certificate, error) {
+ var b bytes.Buffer
+ b.WriteString(fmt.Sprintf(`
+wm title . "TLS client authentication: %s"
+
+label .lVersion -text "Version: %s"
+grib .lVersion
+
+set lb [listbox .lb]
+.lb insert end ""
+`,
+ g.host,
+ ucspi.TLSVersion(cri.Version),
+ ))
+
+ ents, err := os.ReadDir(CCerts)
+ if err != nil {
+ log.Fatalln(err)
+ }
+ certs := make([]*x509.Certificate, 0, len(ents))
+ tlsCerts := make([]*tls.Certificate, 0, len(ents))
+ for i, ent := range ents {
+ p := filepath.Join(CCerts, ent.Name())
+ _, cert, err := ucspi.CertificateFromFile(p)
+ if err != nil {
+ log.Fatalln(err)
+ }
+ prv, err := ucspi.PrivateKeyFromFile(p)
+ if err != nil {
+ log.Fatalln(err)
+ }
+ certs = append(certs, cert)
+ tlsCerts = append(tlsCerts, &tls.Certificate{
+ Certificate: [][]byte{cert.Raw},
+ PrivateKey: prv,
+ })
+ b.WriteString(fmt.Sprintf(".lb insert end \"%d: %s\"\n", i, cert.Subject))
+ }
+ b.WriteString(`
+grid .lb
+
+proc submit {} {
+ global lb
+ puts [$lb get active]
+ exit
+}
+
+button .submit -text "Use" -command submit
+grid .submit
+`)
+ cmd := exec.Command(CmdWish)
+ cmd.Stdin = &b
+ out, err := cmd.Output()
+ if err != nil {
+ return nil, err
+ }
+ lines := strings.Split(string(out), "\n")
+ if len(lines) < 1 {
+ return nil, errors.New("invalid output from authentication form")
+ }
+ t := strings.Split(lines[0], ":")[0]
+ i, err := strconv.Atoi(t)
+ if err != nil {
+ return &tls.Certificate{}, nil
+ }
+ fifos.SinkCert <- fmt.Sprintf("ClientAuth\t%s\t%s", g.host, certs[i].Subject)
+ return tlsCerts[i], nil
+}