]> Sergey Matveev's repositories - godlighty.git/blob - tls.go
Use mtime instead of ctime
[godlighty.git] / tls.go
1 // godlighty -- highly-customizable HTTP, HTTP/2, HTTPS server
2 // Copyright (C) 2021-2024 Sergey Matveev <stargrave@stargrave.org>
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, version 3 of the License.
7 //
8 // This program is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 // GNU General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
15
16 package godlighty
17
18 import (
19         "crypto/tls"
20         "crypto/x509"
21         "encoding/pem"
22         "errors"
23         "fmt"
24         "log"
25         "os"
26 )
27
28 var (
29         NextProtos = []string{"h2", "http/1.1"}
30
31         HostToECDSACertificate map[string]*tls.Certificate
32         HostECDSAClientAuth    map[string]*x509.CertPool
33
34         HostToEdDSACertificate map[string]*tls.Certificate
35         HostEdDSAClientAuth    map[string]*x509.CertPool
36
37         HostToGOSTCertificate map[string]*tls.Certificate
38         HostGOSTClientAuth    map[string]*x509.CertPool
39 )
40
41 func CHIHasTLS13(chi *tls.ClientHelloInfo) bool {
42         for _, v := range chi.SupportedVersions {
43                 if v == tls.VersionTLS13 {
44                         return true
45                 }
46         }
47         return false
48 }
49
50 func CHIHasEdDSA(chi *tls.ClientHelloInfo) bool {
51         if !CHIHasTLS13(chi) {
52                 return false
53         }
54         for _, ss := range chi.SignatureSchemes {
55                 if ss == tls.Ed25519 {
56                         return true
57                 }
58         }
59         return false
60 }
61
62 func GetCertificate(chi *tls.ClientHelloInfo) (*tls.Certificate, error) {
63         if CHIHasGOST(chi) {
64                 if cert := HostToGOSTCertificate[chi.ServerName]; cert != nil {
65                         return cert, nil
66                 }
67         }
68         var cert *tls.Certificate
69         if len(HostToECDSACertificate) == 0 {
70                 cert = HostToEdDSACertificate[chi.ServerName]
71         } else {
72                 if CHIHasEdDSA(chi) {
73                         if cert := HostToEdDSACertificate[chi.ServerName]; cert != nil {
74                                 return cert, nil
75                         }
76                 }
77                 cert = HostToECDSACertificate[chi.ServerName]
78         }
79         if cert == nil {
80                 return nil, errors.New("no certificate found")
81         }
82         return cert, nil
83 }
84
85 func GetConfigForClient(chi *tls.ClientHelloInfo) (*tls.Config, error) {
86         var pool *x509.CertPool
87         if CHIHasGOST(chi) {
88                 pool = HostGOSTClientAuth[chi.ServerName]
89         }
90         if pool == nil && (CHIHasEdDSA(chi) || len(HostECDSAClientAuth) == 0) {
91                 pool = HostEdDSAClientAuth[chi.ServerName]
92         }
93         if pool == nil {
94                 pool = HostECDSAClientAuth[chi.ServerName]
95         }
96         if pool == nil {
97                 return nil, nil
98         }
99         return &tls.Config{
100                 GetCertificate: GetCertificate,
101                 NextProtos:     NextProtos,
102                 ClientCAs:      pool,
103                 ClientAuth:     tls.RequireAndVerifyClientCert,
104         }, nil
105 }
106
107 func loadCertificates(
108         host string,
109         cfg *TLSCfg,
110         hostToCertificate *map[string]*tls.Certificate,
111         hostClientAuth *map[string]*x509.CertPool,
112 ) {
113         if cfg == nil {
114                 return
115         }
116         cert, err := tls.LoadX509KeyPair(cfg.Cert, cfg.Key)
117         if err != nil {
118                 log.Fatalln(err)
119         }
120         if cfg.CACert != "" {
121                 data, err := os.ReadFile(cfg.CACert)
122                 if err != nil {
123                         log.Fatalln(err)
124                 }
125                 block, _ := pem.Decode(data)
126                 if block == nil {
127                         log.Fatalln(fmt.Errorf("no PEM found: %s", cfg.CACert))
128                 }
129                 if block.Type != "CERTIFICATE" {
130                         log.Fatalln(fmt.Errorf("non CERTIFICATE: %s", cfg.CACert))
131                 }
132                 cert.Certificate = append(cert.Certificate, block.Bytes)
133         }
134         (*hostToCertificate)[host] = &cert
135         pool := x509.NewCertPool()
136         for _, p := range cfg.ClientCAs {
137                 data, err := os.ReadFile(p)
138                 if err != nil {
139                         log.Fatalln(err)
140                 }
141                 var block *pem.Block
142                 for len(data) > 0 {
143                         block, data = pem.Decode(data)
144                         if block == nil {
145                                 log.Fatalln("can not decode PEM:", p)
146                         }
147                         if block.Type != "CERTIFICATE" {
148                                 continue
149                         }
150                         ca, err := x509.ParseCertificate(block.Bytes)
151                         if err != nil {
152                                 log.Fatalln(err)
153                         }
154                         pool.AddCert(ca)
155                         (*hostClientAuth)[host] = pool
156                 }
157         }
158 }
159
160 func LoadCertificates() {
161         HostToECDSACertificate = make(map[string]*tls.Certificate, len(Hosts))
162         HostECDSAClientAuth = make(map[string]*x509.CertPool)
163         HostToEdDSACertificate = make(map[string]*tls.Certificate, len(Hosts))
164         HostEdDSAClientAuth = make(map[string]*x509.CertPool)
165         HostToGOSTCertificate = make(map[string]*tls.Certificate, len(Hosts))
166         HostGOSTClientAuth = make(map[string]*x509.CertPool)
167         for host, cfg := range Hosts {
168                 loadCertificates(host, cfg.ECDSATLS, &HostToECDSACertificate, &HostECDSAClientAuth)
169                 loadCertificates(host, cfg.EdDSATLS, &HostToEdDSACertificate, &HostEdDSAClientAuth)
170                 loadCertificates(host, cfg.GOSTTLS, &HostToGOSTCertificate, &HostGOSTClientAuth)
171         }
172 }
173
174 func NewTLSConfig() *tls.Config {
175         return &tls.Config{
176                 MinVersion:         tls.VersionTLS12,
177                 NextProtos:         NextProtos,
178                 GetCertificate:     GetCertificate,
179                 GetConfigForClient: GetConfigForClient,
180         }
181 }