/
config.go
160 lines (134 loc) · 3.77 KB
/
config.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
package cryptoprov
import (
"encoding/json"
"os"
"path/filepath"
"strings"
"github.com/effective-security/xlog"
"github.com/pkg/errors"
"gopkg.in/yaml.v3"
)
// TokenConfig holds PKCS#11 configuration information.
//
// A token may be identified either by serial number or label. If
// both are specified then the first match wins.
//
// Supply this to Configure(), or alternatively use ConfigureFromFile().
type TokenConfig interface {
// Manufacturer name of the manufacturer
Manufacturer() string
// Model name of the device
Model() string
// Full path to PKCS#11 library
Path() string
// Token serial number
TokenSerial() string
// Token label
TokenLabel() string
// Pin is a secret to access the token.
// If it's prefixed with `file:`, then it will be loaded from the file.
Pin() string
// Comma separated key=value pair of attributes(e.g. "ServiceName=x,UserName=y")
Attributes() string
}
type tokenConfig struct {
Man string `json:"Manufacturer" yaml:"manufacturer"`
Mod string `json:"Model" yaml:"model"`
Dir string `json:"Path" yaml:"path"`
Serial string `json:"TokenSerial" yaml:"token_serial"`
Label string `json:"TokenLabel" yaml:"token_label"`
Pwd string `json:"Pin" yaml:"pin"`
Attrs string `json:"Attributes" yaml:"attributes"`
}
// Manufacturer name of the manufacturer
func (c *tokenConfig) Manufacturer() string {
return c.Man
}
// Model name of the device
func (c *tokenConfig) Model() string {
return c.Mod
}
// Full path to PKCS#11 library
func (c *tokenConfig) Path() string {
return c.Dir
}
// Token serial number
func (c *tokenConfig) TokenSerial() string {
return c.Serial
}
// Token label
func (c *tokenConfig) TokenLabel() string {
return c.Label
}
// Pin is a secret to access the token.
// If it's prefixed with `file:`, then it will be loaded from the file.
func (c *tokenConfig) Pin() string {
return c.Pwd
}
// Attributes is list of additional key=value pairs
func (c *tokenConfig) Attributes() string {
return c.Attrs
}
// LoadTokenConfig loads PKCS#11 token configuration
func LoadTokenConfig(filename string) (TokenConfig, error) {
if filename == "" || filename == "inmem" {
return &tokenConfig{Man: "inmem"}, nil
}
cfr, err := os.Open(filename)
if err != nil {
return nil, errors.WithStack(err)
}
defer cfr.Close()
tokenConfig := new(tokenConfig)
if strings.HasSuffix(filename, ".json") {
err = json.NewDecoder(cfr).Decode(tokenConfig)
if err != nil {
return nil, errors.WithMessagef(err, "failed to decode file: %s", filename)
}
} else {
err = yaml.NewDecoder(cfr).Decode(tokenConfig)
if err != nil {
return nil, errors.WithMessagef(err, "failed to decode file: %s", filename)
}
}
pin := tokenConfig.Pin()
if strings.HasPrefix(pin, "file:") {
pinfile := pin[5:]
// try to resolve pin file
cwd, _ := os.Getwd()
folders := []string{
"",
cwd,
filepath.Dir(filename),
}
for _, folder := range folders {
if resolved, err := resolve(pinfile, folder); err == nil {
pinfile = resolved
break
}
logger.KV(xlog.WARNING, "reason", "resolve", "pinfile", pinfile, "basedir", folder)
}
pb, err := os.ReadFile(pinfile)
if err != nil {
return nil, errors.WithMessagef(err, "unable to load PIN for configuration: %s", filename)
}
tokenConfig.Pwd = string(pb)
}
return tokenConfig, nil
}
// resolve returns absolute file name relative to baseDir,
// or NewNotFound error.
func resolve(file string, baseDir string) (resolved string, err error) {
if file == "" {
return file, nil
}
if filepath.IsAbs(file) {
resolved = file
} else if baseDir != "" {
resolved = filepath.Join(baseDir, file)
}
if _, err := os.Stat(resolved); os.IsNotExist(err) {
return resolved, errors.WithMessagef(err, "not found: %v", resolved)
}
return resolved, nil
}