]> Sergey Matveev's repositories - tofuproxy.git/blob - tlsauth.go
531245d23f6e15773995a24bbc7655a4bc5c09ed
[tofuproxy.git] / tlsauth.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         "bytes"
22         "crypto/tls"
23         "crypto/x509"
24         "errors"
25         "fmt"
26         "log"
27         "os"
28         "os/exec"
29         "path/filepath"
30         "strconv"
31         "strings"
32
33         "go.cypherpunks.ru/ucspi"
34         "go.stargrave.org/tofuproxy/fifos"
35 )
36
37 var CCerts string
38
39 type ClientCertificateGetter struct {
40         host string
41 }
42
43 func (g *ClientCertificateGetter) get(
44         cri *tls.CertificateRequestInfo,
45 ) (*tls.Certificate, error) {
46         var b bytes.Buffer
47         b.WriteString(fmt.Sprintf(`
48 wm title . "TLS client authentication: %s"
49
50 label .lVersion -text "Version: %s"
51 grib .lVersion
52
53 set lb [listbox .lb]
54 .lb insert end ""
55 `,
56                 g.host,
57                 ucspi.TLSVersion(cri.Version),
58         ))
59
60         ents, err := os.ReadDir(CCerts)
61         if err != nil {
62                 log.Fatalln(err)
63         }
64         certs := make([]*x509.Certificate, 0, len(ents))
65         tlsCerts := make([]*tls.Certificate, 0, len(ents))
66         for i, ent := range ents {
67                 p := filepath.Join(CCerts, ent.Name())
68                 _, cert, err := ucspi.CertificateFromFile(p)
69                 if err != nil {
70                         log.Fatalln(err)
71                 }
72                 prv, err := ucspi.PrivateKeyFromFile(p)
73                 if err != nil {
74                         log.Fatalln(err)
75                 }
76                 certs = append(certs, cert)
77                 tlsCerts = append(tlsCerts, &tls.Certificate{
78                         Certificate: [][]byte{cert.Raw},
79                         PrivateKey:  prv,
80                 })
81                 b.WriteString(fmt.Sprintf(".lb insert end \"%d: %s\"\n", i, cert.Subject))
82         }
83         b.WriteString(`
84 grid .lb
85
86 proc submit {} {
87     global lb
88     puts [$lb get active]
89     exit
90 }
91
92 button .submit -text "Use" -command submit
93 grid .submit
94 `)
95         cmd := exec.Command(CmdWish)
96         cmd.Stdin = &b
97         out, err := cmd.Output()
98         if err != nil {
99                 return nil, err
100         }
101         lines := strings.Split(string(out), "\n")
102         if len(lines) < 1 {
103                 return nil, errors.New("invalid output from authentication form")
104         }
105         t := strings.Split(lines[0], ":")[0]
106         i, err := strconv.Atoi(t)
107         if err != nil {
108                 return &tls.Certificate{}, nil
109         }
110         fifos.SinkCert <- fmt.Sprintf("ClientAuth\t%s\t%s", g.host, certs[i].Subject)
111         return tlsCerts[i], nil
112 }