-
Notifications
You must be signed in to change notification settings - Fork 153
/
store.go
156 lines (145 loc) · 4.55 KB
/
store.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
// Copyright 2020 Anapaya Systems
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package trust
import (
"context"
"encoding/pem"
"errors"
"fmt"
"os"
"path/filepath"
"time"
"github.com/scionproto/scion/pkg/private/serrors"
"github.com/scionproto/scion/pkg/scrypto/cppki"
)
var (
// ErrAlreadyExists indicates a file is ignored because the contents have
// already been loaded previously.
ErrAlreadyExists = serrors.New("already exists")
// ErrOutsideValidity indicates a file is ignored because the current time
// is outside of the certificates validity period.
ErrOutsideValidity = serrors.New("outside validity")
)
// LoadResult indicates which files were loaded, which files were ignored.
type LoadResult struct {
Loaded []string
Ignored map[string]error
}
// LoadChains loads all *.pem files located in a directory in the database after
// validating first that each one is a valid CP certificate chains. All *.pem
// files that are not valid chains are ignored.
func LoadChains(ctx context.Context, dir string, db DB) (LoadResult, error) {
if _, err := os.Stat(dir); err != nil {
return LoadResult{}, serrors.WithCtx(err, "dir", dir)
}
files, err := filepath.Glob(fmt.Sprintf("%s/*.pem", dir))
if err != nil {
return LoadResult{}, serrors.WithCtx(err, "dir", dir)
}
res := LoadResult{Ignored: map[string]error{}}
// TODO(roosd): should probably be a transaction.
for _, f := range files {
chain, err := cppki.ReadPEMCerts(f)
if err != nil {
res.Ignored[f] = err
continue
}
if err := cppki.ValidateChain(chain); err != nil {
res.Ignored[f] = err
continue
}
validity := cppki.Validity{NotBefore: chain[0].NotBefore, NotAfter: chain[0].NotAfter}
if !validity.Contains(time.Now()) {
res.Ignored[f] = ErrOutsideValidity
continue
}
ia, err := cppki.ExtractIA(chain[0].Subject)
if err != nil {
res.Ignored[f] = err
continue
}
trcs, _, err := activeTRCs(ctx, db, ia.ISD())
if errors.Is(err, errNotFound) {
res.Ignored[f] = serrors.New("TRC not found", "isd", ia.ISD())
continue
}
if err != nil {
return res, serrors.WrapStr("loading TRC(s) to verify certificate chain", err,
"file", f)
}
var verifyErrors serrors.List
for _, trc := range trcs {
opts := cppki.VerifyOptions{TRC: []*cppki.TRC{&trc.TRC}}
if err := cppki.VerifyChain(chain, opts); err != nil {
verifyErrors = append(verifyErrors, err)
}
}
if len(verifyErrors) == len(trcs) {
res.Ignored[f] = verifyErrors.ToError()
continue
}
inserted, err := db.InsertChain(ctx, chain)
if err != nil {
return res, serrors.WrapStr("inserting certificate chain", err, "file", f)
}
if !inserted {
res.Ignored[f] = serrors.Wrap(ErrAlreadyExists, err)
continue
}
res.Loaded = append(res.Loaded, f)
}
return res, nil
}
// LoadTRCs loads all *.trc located in a directory in the database. This
// function exits on the first encountered error. TRCs with a not before time
// in the future are ignored.
func LoadTRCs(ctx context.Context, dir string, db DB) (LoadResult, error) {
if _, err := os.Stat(dir); err != nil {
return LoadResult{}, serrors.WithCtx(err, "dir", dir)
}
files, err := filepath.Glob(fmt.Sprintf("%s/*.trc", dir))
if err != nil {
return LoadResult{}, serrors.WithCtx(err, "dir", dir)
}
res := LoadResult{Ignored: map[string]error{}}
// TODO(roosd): should probably be a transaction.
for _, f := range files {
raw, err := os.ReadFile(f)
if err != nil {
return res, serrors.WithCtx(err, "file", f)
}
block, _ := pem.Decode(raw)
if block != nil && block.Type == "TRC" {
raw = block.Bytes
}
trc, err := cppki.DecodeSignedTRC(raw)
if err != nil {
return res, serrors.WithCtx(err, "file", f)
}
if time.Now().Before(trc.TRC.Validity.NotBefore) {
res.Ignored[f] = serrors.New("TRC in the future", "validity", trc.TRC.Validity)
continue
}
inserted, err := db.InsertTRC(ctx, trc)
if err != nil {
return res, serrors.WithCtx(err, "file", f)
}
if !inserted {
res.Ignored[f] = serrors.Wrap(ErrAlreadyExists, err)
continue
}
res.Loaded = append(res.Loaded, f)
}
return res, nil
}