]> Sergey Matveev's repositories - godlighty.git/blob - tls.go
Authentication and authorization
[godlighty.git] / tls.go
1 /*
2 godlighty -- highly-customizable HTTP, HTTP/2, HTTPS server
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 godlighty
19
20 import (
21         "crypto/tls"
22         "crypto/x509"
23         "encoding/pem"
24         "errors"
25         "fmt"
26         "io/ioutil"
27         "log"
28 )
29
30 var (
31         NextProtos        = []string{"h2", "http/1.1"}
32         HostToCertificate map[string]*tls.Certificate
33         HostClientAuth    map[string]*x509.CertPool
34 )
35
36 func GetCertificate(chi *tls.ClientHelloInfo) (*tls.Certificate, error) {
37         cert := HostToCertificate[chi.ServerName]
38         if cert == nil {
39                 return nil, errors.New("no certificate found")
40         }
41         return cert, nil
42 }
43
44 func GetConfigForClient(chi *tls.ClientHelloInfo) (*tls.Config, error) {
45         pool := HostClientAuth[chi.ServerName]
46         if pool == nil {
47                 return nil, nil
48         }
49         return &tls.Config{
50                 GetCertificate: GetCertificate,
51                 NextProtos:     NextProtos,
52                 ClientCAs:      pool,
53                 ClientAuth:     tls.RequireAndVerifyClientCert,
54         }, nil
55 }
56
57 func LoadCertificates() {
58         HostToCertificate = make(map[string]*tls.Certificate, len(Hosts))
59         HostClientAuth = make(map[string]*x509.CertPool)
60         for host, cfg := range Hosts {
61                 if cfg.TLS == nil {
62                         continue
63                 }
64                 cert, err := tls.LoadX509KeyPair(cfg.TLS.Cert, cfg.TLS.Key)
65                 if err != nil {
66                         log.Fatalln(err)
67                 }
68                 if cfg.TLS.CACert != "" {
69                         data, err := ioutil.ReadFile(cfg.TLS.CACert)
70                         if err != nil {
71                                 log.Fatalln(err)
72                         }
73                         block, _ := pem.Decode(data)
74                         if block == nil {
75                                 log.Fatalln(fmt.Errorf("no PEM found: %s", cfg.TLS.CACert))
76                         }
77                         if block.Type != "CERTIFICATE" {
78                                 log.Fatalln(fmt.Errorf("non CERTIFICATE: %s", cfg.TLS.CACert))
79                         }
80                         cert.Certificate = append(cert.Certificate, block.Bytes)
81                 }
82                 HostToCertificate[host] = &cert
83                 pool := x509.NewCertPool()
84                 for _, p := range cfg.TLS.ClientCAs {
85                         data, err := ioutil.ReadFile(p)
86                         if err != nil {
87                                 log.Fatalln(err)
88                         }
89                         var block *pem.Block
90                         for len(data) > 0 {
91                                 block, data = pem.Decode(data)
92                                 if block == nil {
93                                         log.Fatalln("can not decode PEM:", p)
94                                 }
95                                 if block.Type != "CERTIFICATE" {
96                                         continue
97                                 }
98                                 ca, err := x509.ParseCertificate(block.Bytes)
99                                 if err != nil {
100                                         log.Fatalln(err)
101                                 }
102                                 pool.AddCert(ca)
103                         }
104                 }
105                 if len(pool.Subjects()) > 0 {
106                         HostClientAuth[host] = pool
107                 }
108         }
109 }
110
111 func NewTLSConfig() *tls.Config {
112         return &tls.Config{
113                 NextProtos:         NextProtos,
114                 GetCertificate:     GetCertificate,
115                 GetConfigForClient: GetConfigForClient,
116         }
117 }