1 package sa
2
3 import (
4 "context"
5 "crypto/x509"
6 "encoding/json"
7 "errors"
8 "fmt"
9 "strings"
10 "time"
11
12 "github.com/jmhodges/clock"
13 "github.com/prometheus/client_golang/prometheus"
14 "golang.org/x/crypto/ocsp"
15 "google.golang.org/protobuf/types/known/emptypb"
16 "google.golang.org/protobuf/types/known/timestamppb"
17
18 "github.com/letsencrypt/boulder/core"
19 corepb "github.com/letsencrypt/boulder/core/proto"
20 "github.com/letsencrypt/boulder/db"
21 berrors "github.com/letsencrypt/boulder/errors"
22 bgrpc "github.com/letsencrypt/boulder/grpc"
23 blog "github.com/letsencrypt/boulder/log"
24 "github.com/letsencrypt/boulder/revocation"
25 sapb "github.com/letsencrypt/boulder/sa/proto"
26 )
27
28 var (
29 errIncompleteRequest = errors.New("incomplete gRPC request message")
30 )
31
32
33
34
35
36
37 type SQLStorageAuthority struct {
38 sapb.UnimplementedStorageAuthorityServer
39 *SQLStorageAuthorityRO
40
41 dbMap *db.WrappedMap
42
43
44
45
46
47
48 rateLimitWriteErrors prometheus.Counter
49 }
50
51
52
53
54
55 func NewSQLStorageAuthorityWrapping(
56 ssaro *SQLStorageAuthorityRO,
57 dbMap *db.WrappedMap,
58 stats prometheus.Registerer,
59 ) (*SQLStorageAuthority, error) {
60 rateLimitWriteErrors := prometheus.NewCounter(prometheus.CounterOpts{
61 Name: "rate_limit_write_errors",
62 Help: "number of failed ratelimit update transactions during AddCertificate",
63 })
64 stats.MustRegister(rateLimitWriteErrors)
65
66 ssa := &SQLStorageAuthority{
67 SQLStorageAuthorityRO: ssaro,
68 dbMap: dbMap,
69 rateLimitWriteErrors: rateLimitWriteErrors,
70 }
71
72 return ssa, nil
73 }
74
75
76
77 func NewSQLStorageAuthority(
78 dbMap *db.WrappedMap,
79 dbReadOnlyMap *db.WrappedMap,
80 dbIncidentsMap *db.WrappedMap,
81 parallelismPerRPC int,
82 lagFactor time.Duration,
83 clk clock.Clock,
84 logger blog.Logger,
85 stats prometheus.Registerer,
86 ) (*SQLStorageAuthority, error) {
87 ssaro, err := NewSQLStorageAuthorityRO(
88 dbReadOnlyMap, dbIncidentsMap, stats, parallelismPerRPC, lagFactor, clk, logger)
89 if err != nil {
90 return nil, err
91 }
92
93 return NewSQLStorageAuthorityWrapping(ssaro, dbMap, stats)
94 }
95
96
97 func (ssa *SQLStorageAuthority) NewRegistration(ctx context.Context, req *corepb.Registration) (*corepb.Registration, error) {
98 if len(req.Key) == 0 || len(req.InitialIP) == 0 {
99 return nil, errIncompleteRequest
100 }
101
102 reg, err := registrationPbToModel(req)
103 if err != nil {
104 return nil, err
105 }
106
107 reg.CreatedAt = ssa.clk.Now()
108
109 err = ssa.dbMap.Insert(ctx, reg)
110 if err != nil {
111 if db.IsDuplicate(err) {
112
113
114 return nil, berrors.DuplicateError("key is already in use for a different account")
115 }
116 return nil, err
117 }
118 return registrationModelToPb(reg)
119 }
120
121
122 func (ssa *SQLStorageAuthority) UpdateRegistration(ctx context.Context, req *corepb.Registration) (*emptypb.Empty, error) {
123 if req == nil || req.Id == 0 || len(req.Key) == 0 || len(req.InitialIP) == 0 {
124 return nil, errIncompleteRequest
125 }
126
127 curr, err := selectRegistration(ctx, ssa.dbMap, "id", req.Id)
128 if err != nil {
129 if db.IsNoRows(err) {
130 return nil, berrors.NotFoundError("registration with ID '%d' not found", req.Id)
131 }
132 return nil, err
133 }
134
135 update, err := registrationPbToModel(req)
136 if err != nil {
137 return nil, err
138 }
139
140
141
142 update.LockCol = curr.LockCol
143 n, err := ssa.dbMap.Update(ctx, update)
144 if err != nil {
145 if db.IsDuplicate(err) {
146
147
148 return nil, berrors.DuplicateError("key is already in use for a different account")
149 }
150 return nil, err
151 }
152 if n == 0 {
153 return nil, berrors.NotFoundError("registration with ID '%d' not found", req.Id)
154 }
155
156 return &emptypb.Empty{}, nil
157 }
158
159
160 func (ssa *SQLStorageAuthority) AddSerial(ctx context.Context, req *sapb.AddSerialRequest) (*emptypb.Empty, error) {
161 if req.Serial == "" || req.RegID == 0 || req.CreatedNS == 0 || req.ExpiresNS == 0 {
162 return nil, errIncompleteRequest
163 }
164 err := ssa.dbMap.Insert(ctx, &recordedSerialModel{
165 Serial: req.Serial,
166 RegistrationID: req.RegID,
167 Created: time.Unix(0, req.CreatedNS),
168 Expires: time.Unix(0, req.ExpiresNS),
169 })
170 if err != nil {
171 return nil, err
172 }
173 return &emptypb.Empty{}, nil
174 }
175
176
177
178 func (ssa *SQLStorageAuthority) SetCertificateStatusReady(ctx context.Context, req *sapb.Serial) (*emptypb.Empty, error) {
179 res, err := ssa.dbMap.ExecContext(ctx,
180 `UPDATE certificateStatus
181 SET status = ?
182 WHERE status = ? AND
183 serial = ?`,
184 string(core.OCSPStatusGood),
185 string(core.OCSPStatusNotReady),
186 req.Serial,
187 )
188 if err != nil {
189 return nil, err
190 }
191 rows, err := res.RowsAffected()
192 if err != nil {
193 return nil, err
194 }
195 if rows == 0 {
196 return nil, errors.New("failed to set certificate status to ready")
197 }
198
199 return &emptypb.Empty{}, nil
200 }
201
202
203
204
205
206 func (ssa *SQLStorageAuthority) AddPrecertificate(ctx context.Context, req *sapb.AddCertificateRequest) (*emptypb.Empty, error) {
207 if len(req.Der) == 0 || req.RegID == 0 || req.IssuedNS == 0 || req.IssuerNameID == 0 {
208 return nil, errIncompleteRequest
209 }
210 parsed, err := x509.ParseCertificate(req.Der)
211 if err != nil {
212 return nil, err
213 }
214 serialHex := core.SerialToString(parsed.SerialNumber)
215
216 preCertModel := &precertificateModel{
217 Serial: serialHex,
218 RegistrationID: req.RegID,
219 DER: req.Der,
220 Issued: time.Unix(0, req.IssuedNS),
221 Expires: parsed.NotAfter,
222 }
223
224 _, overallError := db.WithTransaction(ctx, ssa.dbMap, func(tx db.Executor) (interface{}, error) {
225
226 var row struct {
227 Count int64
228 }
229 err := tx.SelectOne(ctx, &row, "SELECT COUNT(*) as count FROM precertificates WHERE serial=?", serialHex)
230 if err != nil {
231 return nil, err
232 }
233 if row.Count > 0 {
234 return nil, berrors.DuplicateError("cannot add a duplicate cert")
235 }
236
237 err = tx.Insert(ctx, preCertModel)
238 if err != nil {
239 return nil, err
240 }
241
242 status := core.OCSPStatusGood
243 if req.OcspNotReady {
244 status = core.OCSPStatusNotReady
245 }
246 cs := &core.CertificateStatus{
247 Serial: serialHex,
248 Status: status,
249 OCSPLastUpdated: ssa.clk.Now(),
250 RevokedDate: time.Time{},
251 RevokedReason: 0,
252 LastExpirationNagSent: time.Time{},
253 NotAfter: parsed.NotAfter,
254 IsExpired: false,
255 IssuerNameID: req.IssuerNameID,
256 }
257 err = ssa.dbMap.Insert(ctx, cs)
258 if err != nil {
259 return nil, err
260 }
261
262
263
264
265
266
267
268 isRenewal, err := ssa.checkFQDNSetExists(
269 ctx,
270 tx.SelectOne,
271 parsed.DNSNames)
272 if err != nil {
273 return nil, err
274 }
275
276 err = addIssuedNames(ctx, tx, parsed, isRenewal)
277 if err != nil {
278 return nil, err
279 }
280
281 err = addKeyHash(ctx, tx, parsed)
282 if err != nil {
283 return nil, err
284 }
285
286 return nil, nil
287 })
288 if overallError != nil {
289 return nil, overallError
290 }
291
292 return &emptypb.Empty{}, nil
293 }
294
295
296
297 func (ssa *SQLStorageAuthority) AddCertificate(ctx context.Context, req *sapb.AddCertificateRequest) (*emptypb.Empty, error) {
298 if len(req.Der) == 0 || req.RegID == 0 || req.IssuedNS == 0 {
299 return nil, errIncompleteRequest
300 }
301 parsedCertificate, err := x509.ParseCertificate(req.Der)
302 if err != nil {
303 return nil, err
304 }
305 digest := core.Fingerprint256(req.Der)
306 serial := core.SerialToString(parsedCertificate.SerialNumber)
307
308 cert := &core.Certificate{
309 RegistrationID: req.RegID,
310 Serial: serial,
311 Digest: digest,
312 DER: req.Der,
313 Issued: time.Unix(0, req.IssuedNS),
314 Expires: parsedCertificate.NotAfter,
315 }
316
317 isRenewalRaw, overallError := db.WithTransaction(ctx, ssa.dbMap, func(tx db.Executor) (interface{}, error) {
318
319 var row struct {
320 Count int64
321 }
322 err := tx.SelectOne(ctx, &row, "SELECT COUNT(*) as count FROM certificates WHERE serial=?", serial)
323 if err != nil {
324 return nil, err
325 }
326 if row.Count > 0 {
327 return nil, berrors.DuplicateError("cannot add a duplicate cert")
328 }
329
330
331 err = tx.Insert(ctx, cert)
332 if err != nil {
333 return nil, err
334 }
335
336
337
338
339
340
341
342 isRenewal, err := ssa.checkFQDNSetExists(
343 ctx,
344 tx.SelectOne,
345 parsedCertificate.DNSNames)
346 if err != nil {
347 return nil, err
348 }
349
350 return isRenewal, err
351 })
352 if overallError != nil {
353 return nil, overallError
354 }
355
356
357
358 var isRenewal bool
359 if boolVal, ok := isRenewalRaw.(bool); !ok {
360 return nil, fmt.Errorf(
361 "AddCertificate db.WithTransaction returned %T out var, expected bool",
362 isRenewalRaw)
363 } else {
364 isRenewal = boolVal
365 }
366
367
368
369
370
371 _, rlTransactionErr := db.WithTransaction(ctx, ssa.dbMap, func(tx db.Executor) (interface{}, error) {
372
373
374 if !isRenewal {
375 timeToTheHour := parsedCertificate.NotBefore.Round(time.Hour)
376 err := ssa.addCertificatesPerName(ctx, tx, parsedCertificate.DNSNames, timeToTheHour)
377 if err != nil {
378 return nil, err
379 }
380 }
381
382
383
384 err = addFQDNSet(
385 ctx,
386 tx,
387 parsedCertificate.DNSNames,
388 core.SerialToString(parsedCertificate.SerialNumber),
389 parsedCertificate.NotBefore,
390 parsedCertificate.NotAfter,
391 )
392 if err != nil {
393 return nil, err
394 }
395
396 return nil, nil
397 })
398
399
400 if rlTransactionErr != nil {
401 ssa.rateLimitWriteErrors.Inc()
402 ssa.log.AuditErrf("failed AddCertificate ratelimit update transaction: %v", rlTransactionErr)
403 }
404
405 return &emptypb.Empty{}, nil
406 }
407
408
409 func (ssa *SQLStorageAuthority) DeactivateRegistration(ctx context.Context, req *sapb.RegistrationID) (*emptypb.Empty, error) {
410 if req == nil || req.Id == 0 {
411 return nil, errIncompleteRequest
412 }
413 _, err := ssa.dbMap.ExecContext(ctx,
414 "UPDATE registrations SET status = ? WHERE status = ? AND id = ?",
415 string(core.StatusDeactivated),
416 string(core.StatusValid),
417 req.Id,
418 )
419 if err != nil {
420 return nil, err
421 }
422 return &emptypb.Empty{}, nil
423 }
424
425
426 func (ssa *SQLStorageAuthority) DeactivateAuthorization2(ctx context.Context, req *sapb.AuthorizationID2) (*emptypb.Empty, error) {
427 if req.Id == 0 {
428 return nil, errIncompleteRequest
429 }
430
431 _, err := ssa.dbMap.ExecContext(ctx,
432 `UPDATE authz2 SET status = :deactivated WHERE id = :id and status IN (:valid,:pending)`,
433 map[string]interface{}{
434 "deactivated": statusUint(core.StatusDeactivated),
435 "id": req.Id,
436 "valid": statusUint(core.StatusValid),
437 "pending": statusUint(core.StatusPending),
438 },
439 )
440 if err != nil {
441 return nil, err
442 }
443 return &emptypb.Empty{}, nil
444 }
445
446
447
448
449
450
451 func (ssa *SQLStorageAuthority) NewOrderAndAuthzs(ctx context.Context, req *sapb.NewOrderAndAuthzsRequest) (*corepb.Order, error) {
452 if req.NewOrder == nil {
453 return nil, errIncompleteRequest
454 }
455
456 output, err := db.WithTransaction(ctx, ssa.dbMap, func(tx db.Executor) (interface{}, error) {
457
458 newAuthzIDs := make([]int64, 0)
459 if len(req.NewAuthzs) != 0 {
460 inserter, err := db.NewMultiInserter("authz2", strings.Split(authzFields, ", "), "id")
461 if err != nil {
462 return nil, err
463 }
464 for _, authz := range req.NewAuthzs {
465 if authz.Status != string(core.StatusPending) {
466 return nil, berrors.InternalServerError("authorization must be pending")
467 }
468 am, err := authzPBToModel(authz)
469 if err != nil {
470 return nil, err
471 }
472 err = inserter.Add([]interface{}{
473 am.ID,
474 am.IdentifierType,
475 am.IdentifierValue,
476 am.RegistrationID,
477 statusToUint[core.StatusPending],
478 am.Expires,
479 am.Challenges,
480 nil,
481 nil,
482 am.Token,
483 nil,
484 nil,
485 })
486 if err != nil {
487 return nil, err
488 }
489 }
490 newAuthzIDs, err = inserter.Insert(ctx, tx)
491 if err != nil {
492 return nil, err
493 }
494 }
495
496
497 order := &orderModel{
498 RegistrationID: req.NewOrder.RegistrationID,
499 Expires: time.Unix(0, req.NewOrder.ExpiresNS),
500 Created: ssa.clk.Now(),
501 }
502 err := tx.Insert(ctx, order)
503 if err != nil {
504 return nil, err
505 }
506
507
508 inserter, err := db.NewMultiInserter("orderToAuthz2", []string{"orderID", "authzID"}, "")
509 if err != nil {
510 return nil, err
511 }
512 for _, id := range req.NewOrder.V2Authorizations {
513 err = inserter.Add([]interface{}{order.ID, id})
514 if err != nil {
515 return nil, err
516 }
517 }
518 for _, id := range newAuthzIDs {
519 err = inserter.Add([]interface{}{order.ID, id})
520 if err != nil {
521 return nil, err
522 }
523 }
524 _, err = inserter.Insert(ctx, tx)
525 if err != nil {
526 return nil, err
527 }
528
529
530 inserter, err = db.NewMultiInserter("requestedNames", []string{"orderID", "reversedName"}, "")
531 if err != nil {
532 return nil, err
533 }
534 for _, name := range req.NewOrder.Names {
535 err = inserter.Add([]interface{}{order.ID, ReverseName(name)})
536 if err != nil {
537 return nil, err
538 }
539 }
540 _, err = inserter.Insert(ctx, tx)
541 if err != nil {
542 return nil, err
543 }
544
545
546 err = addOrderFQDNSet(ctx, tx, req.NewOrder.Names, order.ID, order.RegistrationID, order.Expires)
547 if err != nil {
548 return nil, err
549 }
550
551
552 res := &corepb.Order{
553
554 Id: order.ID,
555 CreatedNS: order.Created.UnixNano(),
556 Created: timestamppb.New(order.Created),
557
558 RegistrationID: req.NewOrder.RegistrationID,
559 ExpiresNS: req.NewOrder.ExpiresNS,
560 Expires: timestamppb.New(time.Unix(0, req.NewOrder.ExpiresNS)),
561 Names: req.NewOrder.Names,
562
563 V2Authorizations: append(req.NewOrder.V2Authorizations, newAuthzIDs...),
564
565 BeganProcessing: false,
566 }
567
568
569
570 status, err := statusForOrder(ctx, tx, res, ssa.clk.Now())
571 if err != nil {
572 return nil, err
573 }
574 res.Status = status
575
576 return res, nil
577 })
578 if err != nil {
579 return nil, err
580 }
581
582 order, ok := output.(*corepb.Order)
583 if !ok {
584 return nil, fmt.Errorf("casting error in NewOrderAndAuthzs")
585 }
586
587
588 err = addNewOrdersRateLimit(ctx, ssa.dbMap, req.NewOrder.RegistrationID, ssa.clk.Now().Truncate(time.Minute))
589 if err != nil {
590 return nil, err
591 }
592
593 return order, nil
594 }
595
596
597
598
599 func (ssa *SQLStorageAuthority) SetOrderProcessing(ctx context.Context, req *sapb.OrderRequest) (*emptypb.Empty, error) {
600 if req.Id == 0 {
601 return nil, errIncompleteRequest
602 }
603 _, overallError := db.WithTransaction(ctx, ssa.dbMap, func(tx db.Executor) (interface{}, error) {
604 result, err := tx.ExecContext(ctx, `
605 UPDATE orders
606 SET beganProcessing = ?
607 WHERE id = ?
608 AND beganProcessing = ?`,
609 true,
610 req.Id,
611 false)
612 if err != nil {
613 return nil, berrors.InternalServerError("error updating order to beganProcessing status")
614 }
615
616 n, err := result.RowsAffected()
617 if err != nil || n == 0 {
618 return nil, berrors.OrderNotReadyError("Order was already processing. This may indicate your client finalized the same order multiple times, possibly due to a client bug.")
619 }
620
621 return nil, nil
622 })
623 if overallError != nil {
624 return nil, overallError
625 }
626 return &emptypb.Empty{}, nil
627 }
628
629
630 func (ssa *SQLStorageAuthority) SetOrderError(ctx context.Context, req *sapb.SetOrderErrorRequest) (*emptypb.Empty, error) {
631 if req.Id == 0 || req.Error == nil {
632 return nil, errIncompleteRequest
633 }
634 _, overallError := db.WithTransaction(ctx, ssa.dbMap, func(tx db.Executor) (interface{}, error) {
635 om, err := orderToModel(&corepb.Order{
636 Id: req.Id,
637 Error: req.Error,
638 })
639 if err != nil {
640 return nil, err
641 }
642
643 result, err := tx.ExecContext(ctx, `
644 UPDATE orders
645 SET error = ?
646 WHERE id = ?`,
647 om.Error,
648 om.ID)
649 if err != nil {
650 return nil, berrors.InternalServerError("error updating order error field")
651 }
652
653 n, err := result.RowsAffected()
654 if err != nil || n == 0 {
655 return nil, berrors.InternalServerError("no order updated with new error field")
656 }
657
658 return nil, nil
659 })
660 if overallError != nil {
661 return nil, overallError
662 }
663 return &emptypb.Empty{}, nil
664 }
665
666
667
668
669
670 func (ssa *SQLStorageAuthority) FinalizeOrder(ctx context.Context, req *sapb.FinalizeOrderRequest) (*emptypb.Empty, error) {
671 if req.Id == 0 || req.CertificateSerial == "" {
672 return nil, errIncompleteRequest
673 }
674 _, overallError := db.WithTransaction(ctx, ssa.dbMap, func(tx db.Executor) (interface{}, error) {
675 result, err := tx.ExecContext(ctx, `
676 UPDATE orders
677 SET certificateSerial = ?
678 WHERE id = ? AND
679 beganProcessing = true`,
680 req.CertificateSerial,
681 req.Id)
682 if err != nil {
683 return nil, berrors.InternalServerError("error updating order for finalization")
684 }
685
686 n, err := result.RowsAffected()
687 if err != nil || n == 0 {
688 return nil, berrors.InternalServerError("no order updated for finalization")
689 }
690
691
692
693 err = deleteOrderFQDNSet(ctx, tx, req.Id)
694 if err != nil {
695 return nil, err
696 }
697
698 return nil, nil
699 })
700 if overallError != nil {
701 return nil, overallError
702 }
703 return &emptypb.Empty{}, nil
704 }
705
706
707
708
709 func (ssa *SQLStorageAuthority) FinalizeAuthorization2(ctx context.Context, req *sapb.FinalizeAuthorizationRequest) (*emptypb.Empty, error) {
710 if req.Status == "" || req.Attempted == "" || req.ExpiresNS == 0 || req.Id == 0 {
711 return nil, errIncompleteRequest
712 }
713
714 if req.Status != string(core.StatusValid) && req.Status != string(core.StatusInvalid) {
715 return nil, berrors.InternalServerError("authorization must have status valid or invalid")
716 }
717 query := `UPDATE authz2 SET
718 status = :status,
719 attempted = :attempted,
720 attemptedAt = :attemptedAt,
721 validationRecord = :validationRecord,
722 validationError = :validationError,
723 expires = :expires
724 WHERE id = :id AND status = :pending`
725 var validationRecords []core.ValidationRecord
726 for _, recordPB := range req.ValidationRecords {
727 record, err := bgrpc.PBToValidationRecord(recordPB)
728 if err != nil {
729 return nil, err
730 }
731 if req.Attempted == string(core.ChallengeTypeHTTP01) {
732
733
734 record.Hostname = ""
735 record.Port = ""
736 }
737 validationRecords = append(validationRecords, record)
738 }
739 vrJSON, err := json.Marshal(validationRecords)
740 if err != nil {
741 return nil, err
742 }
743 var veJSON []byte
744 if req.ValidationError != nil {
745 validationError, err := bgrpc.PBToProblemDetails(req.ValidationError)
746 if err != nil {
747 return nil, err
748 }
749 j, err := json.Marshal(validationError)
750 if err != nil {
751 return nil, err
752 }
753 veJSON = j
754 }
755
756
757
758
759 var attemptedTime *time.Time
760 if req.AttemptedAtNS != 0 {
761 val := time.Unix(0, req.AttemptedAtNS).UTC()
762 attemptedTime = &val
763 }
764 params := map[string]interface{}{
765 "status": statusToUint[core.AcmeStatus(req.Status)],
766 "attempted": challTypeToUint[req.Attempted],
767 "attemptedAt": attemptedTime,
768 "validationRecord": vrJSON,
769 "id": req.Id,
770 "pending": statusUint(core.StatusPending),
771 "expires": time.Unix(0, req.ExpiresNS).UTC(),
772
773
774 "validationError": veJSON,
775 }
776
777 res, err := ssa.dbMap.ExecContext(ctx, query, params)
778 if err != nil {
779 return nil, err
780 }
781 rows, err := res.RowsAffected()
782 if err != nil {
783 return nil, err
784 }
785 if rows == 0 {
786 return nil, berrors.NotFoundError("authorization with id %d not found", req.Id)
787 } else if rows > 1 {
788 return nil, berrors.InternalServerError("multiple rows updated for authorization id %d", req.Id)
789 }
790 return &emptypb.Empty{}, nil
791 }
792
793
794
795
796
797
798
799 func addRevokedCertificate(ctx context.Context, tx db.Executor, req *sapb.RevokeCertificateRequest, revokedDate time.Time) error {
800 if req.ShardIdx == 0 {
801 return errors.New("cannot add revoked certificate with shard index 0")
802 }
803
804 var serial struct {
805 Expires time.Time
806 }
807 err := tx.SelectOne(
808 ctx, &serial, `SELECT expires FROM serials WHERE serial = ?`, req.Serial)
809 if err != nil {
810 return fmt.Errorf("retrieving revoked certificate expiration: %w", err)
811 }
812
813 err = tx.Insert(ctx, &revokedCertModel{
814 IssuerID: req.IssuerID,
815 Serial: req.Serial,
816 ShardIdx: req.ShardIdx,
817 RevokedDate: revokedDate,
818 RevokedReason: revocation.Reason(req.Reason),
819
820
821 NotAfterHour: serial.Expires.Add(time.Hour).Truncate(time.Hour),
822 })
823 if err != nil {
824 return fmt.Errorf("inserting revoked certificate row: %w", err)
825 }
826
827 return nil
828 }
829
830
831
832 func (ssa *SQLStorageAuthority) RevokeCertificate(ctx context.Context, req *sapb.RevokeCertificateRequest) (*emptypb.Empty, error) {
833 if req.Serial == "" || req.DateNS == 0 || req.IssuerID == 0 {
834 return nil, errIncompleteRequest
835 }
836
837 _, overallError := db.WithTransaction(ctx, ssa.dbMap, func(tx db.Executor) (interface{}, error) {
838 revokedDate := time.Unix(0, req.DateNS)
839
840 res, err := tx.ExecContext(ctx,
841 `UPDATE certificateStatus SET
842 status = ?,
843 revokedReason = ?,
844 revokedDate = ?,
845 ocspLastUpdated = ?
846 WHERE serial = ? AND status != ?`,
847 string(core.OCSPStatusRevoked),
848 revocation.Reason(req.Reason),
849 revokedDate,
850 revokedDate,
851 req.Serial,
852 string(core.OCSPStatusRevoked),
853 )
854 if err != nil {
855 return nil, err
856 }
857 rows, err := res.RowsAffected()
858 if err != nil {
859 return nil, err
860 }
861 if rows == 0 {
862 return nil, berrors.AlreadyRevokedError("no certificate with serial %s and status other than %s", req.Serial, string(core.OCSPStatusRevoked))
863 }
864
865 if req.ShardIdx != 0 {
866 err = addRevokedCertificate(ctx, tx, req, revokedDate)
867 if err != nil {
868 return nil, err
869 }
870 }
871
872 return nil, nil
873 })
874 if overallError != nil {
875 return nil, overallError
876 }
877
878 return &emptypb.Empty{}, nil
879 }
880
881
882
883
884
885 func (ssa *SQLStorageAuthority) UpdateRevokedCertificate(ctx context.Context, req *sapb.RevokeCertificateRequest) (*emptypb.Empty, error) {
886 if req.Serial == "" || req.DateNS == 0 || req.BackdateNS == 0 || req.IssuerID == 0 {
887 return nil, errIncompleteRequest
888 }
889 if req.Reason != ocsp.KeyCompromise {
890 return nil, fmt.Errorf("cannot update revocation for any reason other than keyCompromise (1); got: %d", req.Reason)
891 }
892
893 _, overallError := db.WithTransaction(ctx, ssa.dbMap, func(tx db.Executor) (interface{}, error) {
894 thisUpdate := time.Unix(0, req.DateNS)
895 revokedDate := time.Unix(0, req.BackdateNS)
896
897 res, err := tx.ExecContext(ctx,
898 `UPDATE certificateStatus SET
899 revokedReason = ?,
900 ocspLastUpdated = ?
901 WHERE serial = ? AND status = ? AND revokedReason != ? AND revokedDate = ?`,
902 revocation.Reason(ocsp.KeyCompromise),
903 thisUpdate,
904 req.Serial,
905 string(core.OCSPStatusRevoked),
906 revocation.Reason(ocsp.KeyCompromise),
907 revokedDate,
908 )
909 if err != nil {
910 return nil, err
911 }
912 rows, err := res.RowsAffected()
913 if err != nil {
914 return nil, err
915 }
916 if rows == 0 {
917
918
919 return nil, berrors.InternalServerError("no certificate with serial %s and revoked reason other than keyCompromise", req.Serial)
920 }
921
922
923
924
925 if req.ShardIdx != 0 {
926 var rcm revokedCertModel
927
928
929
930
931
932 err = tx.SelectOne(
933 ctx, &rcm, `SELECT * FROM revokedCertificates WHERE serial = ?`, req.Serial)
934 if db.IsNoRows(err) {
935
936
937
938
939
940 err = addRevokedCertificate(ctx, tx, req, revokedDate)
941 if err != nil {
942 return nil, err
943 }
944 return nil, nil
945 } else if err != nil {
946 return nil, fmt.Errorf("retrieving revoked certificate row: %w", err)
947 }
948
949 rcm.RevokedReason = revocation.Reason(ocsp.KeyCompromise)
950 _, err = tx.Update(ctx, &rcm)
951 if err != nil {
952 return nil, fmt.Errorf("updating revoked certificate row: %w", err)
953 }
954 }
955
956 return nil, nil
957 })
958 if overallError != nil {
959 return nil, overallError
960 }
961
962 return &emptypb.Empty{}, nil
963 }
964
965
966 func (ssa *SQLStorageAuthority) AddBlockedKey(ctx context.Context, req *sapb.AddBlockedKeyRequest) (*emptypb.Empty, error) {
967 if core.IsAnyNilOrZero(req.KeyHash, req.AddedNS, req.Source) {
968 return nil, errIncompleteRequest
969 }
970 sourceInt, ok := stringToSourceInt[req.Source]
971 if !ok {
972 return nil, errors.New("unknown source")
973 }
974 cols, qs := blockedKeysColumns, "?, ?, ?, ?"
975 vals := []interface{}{
976 req.KeyHash,
977 time.Unix(0, req.AddedNS),
978 sourceInt,
979 req.Comment,
980 }
981 if req.RevokedBy != 0 {
982 cols += ", revokedBy"
983 qs += ", ?"
984 vals = append(vals, req.RevokedBy)
985 }
986 _, err := ssa.dbMap.ExecContext(ctx,
987 fmt.Sprintf("INSERT INTO blockedKeys (%s) VALUES (%s)", cols, qs),
988 vals...,
989 )
990 if err != nil {
991 if db.IsDuplicate(err) {
992
993
994 return &emptypb.Empty{}, nil
995 }
996 return nil, err
997 }
998 return &emptypb.Empty{}, nil
999 }
1000
1001
1002 func (ssa *SQLStorageAuthority) Health(ctx context.Context) error {
1003 err := ssa.dbMap.SelectOne(ctx, new(int), "SELECT 1")
1004 if err != nil {
1005 return err
1006 }
1007
1008 err = ssa.SQLStorageAuthorityRO.Health(ctx)
1009 if err != nil {
1010 return err
1011 }
1012 return nil
1013 }
1014
1015
1016
1017
1018
1019 func (ssa *SQLStorageAuthority) LeaseCRLShard(ctx context.Context, req *sapb.LeaseCRLShardRequest) (*sapb.LeaseCRLShardResponse, error) {
1020 if core.IsAnyNilOrZero(req.Until, req.IssuerNameID) {
1021 return nil, errIncompleteRequest
1022 }
1023 if req.Until.AsTime().Before(ssa.clk.Now()) {
1024 return nil, fmt.Errorf("lease timestamp must be in the future, got %q", req.Until.AsTime())
1025 }
1026
1027 if req.MinShardIdx == req.MaxShardIdx {
1028 return ssa.leaseSpecificCRLShard(ctx, req)
1029 }
1030
1031 return ssa.leaseOldestCRLShard(ctx, req)
1032 }
1033
1034
1035
1036
1037
1038 func (ssa *SQLStorageAuthority) leaseOldestCRLShard(ctx context.Context, req *sapb.LeaseCRLShardRequest) (*sapb.LeaseCRLShardResponse, error) {
1039 shardIdx, err := db.WithTransaction(ctx, ssa.dbMap, func(tx db.Executor) (interface{}, error) {
1040 var shards []*crlShardModel
1041 _, err := tx.Select(
1042 ctx,
1043 &shards,
1044 `SELECT id, issuerID, idx, thisUpdate, nextUpdate, leasedUntil
1045 FROM crlShards
1046 WHERE issuerID = ?
1047 AND idx BETWEEN ? AND ?`,
1048 req.IssuerNameID, req.MinShardIdx, req.MaxShardIdx,
1049 )
1050 if err != nil {
1051 return -1, fmt.Errorf("selecting candidate shards: %w", err)
1052 }
1053
1054
1055 var shardIdx int
1056 var needToInsert bool
1057 if len(shards) < (int(req.MaxShardIdx + 1 - req.MinShardIdx)) {
1058
1059
1060 missing := make(map[int]struct{}, req.MaxShardIdx+1-req.MinShardIdx)
1061 for i := req.MinShardIdx; i <= req.MaxShardIdx; i++ {
1062 missing[int(i)] = struct{}{}
1063 }
1064 for _, shard := range shards {
1065 delete(missing, shard.Idx)
1066 }
1067 for idx := range missing {
1068
1069 shardIdx = idx
1070 break
1071 }
1072 needToInsert = true
1073 } else {
1074
1075 var oldest *crlShardModel
1076 for _, shard := range shards {
1077 if shard.LeasedUntil.After(ssa.clk.Now()) {
1078 continue
1079 }
1080 if oldest == nil ||
1081 (oldest.ThisUpdate != nil && shard.ThisUpdate == nil) ||
1082 (oldest.ThisUpdate != nil && shard.ThisUpdate.Before(*oldest.ThisUpdate)) {
1083 oldest = shard
1084 }
1085 }
1086 if oldest == nil {
1087 return -1, fmt.Errorf("issuer %d has no unleased shards in range %d-%d", req.IssuerNameID, req.MinShardIdx, req.MaxShardIdx)
1088 }
1089 shardIdx = oldest.Idx
1090 needToInsert = false
1091 }
1092
1093 if needToInsert {
1094 _, err = tx.ExecContext(ctx,
1095 `INSERT INTO crlShards (issuerID, idx, leasedUntil)
1096 VALUES (?, ?, ?)`,
1097 req.IssuerNameID,
1098 shardIdx,
1099 req.Until.AsTime(),
1100 )
1101 if err != nil {
1102 return -1, fmt.Errorf("inserting selected shard: %w", err)
1103 }
1104 } else {
1105 _, err = tx.ExecContext(ctx,
1106 `UPDATE crlShards
1107 SET leasedUntil = ?
1108 WHERE issuerID = ?
1109 AND idx = ?
1110 LIMIT 1`,
1111 req.Until.AsTime(),
1112 req.IssuerNameID,
1113 shardIdx,
1114 )
1115 if err != nil {
1116 return -1, fmt.Errorf("updating selected shard: %w", err)
1117 }
1118 }
1119
1120 return shardIdx, err
1121 })
1122 if err != nil {
1123 return nil, fmt.Errorf("leasing oldest shard: %w", err)
1124 }
1125
1126 return &sapb.LeaseCRLShardResponse{
1127 IssuerNameID: req.IssuerNameID,
1128 ShardIdx: int64(shardIdx.(int)),
1129 }, nil
1130 }
1131
1132
1133
1134
1135 func (ssa *SQLStorageAuthority) leaseSpecificCRLShard(ctx context.Context, req *sapb.LeaseCRLShardRequest) (*sapb.LeaseCRLShardResponse, error) {
1136 if req.MinShardIdx != req.MaxShardIdx {
1137 return nil, fmt.Errorf("request must identify a single shard index: %d != %d", req.MinShardIdx, req.MaxShardIdx)
1138 }
1139
1140 _, err := db.WithTransaction(ctx, ssa.dbMap, func(tx db.Executor) (interface{}, error) {
1141 needToInsert := false
1142 var shardModel crlShardModel
1143 err := tx.SelectOne(ctx,
1144 &shardModel,
1145 `SELECT leasedUntil
1146 FROM crlShards
1147 WHERE issuerID = ?
1148 AND idx = ?
1149 LIMIT 1`,
1150 req.IssuerNameID,
1151 req.MinShardIdx,
1152 )
1153 if db.IsNoRows(err) {
1154 needToInsert = true
1155 } else if err != nil {
1156 return nil, fmt.Errorf("selecting requested shard: %w", err)
1157 } else if shardModel.LeasedUntil.After(ssa.clk.Now()) {
1158 return nil, fmt.Errorf("shard %d for issuer %d already leased", req.MinShardIdx, req.IssuerNameID)
1159 }
1160
1161 if needToInsert {
1162 _, err = tx.ExecContext(ctx,
1163 `INSERT INTO crlShards (issuerID, idx, leasedUntil)
1164 VALUES (?, ?, ?)`,
1165 req.IssuerNameID,
1166 req.MinShardIdx,
1167 req.Until.AsTime(),
1168 )
1169 if err != nil {
1170 return nil, fmt.Errorf("inserting selected shard: %w", err)
1171 }
1172 } else {
1173 _, err = tx.ExecContext(ctx,
1174 `UPDATE crlShards
1175 SET leasedUntil = ?
1176 WHERE issuerID = ?
1177 AND idx = ?
1178 LIMIT 1`,
1179 req.Until.AsTime(),
1180 req.IssuerNameID,
1181 req.MinShardIdx,
1182 )
1183 if err != nil {
1184 return nil, fmt.Errorf("updating selected shard: %w", err)
1185 }
1186 }
1187
1188 return nil, nil
1189 })
1190 if err != nil {
1191 return nil, fmt.Errorf("leasing specific shard: %w", err)
1192 }
1193
1194 return &sapb.LeaseCRLShardResponse{
1195 IssuerNameID: req.IssuerNameID,
1196 ShardIdx: req.MinShardIdx,
1197 }, nil
1198 }
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209 func (ssa *SQLStorageAuthority) UpdateCRLShard(ctx context.Context, req *sapb.UpdateCRLShardRequest) (*emptypb.Empty, error) {
1210 if core.IsAnyNilOrZero(req.IssuerNameID, req.ThisUpdate) {
1211 return nil, errIncompleteRequest
1212 }
1213
1214
1215 var nextUpdate *time.Time
1216 if req.NextUpdate != nil {
1217 nut := req.NextUpdate.AsTime()
1218 nextUpdate = &nut
1219 }
1220
1221 _, err := db.WithTransaction(ctx, ssa.dbMap, func(tx db.Executor) (interface{}, error) {
1222 res, err := tx.ExecContext(ctx,
1223 `UPDATE crlShards
1224 SET thisUpdate = ?, nextUpdate = ?, leasedUntil = ?
1225 WHERE issuerID = ?
1226 AND idx = ?
1227 AND (thisUpdate is NULL OR thisUpdate < ?)
1228 LIMIT 1`,
1229 req.ThisUpdate.AsTime(),
1230 nextUpdate,
1231 req.ThisUpdate.AsTime(),
1232 req.IssuerNameID,
1233 req.ShardIdx,
1234 req.ThisUpdate.AsTime(),
1235 )
1236 if err != nil {
1237 return nil, err
1238 }
1239
1240 rowsAffected, err := res.RowsAffected()
1241 if err != nil {
1242 return nil, err
1243 }
1244 if rowsAffected == 0 {
1245 return nil, fmt.Errorf("unable to update shard %d for issuer %d", req.ShardIdx, req.IssuerNameID)
1246 }
1247 if rowsAffected != 1 {
1248 return nil, errors.New("update affected unexpected number of rows")
1249 }
1250 return nil, nil
1251 })
1252 if err != nil {
1253 return nil, err
1254 }
1255
1256 return &emptypb.Empty{}, nil
1257 }
1258
View as plain text