/* tofuproxy -- HTTP proxy with TLS certificates management Copyright (C) 2021 Sergey Matveev 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 . */ 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/caches" "go.stargrave.org/tofuproxy/fifos" ) var CCerts string type ClientCertificateGetter struct { host string auth bool } func (g *ClientCertificateGetter) get( cri *tls.CertificateRequestInfo, ) (*tls.Certificate, error) { caches.TLSAuthCacheM.RLock() tlsCert := caches.TLSAuthCache[g.host] caches.TLSAuthCacheM.RUnlock() if tlsCert != nil { return tlsCert, nil } 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.LogTLSAuth <- fmt.Sprintf("%s\t%s", g.host, certs[i].Subject) caches.TLSAuthCacheM.Lock() caches.TLSAuthCache[g.host] = tlsCerts[i] caches.TLSAuthCacheM.Unlock() g.auth = true return tlsCerts[i], nil }