cloudflare/cfssl_trust

Public

mirrored from https://github.com/cloudflare/cfssl_trustAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
trust-store-2023.3.0

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

model/certdb/certificate.go

611lines · modecode

1// Package certdb contains Go definitions for the database
2// representation of certificates, as well as associated code for
3// putting it into the database.
4package certdb
5
6import (
7 "crypto/x509"
8 "database/sql"
9 "errors"
10 "fmt"
11 "math/big"
12 "os"
13 "time"
14
15 "github.com/cloudflare/cfssl/signer"
16)
17
18// Finalize finishes a transaction, committing it if needed or rolling
19// back on error.
20func Finalize(err *error, tx *sql.Tx) {
21 if *err == nil {
22 *err = tx.Commit()
23 if *err != nil {
24 fmt.Fprintf(os.Stderr, "[!] failed to commit transaction: %s\n", *err)
25 os.Exit(1)
26 }
27 } else {
28 tx.Rollback()
29 }
30}
31
32// FindCertificateBySKI returns all the certificates with the given
33// SKI.
34func FindCertificateBySKI(db *sql.DB, ski string) ([]*Certificate, error) {
35 tx, err := db.Begin()
36 if err != nil {
37 return nil, err
38 }
39 defer Finalize(&err, tx)
40
41 var certificates []*Certificate
42 rows, err := tx.Query(`
43SELECT aki, serial, not_before, not_after, raw
44 FROM certificates
45 WHERE ski=?`,
46 ski)
47 if err != nil {
48 return nil, err
49 }
50
51 for rows.Next() {
52 cert := &Certificate{
53 SKI: ski,
54 }
55
56 err = rows.Scan(&cert.AKI, &cert.Serial, &cert.NotBefore,
57 &cert.NotAfter, &cert.Raw)
58 if err != nil {
59 return nil, err
60 }
61
62 cert.cert, err = x509.ParseCertificate(cert.Raw)
63 if err != nil {
64 return nil, err
65 }
66
67 certificates = append(certificates, cert)
68 }
69
70 return certificates, nil
71}
72
73// AllCertificates loads all the certificates in the database.
74func AllCertificates(tx *sql.Tx) ([]*Certificate, error) {
75 rows, err := tx.Query("SELECT * FROM certificates")
76 if err != nil {
77 return nil, err
78 }
79
80 var certificates []*Certificate
81 for rows.Next() {
82 cert := &Certificate{}
83 err = rows.Scan(&cert.SKI, &cert.AKI, &cert.Serial, &cert.NotBefore,
84 &cert.NotAfter, &cert.Raw)
85 if err != nil {
86 return nil, err
87 }
88
89 cert.cert, err = x509.ParseCertificate(cert.Raw)
90 if err != nil {
91 return nil, err
92 }
93
94 certificates = append(certificates, cert)
95 }
96
97 return certificates, err
98}
99
100// Table provides an interface for mapping a struct to a table in the
101// database.
102type Table interface {
103 // Insert stores a value in the database; it doesn't check
104 // whether the value exists in the database already and isn't
105 // idempotent --- calling it twice on the same value will
106 // likely violate UNIQUE constraints.
107 Insert(tx *sql.Tx) error
108
109 // Select fills in the value given certain primary fields
110 // being filled in. The function comment for each struct's
111 // implementation should note which fields should be filled in
112 // prior to calling this. It should also return sql.ErrNoRows
113 // if the item doesn't exist in the database.
114 Select(tx *sql.Tx) error
115
116 // Not used yet, but might be useful in the future.
117 // Delete(tx *sql.Tx) error
118 // Update(tx *sql.Tx) error
119}
120
121// Ensure ensures the value is present in the database. It calls
122// Select, and if no rows are returned, it calls Insert. The boolean
123// will be true if the value was inserted. This value is meaningless
124// if err is non-nil.
125func Ensure(table Table, tx *sql.Tx) (bool, error) {
126 var inserted bool
127 err := table.Select(tx)
128 if err == sql.ErrNoRows {
129 err = table.Insert(tx)
130 inserted = true
131 }
132 return inserted, err
133}
134
135// Certificate models the certificate table.
136type Certificate struct {
137 SKI string
138 AKI string
139 Serial []byte
140 NotBefore int64
141 NotAfter int64
142 Raw []byte
143 cert *x509.Certificate
144} // UNIQUE(ski, serial)
145
146// Insert stores the Certificate in the database.
147func (cert *Certificate) Insert(tx *sql.Tx) error {
148 _, err := tx.Exec(`INSERT INTO certificates (ski, aki, serial, not_before, not_after, raw) values (?, ?, ?, ?, ?, ?)`, cert.SKI, cert.AKI, cert.Serial, cert.NotBefore, cert.NotAfter, cert.Raw)
149 return err
150}
151
152// Select requires the SKI and Serial fields to be filled in.
153func (cert *Certificate) Select(tx *sql.Tx) error {
154 row := tx.QueryRow(`SELECT aki, not_before, not_after, raw FROM certificates WHERE ski=? and serial=?`, cert.SKI, cert.Serial)
155 err := row.Scan(&cert.AKI, &cert.NotBefore, &cert.NotAfter, &cert.Raw)
156 if err != nil {
157 return err
158 }
159
160 cert.cert, err = x509.ParseCertificate(cert.Raw)
161 return err
162}
163
164// Releases looks up all the releases for a certificate.
165func (cert *Certificate) Releases(tx *sql.Tx) ([]*Release, error) {
166 var releases []*Release
167
168 rows, err := tx.Query("SELECT release FROM roots WHERE ski=? AND serial=?", cert.SKI, cert.Serial)
169 if err != nil {
170 return nil, err
171 }
172
173 for rows.Next() {
174 rel := &Release{
175 Bundle: "ca",
176 }
177
178 err = rows.Scan(&rel.Version)
179 if err != nil {
180 return nil, err
181 }
182
183 releases = append(releases, rel)
184 }
185 rows.Close()
186
187 rows, err = tx.Query("SELECT release FROM intermediates WHERE ski=? AND serial=?", cert.SKI, cert.Serial)
188 if err != nil {
189 return nil, err
190 }
191
192 for rows.Next() {
193 rel := &Release{
194 Bundle: "int",
195 }
196
197 err = rows.Scan(&rel.Version)
198 if err != nil {
199 return nil, err
200 }
201
202 releases = append(releases, rel)
203 }
204
205 for _, rel := range releases {
206 err = rel.Select(tx)
207 if err != nil {
208 return nil, err
209 }
210 }
211
212 return releases, nil
213}
214
215// Revoked returns true if the certificate was revoked before the
216// timestamp passed in.
217func (cert *Certificate) Revoked(tx *sql.Tx, when int64) (bool, error) {
218 if err := cert.Select(tx); err != nil {
219 return true, err
220 }
221
222 var count int
223 row := tx.QueryRow(`SELECT count(*) FROM revocations WHERE ski=? AND revoked_at <= ?`, cert.SKI, when)
224 err := row.Scan(&count)
225 if err != nil {
226 return true, err
227 }
228
229 return count > 0, nil
230}
231
232// Revoke marks the certificate as revoked.
233func (cert *Certificate) Revoke(tx *sql.Tx, mechanism, reason string, when int64) error {
234 if err := cert.Select(tx); err != nil {
235 return err
236 }
237
238 rev := &Revocation{
239 SKI: cert.SKI,
240 RevokedAt: when,
241 Mechanism: mechanism,
242 Reason: reason,
243 }
244
245 // We ignore the inserted value because if it returns false, that means
246 // the certificate has already been revoked.
247 _, err := Ensure(rev, tx)
248 return err
249}
250
251// X509 returns the *crypto/x509.Certificate from the certificate.
252func (cert *Certificate) X509() *x509.Certificate {
253 return cert.cert
254}
255
256var nullSerial = big.NewInt(0)
257
258// NewCertificate creates a Certificate from a crypto/x509 Certificate
259// structure.
260func NewCertificate(cert *x509.Certificate) *Certificate {
261 c := &Certificate{
262 SKI: fmt.Sprintf("%x", cert.SubjectKeyId),
263 AKI: fmt.Sprintf("%x", cert.AuthorityKeyId),
264 Serial: cert.SerialNumber.Bytes(),
265 NotBefore: cert.NotBefore.Unix(),
266 NotAfter: cert.NotAfter.Unix(),
267 Raw: cert.Raw,
268 }
269
270 // Workaround the NOT NULL constraint.
271 if cert.SerialNumber.Cmp(nullSerial) == 0 {
272 c.Serial = []byte{0}
273 }
274
275 // Work around the fact that many early CA roots don't have an
276 // SKI. This uses the method found in RFC 5280 Section 4.2.1.2
277 // (1).
278 if c.SKI == "" {
279 ski, err := signer.ComputeSKI(cert)
280 if err != nil {
281 panic("invalid public key in root certificate")
282 }
283
284 c.SKI = fmt.Sprintf("%x", ski)
285 }
286
287 c.cert = cert
288
289 return c
290}
291
292// AIA models the aia table.
293type AIA struct {
294 SKI string // Primary key.
295 URL string
296}
297
298// Insert stores the release in the database.
299func (aia *AIA) Insert(tx *sql.Tx) error {
300 _, err := tx.Exec(`INSERT INTO aia (ski, url) values (?, ?)`, aia.SKI, aia.URL)
301 return err
302}
303
304// Select requires the SKI field to be filled in.
305func (aia *AIA) Select(tx *sql.Tx) error {
306 row := tx.QueryRow(`SELECT url FROM aia WHERE ski=?`, aia.SKI)
307 err := row.Scan(&aia.URL)
308 if err != nil {
309 return err
310 }
311
312 return nil
313}
314
315// NewAIA populates an AIA structure from a Certificate.
316func NewAIA(cert *Certificate) *AIA {
317 if len(cert.cert.IssuingCertificateURL) == 0 {
318 return nil
319 }
320
321 // Arbitrary choice: store the first HTTP URL. We can always
322 // look up the other URLs later and replace this one if
323 // need be.
324 return &AIA{
325 SKI: cert.AKI,
326 URL: cert.cert.IssuingCertificateURL[0],
327 }
328}
329
330func validBundle(bundle string) bool {
331 switch bundle {
332 case "ca", "int":
333 return true
334 default:
335 return false
336 }
337}
338
339func tableForBundle(bundle string) string {
340 switch bundle {
341 case "ca":
342 return "root"
343 case "int":
344 return "intermediate"
345 default:
346 // The bundle should have been validated by here; it's better
347 // to panic and stop the world (generating a stack trace)
348 // than to continue.
349 panic("certdb: bundle should have been validated before the table selection")
350 }
351}
352
353// Release models the root_releases and intermediate_releases tables.
354type Release struct {
355 Bundle string // Is this a CA or intermediate release?
356 Version string
357 ReleasedAt int64
358}
359
360func (r *Release) validBundle() bool {
361 return validBundle(r.Bundle)
362}
363
364func (r *Release) table() string {
365 return tableForBundle(r.Bundle)
366}
367
368func (r *Release) errInvalidBundle() error {
369 return errors.New("certdb: invalid bundle" + r.Bundle + " (valid bundles are ca|int)")
370}
371
372// NewRelease verifies the bundle is valid, and creates a new Release
373// with the current time stamp.
374func NewRelease(bundle, version string) (*Release, error) {
375 r := &Release{
376 Bundle: bundle,
377 Version: version,
378 ReleasedAt: time.Now().Unix(),
379 }
380
381 if !r.validBundle() {
382 return nil, r.errInvalidBundle()
383 }
384
385 return r, nil
386}
387
388// Insert stores the Release in the database.
389func (r *Release) Insert(tx *sql.Tx) error {
390 if !r.validBundle() {
391 return r.errInvalidBundle()
392 }
393
394 query := fmt.Sprintf("INSERT INTO %s_releases (version, released_at) VALUES (?, ?)",
395 r.table())
396 _, err := tx.Exec(query, r.Version, r.ReleasedAt)
397 return err
398}
399
400// Select requires the Version field to have been populated.
401func (r *Release) Select(tx *sql.Tx) error {
402 if !r.validBundle() {
403 return r.errInvalidBundle()
404 }
405
406 query := fmt.Sprintf("SELECT released_at FROM %s_releases WHERE version=?", r.table())
407 row := tx.QueryRow(query, r.Version)
408 return row.Scan(&r.ReleasedAt)
409}
410
411// Count requires the Release to be Selectable, and will return the
412// number of certificates in the release.
413func (r *Release) Count(db *sql.DB) (int, error) {
414 tx, err := db.Begin()
415 if err != nil {
416 return 0, err
417 }
418 defer tx.Rollback()
419
420 err = r.Select(tx)
421 if err != nil {
422 if err == sql.ErrNoRows {
423 err = errors.New("model/certdb: release doesn't exist")
424 }
425 return 0, err
426 }
427
428 var count int
429 q := fmt.Sprintf("SELECT count(*) FROM %ss WHERE release = ?", r.table())
430 row := tx.QueryRow(q, r.Version)
431 err = row.Scan(&count)
432 if err == nil {
433 err = tx.Commit()
434 }
435 return count, err
436}
437
438// Previous returns the previous release. The Release must fully filled out.
439func (r *Release) Previous(db *sql.DB) (*Release, error) {
440 tx, err := db.Begin()
441 if err != nil {
442 return nil, err
443 }
444 defer tx.Rollback()
445
446 var prev = &Release{Bundle: r.Bundle}
447 query := fmt.Sprintf(`SELECT version, released_at FROM %s_releases WHERE released_at < ? ORDER BY released_at DESC LIMIT 1`, r.table())
448 row := tx.QueryRow(query, r.ReleasedAt)
449 err = row.Scan(&prev.Version, &prev.ReleasedAt)
450 if err == nil {
451 err = tx.Commit()
452 }
453 return prev, err
454}
455
456// AllReleases returns the list of all releases, sorted in reverse chronological
457// order.
458func AllReleases(db *sql.DB, bundle string) ([]*Release, error) {
459 if !validBundle(bundle) {
460 return nil, errors.New("model/certdb: invalid bundle " + bundle)
461 }
462
463 tx, err := db.Begin()
464 if err != nil {
465 return nil, err
466 }
467 defer tx.Rollback() // nop if commit is called.
468
469 tbl := tableForBundle(bundle)
470 var releases []*Release
471
472 rows, err := tx.Query(fmt.Sprintf(`SELECT version,released_at FROM %s_releases ORDER BY released_at DESC`, tbl))
473 if err != nil {
474 return nil, err
475 }
476
477 for rows.Next() {
478 release := &Release{Bundle: bundle}
479 err = rows.Scan(&release.Version, &release.ReleasedAt)
480 if err != nil {
481 break
482 }
483 releases = append(releases, release)
484 }
485
486 if err == nil {
487 err = tx.Commit()
488 }
489
490 return releases, err
491}
492
493// LatestRelease returns the latest release.
494func LatestRelease(db *sql.DB, bundle string) (*Release, error) {
495 if !validBundle(bundle) {
496 return nil, errors.New("model/certdb: invalid bundle " + bundle)
497 }
498
499 tx, err := db.Begin()
500 if err != nil {
501 return nil, err
502 }
503 defer tx.Rollback() // nop if commit is called.
504
505 release := &Release{Bundle: bundle}
506 tbl := tableForBundle(bundle)
507 q := fmt.Sprintf(`SELECT version,released_at FROM %s_releases ORDER BY released_at DESC LIMIT 1`, tbl)
508
509 row := tx.QueryRow(q)
510 err = row.Scan(&release.Version, &release.ReleasedAt)
511 if err == nil {
512 err = tx.Commit()
513 }
514
515 return release, err
516}
517
518// FetchRelease looks for the specified release. It does its own
519// transaction to match the style of the other release fetching
520// functions.
521func FetchRelease(db *sql.DB, bundle, version string) (*Release, error) {
522 rel, err := NewRelease(bundle, version)
523 if err != nil {
524 return nil, err
525 }
526
527 tx, err := db.Begin()
528 if err != nil {
529 return nil, err
530 }
531 defer tx.Rollback() // nop if commit is called.
532
533 err = rel.Select(tx)
534 if err != nil {
535 return nil, err
536 }
537
538 err = tx.Commit()
539 if err != nil {
540 return nil, err
541 }
542
543 return rel, nil
544}
545
546// A CertificateRelease pairs a Certificate and Release to enable adding
547// certificates to the relevant release tables.
548type CertificateRelease struct {
549 Certificate *Certificate
550 Release *Release
551}
552
553// NewCertificateRelease is a convenience function for building a
554// CertificateRelease structure.
555func NewCertificateRelease(c *Certificate, r *Release) *CertificateRelease {
556 return &CertificateRelease{
557 Certificate: c,
558 Release: r,
559 }
560}
561
562// Insert stores the CertificateRelease in the database. It does no
563// checking to determine if the CertificateRelease is already in the
564// database, and will fail if it's already present in the database
565// (due to UNIQUE constraints).
566func (cr *CertificateRelease) Insert(tx *sql.Tx) error {
567 query := fmt.Sprintf("INSERT INTO %ss (ski, serial, release) VALUES (?, ?, ?)", cr.Release.table())
568 _, err := tx.Exec(query, cr.Certificate.SKI, cr.Certificate.Serial, cr.Release.Version)
569 return err
570}
571
572// Select requires the Certificate field to have the SKI and Serial
573// filled in, and the Release field to have the Version field filled
574// in.
575func (cr *CertificateRelease) Select(tx *sql.Tx) error {
576 var count int
577 query := fmt.Sprintf("SELECT count(*) FROM %ss WHERE ski=? AND serial=? AND release=?", cr.Release.table())
578 row := tx.QueryRow(query, cr.Certificate.SKI, cr.Certificate.Serial, cr.Release.Version)
579 err := row.Scan(&count)
580 if err == nil && count == 0 {
581 return sql.ErrNoRows
582 }
583 return err
584}
585
586// Revocation models the revocations table.
587type Revocation struct {
588 SKI string
589 RevokedAt int64
590 Mechanism string
591 Reason string
592}
593
594// Select requires the SKI field to be filled in. Note that only one
595// revocation per SKI should exist.
596func (rev *Revocation) Select(tx *sql.Tx) error {
597 row := tx.QueryRow(`SELECT revoked_at, mechanism, reason FROM revocations WHERE ski=?`, rev.SKI)
598 err := row.Scan(&rev.RevokedAt, &rev.Mechanism, &rev.Reason)
599 if err != nil {
600 return err
601 }
602
603 return nil
604}
605
606// Insert adds the revocation to the database if no revocation exists
607// yet.
608func (rev *Revocation) Insert(tx *sql.Tx) error {
609 _, err := tx.Exec(`INSERT INTO revocations (ski, revoked_at, mechanism, reason) VALUES (?, ?, ?, ?)`, rev.SKI, rev.RevokedAt, rev.Mechanism, rev.Reason)
610 return err
611}
612