1 package sa
2
3 import (
4 "context"
5 "crypto/sha256"
6 "crypto/x509"
7 "encoding/base64"
8 "encoding/json"
9 "errors"
10 "fmt"
11 "math"
12 "net"
13 "net/url"
14 "slices"
15 "strconv"
16 "time"
17
18 "google.golang.org/protobuf/types/known/timestamppb"
19 "gopkg.in/go-jose/go-jose.v2"
20
21 "github.com/letsencrypt/boulder/core"
22 corepb "github.com/letsencrypt/boulder/core/proto"
23 "github.com/letsencrypt/boulder/db"
24 berrors "github.com/letsencrypt/boulder/errors"
25 "github.com/letsencrypt/boulder/grpc"
26 "github.com/letsencrypt/boulder/identifier"
27 "github.com/letsencrypt/boulder/probs"
28 "github.com/letsencrypt/boulder/revocation"
29 sapb "github.com/letsencrypt/boulder/sa/proto"
30 )
31
32
33
34
35 type errBadJSON struct {
36 msg string
37 json []byte
38 err error
39 }
40
41
42
43 func (e errBadJSON) Error() string {
44 return fmt.Sprintf(
45 "%s: error unmarshaling JSON %q: %s",
46 e.msg,
47 string(e.json),
48 e.err)
49 }
50
51
52
53 func badJSONError(msg string, jsonData []byte, err error) error {
54 return errBadJSON{
55 msg: msg,
56 json: jsonData,
57 err: err,
58 }
59 }
60
61 const regFields = "id, jwk, jwk_sha256, contact, agreement, initialIP, createdAt, LockCol, status"
62
63
64
65
66 func ClearEmail(ctx context.Context, dbMap db.DatabaseMap, regID int64, email string) error {
67 _, overallError := db.WithTransaction(ctx, dbMap, func(tx db.Executor) (interface{}, error) {
68 curr, err := selectRegistration(ctx, tx, "id", regID)
69 if err != nil {
70 return nil, err
71 }
72
73 currPb, err := registrationModelToPb(curr)
74 if err != nil {
75 return nil, err
76 }
77
78
79 var newContacts []string
80 for _, contact := range currPb.Contact {
81 if contact != "mailto:"+email {
82 newContacts = append(newContacts, contact)
83 }
84 }
85
86 if slices.Equal(currPb.Contact, newContacts) {
87 return nil, nil
88 }
89
90 currPb.Contact = newContacts
91 newModel, err := registrationPbToModel(currPb)
92 if err != nil {
93 return nil, err
94 }
95
96 return tx.Update(ctx, newModel)
97 })
98 if overallError != nil {
99 return overallError
100 }
101
102 return nil
103 }
104
105
106 func selectRegistration(ctx context.Context, s db.OneSelector, whereCol string, args ...interface{}) (*regModel, error) {
107 if whereCol != "id" && whereCol != "jwk_sha256" {
108 return nil, fmt.Errorf("column name %q invalid for registrations table WHERE clause", whereCol)
109 }
110
111 var model regModel
112 err := s.SelectOne(
113 ctx,
114 &model,
115 "SELECT "+regFields+" FROM registrations WHERE "+whereCol+" = ? LIMIT 1",
116 args...,
117 )
118 return &model, err
119 }
120
121 const certFields = "registrationID, serial, digest, der, issued, expires"
122
123
124
125
126 func SelectCertificate(ctx context.Context, s db.OneSelector, serial string) (core.Certificate, error) {
127 var model core.Certificate
128 err := s.SelectOne(
129 ctx,
130 &model,
131 "SELECT "+certFields+" FROM certificates WHERE serial = ? LIMIT 1",
132 serial,
133 )
134 return model, err
135 }
136
137 const precertFields = "registrationID, serial, der, issued, expires"
138
139
140
141 func SelectPrecertificate(ctx context.Context, s db.OneSelector, serial string) (core.Certificate, error) {
142 var model precertificateModel
143 err := s.SelectOne(
144 ctx,
145 &model,
146 "SELECT "+precertFields+" FROM precertificates WHERE serial = ? LIMIT 1",
147 serial)
148 return core.Certificate{
149 RegistrationID: model.RegistrationID,
150 Serial: model.Serial,
151 DER: model.DER,
152 Issued: model.Issued,
153 Expires: model.Expires,
154 }, err
155 }
156
157 type CertWithID struct {
158 ID int64
159 core.Certificate
160 }
161
162
163 func SelectCertificates(ctx context.Context, s db.Selector, q string, args map[string]interface{}) ([]CertWithID, error) {
164 var models []CertWithID
165 _, err := s.Select(
166 ctx,
167 &models,
168 "SELECT id, "+certFields+" FROM certificates "+q, args)
169 return models, err
170 }
171
172
173 func SelectPrecertificates(ctx context.Context, s db.Selector, q string, args map[string]interface{}) ([]CertWithID, error) {
174 var models []CertWithID
175 _, err := s.Select(
176 ctx,
177 &models,
178 "SELECT id, "+precertFields+" FROM precertificates "+q, args)
179 return models, err
180 }
181
182 type CertStatusMetadata struct {
183 ID int64 `db:"id"`
184 Serial string `db:"serial"`
185 Status core.OCSPStatus `db:"status"`
186 OCSPLastUpdated time.Time `db:"ocspLastUpdated"`
187 RevokedDate time.Time `db:"revokedDate"`
188 RevokedReason revocation.Reason `db:"revokedReason"`
189 LastExpirationNagSent time.Time `db:"lastExpirationNagSent"`
190 NotAfter time.Time `db:"notAfter"`
191 IsExpired bool `db:"isExpired"`
192 IssuerID int64 `db:"issuerID"`
193 }
194
195 const certStatusFields = "id, serial, status, ocspLastUpdated, revokedDate, revokedReason, lastExpirationNagSent, notAfter, isExpired, issuerID"
196
197
198
199 func SelectCertificateStatus(ctx context.Context, s db.OneSelector, serial string) (core.CertificateStatus, error) {
200 var model core.CertificateStatus
201 err := s.SelectOne(
202 ctx,
203 &model,
204 "SELECT "+certStatusFields+" FROM certificateStatus WHERE serial = ? LIMIT 1",
205 serial,
206 )
207 return model, err
208 }
209
210
211
212
213 type RevocationStatusModel struct {
214 Status core.OCSPStatus `db:"status"`
215 RevokedDate time.Time `db:"revokedDate"`
216 RevokedReason revocation.Reason `db:"revokedReason"`
217 }
218
219
220
221 func SelectRevocationStatus(ctx context.Context, s db.OneSelector, serial string) (*sapb.RevocationStatus, error) {
222 var model RevocationStatusModel
223 err := s.SelectOne(
224 ctx,
225 &model,
226 "SELECT status, revokedDate, revokedReason FROM certificateStatus WHERE serial = ? LIMIT 1",
227 serial,
228 )
229 if err != nil {
230 return nil, err
231 }
232
233 statusInt, ok := core.OCSPStatusToInt[model.Status]
234 if !ok {
235 return nil, fmt.Errorf("got unrecognized status %q", model.Status)
236 }
237
238 return &sapb.RevocationStatus{
239 Status: int64(statusInt),
240 RevokedDate: timestamppb.New(model.RevokedDate),
241 RevokedReason: int64(model.RevokedReason),
242 }, nil
243 }
244
245 var mediumBlobSize = int(math.Pow(2, 24))
246
247 type issuedNameModel struct {
248 ID int64 `db:"id"`
249 ReversedName string `db:"reversedName"`
250 NotBefore time.Time `db:"notBefore"`
251 Serial string `db:"serial"`
252 }
253
254
255 type regModel struct {
256 ID int64 `db:"id"`
257 Key []byte `db:"jwk"`
258 KeySHA256 string `db:"jwk_sha256"`
259 Contact string `db:"contact"`
260 Agreement string `db:"agreement"`
261
262
263 InitialIP []byte `db:"initialIp"`
264 CreatedAt time.Time `db:"createdAt"`
265 LockCol int64
266 Status string `db:"status"`
267 }
268
269 func registrationPbToModel(reg *corepb.Registration) (*regModel, error) {
270
271
272
273 var jwk jose.JSONWebKey
274 err := jwk.UnmarshalJSON(reg.Key)
275 if err != nil {
276 return nil, err
277 }
278 sha, err := core.KeyDigestB64(jwk.Key)
279 if err != nil {
280 return nil, err
281 }
282
283
284
285
286
287 jsonContact := []byte("[]")
288 if len(reg.Contact) != 0 {
289 jsonContact, err = json.Marshal(reg.Contact)
290 if err != nil {
291 return nil, err
292 }
293 }
294
295
296
297
298 var initialIP net.IP
299 err = initialIP.UnmarshalText(reg.InitialIP)
300 if err != nil {
301 return nil, err
302 }
303
304
305
306
307 var createdAt time.Time
308 if reg.CreatedAtNS != 0 {
309 createdAt = time.Unix(0, reg.CreatedAtNS)
310 }
311
312 return ®Model{
313 ID: reg.Id,
314 Key: reg.Key,
315 KeySHA256: sha,
316 Contact: string(jsonContact),
317 Agreement: reg.Agreement,
318 InitialIP: []byte(initialIP.To16()),
319 CreatedAt: createdAt,
320 Status: reg.Status,
321 }, nil
322 }
323
324 func registrationModelToPb(reg *regModel) (*corepb.Registration, error) {
325 if reg.ID == 0 || len(reg.Key) == 0 || len(reg.InitialIP) == 0 {
326 return nil, errors.New("incomplete Registration retrieved from DB")
327 }
328
329 contact := []string{}
330 contactsPresent := false
331 if len(reg.Contact) > 0 {
332 err := json.Unmarshal([]byte(reg.Contact), &contact)
333 if err != nil {
334 return nil, err
335 }
336 if len(contact) > 0 {
337 contactsPresent = true
338 }
339 }
340
341
342
343
344 ipBytes, err := net.IP(reg.InitialIP).MarshalText()
345 if err != nil {
346 return nil, err
347 }
348
349 return &corepb.Registration{
350 Id: reg.ID,
351 Key: reg.Key,
352 Contact: contact,
353 ContactsPresent: contactsPresent,
354 Agreement: reg.Agreement,
355 InitialIP: ipBytes,
356 CreatedAtNS: reg.CreatedAt.UTC().UnixNano(),
357 CreatedAt: timestamppb.New(reg.CreatedAt.UTC()),
358 Status: reg.Status,
359 }, nil
360 }
361
362 type recordedSerialModel struct {
363 ID int64
364 Serial string
365 RegistrationID int64
366 Created time.Time
367 Expires time.Time
368 }
369
370 type precertificateModel struct {
371 ID int64
372 Serial string
373 RegistrationID int64
374 DER []byte
375 Issued time.Time
376 Expires time.Time
377 }
378
379 type orderModel struct {
380 ID int64
381 RegistrationID int64
382 Expires time.Time
383 Created time.Time
384 Error []byte
385 CertificateSerial string
386 BeganProcessing bool
387 }
388
389 type requestedNameModel struct {
390 ID int64
391 OrderID int64
392 ReversedName string
393 }
394
395 type orderToAuthzModel struct {
396 OrderID int64
397 AuthzID int64
398 }
399
400 func orderToModel(order *corepb.Order) (*orderModel, error) {
401 om := &orderModel{
402 ID: order.Id,
403 RegistrationID: order.RegistrationID,
404 Expires: time.Unix(0, order.ExpiresNS),
405 Created: time.Unix(0, order.CreatedNS),
406 BeganProcessing: order.BeganProcessing,
407 CertificateSerial: order.CertificateSerial,
408 }
409
410 if order.Error != nil {
411 errJSON, err := json.Marshal(order.Error)
412 if err != nil {
413 return nil, err
414 }
415 if len(errJSON) > mediumBlobSize {
416 return nil, fmt.Errorf("Error object is too large to store in the database")
417 }
418 om.Error = errJSON
419 }
420 return om, nil
421 }
422
423 func modelToOrder(om *orderModel) (*corepb.Order, error) {
424 order := &corepb.Order{
425 Id: om.ID,
426 RegistrationID: om.RegistrationID,
427 ExpiresNS: om.Expires.UnixNano(),
428 Expires: timestamppb.New(om.Expires),
429 CreatedNS: om.Created.UnixNano(),
430 Created: timestamppb.New(om.Created),
431 CertificateSerial: om.CertificateSerial,
432 BeganProcessing: om.BeganProcessing,
433 }
434 if len(om.Error) > 0 {
435 var problem corepb.ProblemDetails
436 err := json.Unmarshal(om.Error, &problem)
437 if err != nil {
438 return &corepb.Order{}, badJSONError(
439 "failed to unmarshal order model's error",
440 om.Error,
441 err)
442 }
443 order.Error = &problem
444 }
445 return order, nil
446 }
447
448 var challTypeToUint = map[string]uint8{
449 "http-01": 0,
450 "dns-01": 1,
451 "tls-alpn-01": 2,
452 }
453
454 var uintToChallType = map[uint8]string{
455 0: "http-01",
456 1: "dns-01",
457 2: "tls-alpn-01",
458 }
459
460 var identifierTypeToUint = map[string]uint8{
461 "dns": 0,
462 }
463
464 var uintToIdentifierType = map[uint8]string{
465 0: "dns",
466 }
467
468 var statusToUint = map[core.AcmeStatus]uint8{
469 core.StatusPending: 0,
470 core.StatusValid: 1,
471 core.StatusInvalid: 2,
472 core.StatusDeactivated: 3,
473 core.StatusRevoked: 4,
474 }
475
476 var uintToStatus = map[uint8]core.AcmeStatus{
477 0: core.StatusPending,
478 1: core.StatusValid,
479 2: core.StatusInvalid,
480 3: core.StatusDeactivated,
481 4: core.StatusRevoked,
482 }
483
484 func statusUint(status core.AcmeStatus) uint8 {
485 return statusToUint[status]
486 }
487
488
489
490 const authzFields = "id, identifierType, identifierValue, registrationID, status, expires, challenges, attempted, attemptedAt, token, validationError, validationRecord"
491
492 type authzModel struct {
493 ID int64 `db:"id"`
494 IdentifierType uint8 `db:"identifierType"`
495 IdentifierValue string `db:"identifierValue"`
496 RegistrationID int64 `db:"registrationID"`
497 Status uint8 `db:"status"`
498 Expires time.Time `db:"expires"`
499 Challenges uint8 `db:"challenges"`
500 Attempted *uint8 `db:"attempted"`
501 AttemptedAt *time.Time `db:"attemptedAt"`
502 Token []byte `db:"token"`
503 ValidationError []byte `db:"validationError"`
504 ValidationRecord []byte `db:"validationRecord"`
505 }
506
507
508
509
510
511
512 func rehydrateHostPort(vr *core.ValidationRecord) error {
513 if vr.URL == "" {
514 return fmt.Errorf("rehydrating validation record, URL field cannot be empty")
515 }
516
517 parsedUrl, err := url.Parse(vr.URL)
518 if err != nil {
519 return fmt.Errorf("parsing validation record URL %q: %w", vr.URL, err)
520 }
521
522 if vr.Hostname == "" {
523 hostname := parsedUrl.Hostname()
524 if hostname == "" {
525 return fmt.Errorf("hostname missing in URL %q", vr.URL)
526 }
527 vr.Hostname = hostname
528 }
529
530 if vr.Port == "" {
531
532
533 if parsedUrl.Port() == "" {
534
535 switch parsedUrl.Scheme {
536 case "https":
537 vr.Port = "443"
538 case "http":
539 vr.Port = "80"
540 default:
541
542
543 return fmt.Errorf("unknown scheme %q in URL %q", parsedUrl.Scheme, vr.URL)
544 }
545 } else if parsedUrl.Port() == "80" || parsedUrl.Port() == "443" {
546
547
548 vr.Port = parsedUrl.Port()
549 } else {
550 return fmt.Errorf("only ports 80/tcp and 443/tcp are allowed in URL %q", vr.URL)
551 }
552 }
553
554 return nil
555 }
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571 func SelectAuthzsMatchingIssuance(
572 ctx context.Context,
573 s db.Selector,
574 regID int64,
575 issued time.Time,
576 dnsNames []string,
577 ) ([]*corepb.Authorization, error) {
578 query := fmt.Sprintf(`SELECT %s FROM authz2 WHERE
579 registrationID = ? AND
580 status IN (?, ?) AND
581 expires >= ? AND
582 attemptedAt <= ? AND
583 identifierType = ? AND
584 identifierValue IN (%s)`,
585 authzFields,
586 db.QuestionMarks(len(dnsNames)))
587 var args []any
588 args = append(args,
589 regID,
590 statusToUint[core.StatusValid],
591 statusToUint[core.StatusDeactivated],
592 issued.Add(-1*time.Second),
593 issued.Add(1*time.Second),
594 identifierTypeToUint[string(identifier.DNS)],
595 )
596 for _, name := range dnsNames {
597 args = append(args, name)
598 }
599
600 var authzModels []authzModel
601 _, err := s.Select(ctx, &authzModels, query, args...)
602 if err != nil {
603 return nil, err
604 }
605
606 var authzs []*corepb.Authorization
607 for _, model := range authzModels {
608 authz, err := modelToAuthzPB(model)
609 if err != nil {
610 return nil, err
611 }
612 authzs = append(authzs, authz)
613
614 }
615 return authzs, err
616 }
617
618
619
620 func hasMultipleNonPendingChallenges(challenges []*corepb.Challenge) bool {
621 nonPending := false
622 for _, c := range challenges {
623 if c.Status == string(core.StatusValid) || c.Status == string(core.StatusInvalid) {
624 if !nonPending {
625 nonPending = true
626 } else {
627 return true
628 }
629 }
630 }
631 return false
632 }
633
634
635
636 func authzPBToModel(authz *corepb.Authorization) (*authzModel, error) {
637 am := &authzModel{
638 IdentifierValue: authz.Identifier,
639 RegistrationID: authz.RegistrationID,
640 Status: statusToUint[core.AcmeStatus(authz.Status)],
641 Expires: time.Unix(0, authz.ExpiresNS).UTC(),
642 }
643 if authz.Id != "" {
644
645
646
647 id, err := strconv.Atoi(authz.Id)
648 if err != nil {
649 return nil, err
650 }
651 am.ID = int64(id)
652 }
653 if hasMultipleNonPendingChallenges(authz.Challenges) {
654 return nil, errors.New("multiple challenges are non-pending")
655 }
656
657
658
659
660
661
662
663
664
665
666 var tokenStr string
667 for _, chall := range authz.Challenges {
668
669 am.Challenges |= 1 << challTypeToUint[chall.Type]
670 tokenStr = chall.Token
671
672
673 if chall.Status == string(core.StatusValid) || chall.Status == string(core.StatusInvalid) {
674 attemptedType := challTypeToUint[chall.Type]
675 am.Attempted = &attemptedType
676
677
678 var validated *time.Time
679 if chall.ValidatedNS != 0 {
680 val := time.Unix(0, chall.ValidatedNS).UTC()
681 validated = &val
682 }
683 am.AttemptedAt = validated
684
685
686
687 records := make([]core.ValidationRecord, len(chall.Validationrecords))
688 for i, recordPB := range chall.Validationrecords {
689 if chall.Type == string(core.ChallengeTypeHTTP01) {
690
691
692 recordPB.Hostname = ""
693 recordPB.Port = ""
694 }
695 var err error
696 records[i], err = grpc.PBToValidationRecord(recordPB)
697 if err != nil {
698 return nil, err
699 }
700 }
701 var err error
702 am.ValidationRecord, err = json.Marshal(records)
703 if err != nil {
704 return nil, err
705 }
706
707
708 if chall.Error != nil {
709 prob, err := grpc.PBToProblemDetails(chall.Error)
710 if err != nil {
711 return nil, err
712 }
713 am.ValidationError, err = json.Marshal(prob)
714 if err != nil {
715 return nil, err
716 }
717 }
718 }
719 token, err := base64.RawURLEncoding.DecodeString(tokenStr)
720 if err != nil {
721 return nil, err
722 }
723 am.Token = token
724 }
725
726 return am, nil
727 }
728
729
730
731 func populateAttemptedFields(am authzModel, challenge *corepb.Challenge) error {
732 if len(am.ValidationError) != 0 {
733
734 challenge.Status = string(core.StatusInvalid)
735 var prob probs.ProblemDetails
736 err := json.Unmarshal(am.ValidationError, &prob)
737 if err != nil {
738 return badJSONError(
739 "failed to unmarshal authz2 model's validation error",
740 am.ValidationError,
741 err)
742 }
743 challenge.Error, err = grpc.ProblemDetailsToPB(&prob)
744 if err != nil {
745 return err
746 }
747 } else {
748
749 challenge.Status = string(core.StatusValid)
750 }
751 var records []core.ValidationRecord
752 err := json.Unmarshal(am.ValidationRecord, &records)
753 if err != nil {
754 return badJSONError(
755 "failed to unmarshal authz2 model's validation record",
756 am.ValidationRecord,
757 err)
758 }
759 challenge.Validationrecords = make([]*corepb.ValidationRecord, len(records))
760 for i, r := range records {
761
762
763 r := r
764 if challenge.Type == string(core.ChallengeTypeHTTP01) {
765 err := rehydrateHostPort(&r)
766 if err != nil {
767 return err
768 }
769 }
770 challenge.Validationrecords[i], err = grpc.ValidationRecordToPB(r)
771 if err != nil {
772 return err
773 }
774 }
775 return nil
776 }
777
778 func modelToAuthzPB(am authzModel) (*corepb.Authorization, error) {
779 pb := &corepb.Authorization{
780 Id: fmt.Sprintf("%d", am.ID),
781 Status: string(uintToStatus[am.Status]),
782 Identifier: am.IdentifierValue,
783 RegistrationID: am.RegistrationID,
784 ExpiresNS: am.Expires.UTC().UnixNano(),
785 Expires: timestamppb.New(am.Expires),
786 }
787
788
789
790
791
792
793
794
795 for pos := uint8(0); pos < 8; pos++ {
796 if (am.Challenges>>pos)&1 == 1 {
797 challType := uintToChallType[pos]
798 challenge := &corepb.Challenge{
799 Type: challType,
800 Status: string(core.StatusPending),
801 Token: base64.RawURLEncoding.EncodeToString(am.Token),
802 }
803
804
805
806
807 if am.Attempted != nil {
808 if uintToChallType[*am.Attempted] == challType {
809 err := populateAttemptedFields(am, challenge)
810 if err != nil {
811 return nil, err
812 }
813
814 var validatedInt int64 = 0
815 validatedTS := timestamppb.New(time.Time{})
816 if am.AttemptedAt != nil {
817 validatedInt = am.AttemptedAt.UTC().UnixNano()
818 validatedTS = timestamppb.New(*am.AttemptedAt)
819 }
820 challenge.ValidatedNS = validatedInt
821 challenge.Validated = validatedTS
822 pb.Challenges = append(pb.Challenges, challenge)
823 }
824 } else {
825
826
827 pb.Challenges = append(pb.Challenges, challenge)
828 }
829 }
830 }
831 return pb, nil
832 }
833
834 type keyHashModel struct {
835 ID int64
836 KeyHash []byte
837 CertNotAfter time.Time
838 CertSerial string
839 }
840
841 var stringToSourceInt = map[string]int{
842 "API": 1,
843 "admin-revoker": 2,
844 }
845
846
847 type incidentModel struct {
848 ID int64 `db:"id"`
849 SerialTable string `db:"serialTable"`
850 URL string `db:"url"`
851 RenewBy time.Time `db:"renewBy"`
852 Enabled bool `db:"enabled"`
853 }
854
855 func incidentModelToPB(i incidentModel) sapb.Incident {
856 return sapb.Incident{
857 Id: i.ID,
858 SerialTable: i.SerialTable,
859 Url: i.URL,
860 RenewByNS: i.RenewBy.UnixNano(),
861 RenewBy: timestamppb.New(i.RenewBy),
862 Enabled: i.Enabled,
863 }
864 }
865
866
867 type incidentSerialModel struct {
868 Serial string `db:"serial"`
869 RegistrationID *int64 `db:"registrationID"`
870 OrderID *int64 `db:"orderID"`
871 LastNoticeSent *time.Time `db:"lastNoticeSent"`
872 }
873
874
875
876 type crlEntryModel struct {
877 Serial string `db:"serial"`
878 Status core.OCSPStatus `db:"status"`
879 RevokedReason revocation.Reason `db:"revokedReason"`
880 RevokedDate time.Time `db:"revokedDate"`
881 }
882
883
884
885
886
887 type orderFQDNSet struct {
888 ID int64
889 SetHash []byte
890 OrderID int64
891 RegistrationID int64
892 Expires time.Time
893 }
894
895 func addFQDNSet(ctx context.Context, db db.Inserter, names []string, serial string, issued time.Time, expires time.Time) error {
896 return db.Insert(ctx, &core.FQDNSet{
897 SetHash: core.HashNames(names),
898 Serial: serial,
899 Issued: issued,
900 Expires: expires,
901 })
902 }
903
904
905
906
907
908 func addOrderFQDNSet(
909 ctx context.Context,
910 db db.Inserter,
911 names []string,
912 orderID int64,
913 regID int64,
914 expires time.Time) error {
915 return db.Insert(ctx, &orderFQDNSet{
916 SetHash: core.HashNames(names),
917 OrderID: orderID,
918 RegistrationID: regID,
919 Expires: expires,
920 })
921 }
922
923
924
925
926
927 func deleteOrderFQDNSet(
928 ctx context.Context,
929 db db.Execer,
930 orderID int64) error {
931
932 result, err := db.ExecContext(ctx, `
933 DELETE FROM orderFqdnSets
934 WHERE orderID = ?`,
935 orderID)
936 if err != nil {
937 return err
938 }
939 rowsDeleted, err := result.RowsAffected()
940 if err != nil {
941 return err
942 }
943
944
945
946 if rowsDeleted == 0 {
947 return berrors.InternalServerError("No orderFQDNSet exists to delete")
948 }
949 return nil
950 }
951
952 func addIssuedNames(ctx context.Context, queryer db.Queryer, cert *x509.Certificate, isRenewal bool) error {
953 if len(cert.DNSNames) == 0 {
954 return berrors.InternalServerError("certificate has no DNSNames")
955 }
956
957 multiInserter, err := db.NewMultiInserter("issuedNames", []string{"reversedName", "serial", "notBefore", "renewal"}, "")
958 if err != nil {
959 return err
960 }
961 for _, name := range cert.DNSNames {
962 err = multiInserter.Add([]interface{}{
963 ReverseName(name),
964 core.SerialToString(cert.SerialNumber),
965 cert.NotBefore,
966 isRenewal,
967 })
968 if err != nil {
969 return err
970 }
971 }
972 _, err = multiInserter.Insert(ctx, queryer)
973 return err
974 }
975
976 func addKeyHash(ctx context.Context, db db.Inserter, cert *x509.Certificate) error {
977 if cert.RawSubjectPublicKeyInfo == nil {
978 return errors.New("certificate has a nil RawSubjectPublicKeyInfo")
979 }
980 h := sha256.Sum256(cert.RawSubjectPublicKeyInfo)
981 khm := &keyHashModel{
982 KeyHash: h[:],
983 CertNotAfter: cert.NotAfter,
984 CertSerial: core.SerialToString(cert.SerialNumber),
985 }
986 return db.Insert(ctx, khm)
987 }
988
989 var blockedKeysColumns = "keyHash, added, source, comment"
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005 func statusForOrder(ctx context.Context, s db.Selector, order *corepb.Order, now time.Time) (string, error) {
1006
1007 if order.Error != nil {
1008 return string(core.StatusInvalid), nil
1009 }
1010
1011
1012
1013
1014
1015
1016
1017
1018 orderExpiry := time.Unix(0, order.ExpiresNS)
1019 if orderExpiry.Before(now) {
1020 return string(core.StatusInvalid), nil
1021 }
1022
1023
1024 authzValidityInfo, err := getAuthorizationStatuses(ctx, s, order.V2Authorizations)
1025
1026 if err != nil {
1027 return "", err
1028 }
1029
1030
1031
1032
1033 if len(authzValidityInfo) != len(order.V2Authorizations) {
1034 return "", berrors.InternalServerError(
1035 "getAuthorizationStatuses returned the wrong number of authorization statuses "+
1036 "(%d vs expected %d) for order %d",
1037 len(authzValidityInfo), len(order.V2Authorizations), order.Id)
1038 }
1039
1040
1041 pendingAuthzs := 0
1042 validAuthzs := 0
1043 otherAuthzs := 0
1044 expiredAuthzs := 0
1045
1046
1047 for _, info := range authzValidityInfo {
1048 switch core.AcmeStatus(info.Status) {
1049 case core.StatusPending:
1050 pendingAuthzs++
1051 case core.StatusValid:
1052 validAuthzs++
1053 case core.StatusInvalid:
1054 otherAuthzs++
1055 case core.StatusDeactivated:
1056 otherAuthzs++
1057 case core.StatusRevoked:
1058 otherAuthzs++
1059 default:
1060 return "", berrors.InternalServerError(
1061 "Order is in an invalid state. Authz has invalid status %s",
1062 info.Status)
1063 }
1064 if info.Expires.Before(now) {
1065 expiredAuthzs++
1066 }
1067 }
1068
1069
1070
1071 if otherAuthzs > 0 || expiredAuthzs > 0 {
1072 return string(core.StatusInvalid), nil
1073 }
1074
1075 if pendingAuthzs > 0 {
1076 return string(core.StatusPending), nil
1077 }
1078
1079
1080
1081 fullyAuthorized := len(order.Names) == validAuthzs
1082
1083
1084
1085
1086
1087 if !fullyAuthorized {
1088 return "", berrors.InternalServerError(
1089 "Order has the incorrect number of valid authorizations & no pending, " +
1090 "deactivated or invalid authorizations")
1091 }
1092
1093
1094
1095 if fullyAuthorized && order.CertificateSerial != "" {
1096 return string(core.StatusValid), nil
1097 }
1098
1099
1100
1101 if fullyAuthorized && order.BeganProcessing {
1102 return string(core.StatusProcessing), nil
1103 }
1104
1105 if fullyAuthorized && !order.BeganProcessing {
1106 return string(core.StatusReady), nil
1107 }
1108
1109 return "", berrors.InternalServerError(
1110 "Order %d is in an invalid state. No state known for this order's "+
1111 "authorizations", order.Id)
1112 }
1113
1114 type authzValidity struct {
1115 Status string
1116 Expires time.Time
1117 }
1118
1119
1120
1121 func getAuthorizationStatuses(ctx context.Context, s db.Selector, ids []int64) ([]authzValidity, error) {
1122 var params []interface{}
1123 for _, id := range ids {
1124 params = append(params, id)
1125 }
1126 var validityInfo []struct {
1127 Status uint8
1128 Expires time.Time
1129 }
1130 _, err := s.Select(
1131 ctx,
1132 &validityInfo,
1133 fmt.Sprintf("SELECT status, expires FROM authz2 WHERE id IN (%s)",
1134 db.QuestionMarks(len(ids))),
1135 params...,
1136 )
1137 if err != nil {
1138 return nil, err
1139 }
1140
1141 allAuthzValidity := make([]authzValidity, len(validityInfo))
1142 for i, info := range validityInfo {
1143 allAuthzValidity[i] = authzValidity{
1144 Status: string(uintToStatus[info.Status]),
1145 Expires: info.Expires,
1146 }
1147 }
1148 return allAuthzValidity, nil
1149 }
1150
1151
1152 func authzForOrder(ctx context.Context, s db.Selector, orderID int64) ([]int64, error) {
1153 var v2IDs []int64
1154 _, err := s.Select(
1155 ctx,
1156 &v2IDs,
1157 "SELECT authzID FROM orderToAuthz2 WHERE orderID = ?",
1158 orderID,
1159 )
1160 return v2IDs, err
1161 }
1162
1163
1164
1165 func namesForOrder(ctx context.Context, s db.Selector, orderID int64) ([]string, error) {
1166 var reversedNames []string
1167 _, err := s.Select(
1168 ctx,
1169 &reversedNames,
1170 `SELECT reversedName
1171 FROM requestedNames
1172 WHERE orderID = ?`,
1173 orderID)
1174 if err != nil {
1175 return nil, err
1176 }
1177 return reversedNames, nil
1178 }
1179
1180
1181
1182 type crlShardModel struct {
1183 ID int64 `db:"id"`
1184 IssuerID int64 `db:"issuerID"`
1185 Idx int `db:"idx"`
1186 ThisUpdate *time.Time `db:"thisUpdate"`
1187 NextUpdate *time.Time `db:"nextUpdate"`
1188 LeasedUntil time.Time `db:"leasedUntil"`
1189 }
1190
1191
1192
1193
1194 type revokedCertModel struct {
1195 ID int64 `db:"id"`
1196 IssuerID int64 `db:"issuerID"`
1197 Serial string `db:"serial"`
1198 NotAfterHour time.Time `db:"notAfterHour"`
1199 ShardIdx int64 `db:"shardIdx"`
1200 RevokedDate time.Time `db:"revokedDate"`
1201 RevokedReason revocation.Reason `db:"revokedReason"`
1202 }
1203
View as plain text