-
Notifications
You must be signed in to change notification settings - Fork 1
/
filestore.go
167 lines (138 loc) · 3.83 KB
/
filestore.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
// Package file provides a Storage implementation for storing x509 certificates as files
package file
import (
"context"
"crypto/x509"
"encoding/pem"
"fmt"
"io/fs"
"darvaza.org/darvaza/shared/os"
"darvaza.org/darvaza/shared/os/flock"
"darvaza.org/darvaza/shared/x509utils"
)
var (
_ x509utils.ReadStore = (*Store)(nil)
_ x509utils.WriteStore = (*Store)(nil)
)
// Store is a darvaza Storage implementation for storing x509 certificates as files
type Store struct {
fl flock.Options
}
// Get will return the first x509 certificate and an error, the certificate having the same
// common name as the name parameter
func (m *Store) Get(_ context.Context, name string) (*x509.Certificate, error) {
_, cert, err := m.fileCertFromName(name)
return cert, err
}
// ForEach will walk the store and ececute the StoreIterFunc for each certificate
// it can decode
func (m *Store) ForEach(_ context.Context, f x509utils.StoreIterFunc) error {
return m.forEachCert(func(_ string, cert *x509.Certificate) bool {
if err := f(cert); err != nil {
// TODO: terminate or just continue?
return true
}
// next
return false
})
}
// Put will create a file in the store with the given name and the given certificate
// it will write in the fil eteh content of cert.Raw field
func (m *Store) Put(_ context.Context, name string, cert *x509.Certificate) error {
return m.fl.WriteFile(name, cert.Raw, 0666)
}
// Delete will delete the first certificate with the same common name as
// the given parameter
func (m *Store) Delete(_ context.Context, name string) error {
file, _, err := m.fileCertFromName(name)
if err != nil {
return err
}
err = os.Remove(file)
if err == nil || os.IsNotExist(err) {
return nil
}
return err
}
// DeleteCert will delete from the store the certificate given as parameter
func (m *Store) DeleteCert(_ context.Context, cert *x509.Certificate) error {
name := cert.Subject.CommonName
file, _, err := m.fileCertFromName(name)
if err != nil {
return err
}
err = os.Remove(file)
if err == nil || os.IsNotExist(err) {
return nil
}
return err
}
// Options is a struct containing the storage options
// in the case of FileStorage it only contains a directory name
// and the file mode
type Options struct {
Directory string
DirMode fs.FileMode
}
// DefaultOptions for FileStorage Options
var DefaultOptions = Options{
Directory: "darvaza_store",
DirMode: 0700,
}
// NewStore will create a new File Storage. If no options
// are given it will use the DefaultOptions
func NewStore(options Options) (Store, error) {
result := Store{}
if options.Directory == "" {
options.Directory = DefaultOptions.Directory
}
if options.DirMode == 0 {
options.DirMode = DefaultOptions.DirMode
}
fl := flock.Options{
Base: options.Directory,
Create: true,
DirMode: options.DirMode,
}
err := fl.MkdirBase(0)
if err != nil {
return result, err
}
result.fl = fl
return result, nil
}
func (m *Store) fileCertFromName(name string) (string, *x509.Certificate, error) {
var match *x509.Certificate
var filename string
fn := func(fl string, cert *x509.Certificate) bool {
if verifyHostname(cert, name) {
match = cert
filename = fl
return true // term
}
return false
}
_ = m.forEachCert(fn)
if match != nil {
return filename, match, nil
}
return "", nil, fmt.Errorf("certificate not found")
}
func verifyHostname(cert *x509.Certificate, name string) bool {
if len(cert.URIs) == 0 {
// an "old" certificate, no SAN
return cert.Subject.CommonName == name
}
return cert.VerifyHostname(name) == nil
}
func (m *Store) forEachCert(fn func(string, *x509.Certificate) bool) error {
if fn == nil {
return nil
}
return x509utils.ReadStringPEM(m.fl.Base, func(fl string, block *pem.Block) bool {
if cert, _ := x509utils.BlockToCertificate(block); cert != nil {
return fn(fl, cert)
}
return false
})
}