1 package wfe2
2
3 import (
4 "context"
5 "crypto/x509"
6 "crypto/x509/pkix"
7 "encoding/asn1"
8 "encoding/base64"
9 "encoding/json"
10 "encoding/pem"
11 "errors"
12 "fmt"
13 "math/big"
14 "net"
15 "net/http"
16 "strconv"
17 "strings"
18 "time"
19
20 "github.com/jmhodges/clock"
21 "github.com/prometheus/client_golang/prometheus"
22 "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
23 "go.opentelemetry.io/otel/trace"
24 "google.golang.org/protobuf/types/known/emptypb"
25
26 "github.com/letsencrypt/boulder/core"
27 corepb "github.com/letsencrypt/boulder/core/proto"
28 berrors "github.com/letsencrypt/boulder/errors"
29 "github.com/letsencrypt/boulder/features"
30 "github.com/letsencrypt/boulder/goodkey"
31 bgrpc "github.com/letsencrypt/boulder/grpc"
32 "github.com/letsencrypt/boulder/ratelimits"
33
34
35 _ "github.com/letsencrypt/boulder/grpc/noncebalancer"
36 "github.com/letsencrypt/boulder/identifier"
37 "github.com/letsencrypt/boulder/issuance"
38 blog "github.com/letsencrypt/boulder/log"
39 "github.com/letsencrypt/boulder/metrics/measured_http"
40 "github.com/letsencrypt/boulder/nonce"
41 "github.com/letsencrypt/boulder/probs"
42 rapb "github.com/letsencrypt/boulder/ra/proto"
43 "github.com/letsencrypt/boulder/revocation"
44 sapb "github.com/letsencrypt/boulder/sa/proto"
45 "github.com/letsencrypt/boulder/web"
46 )
47
48
49
50
51
52 const (
53 directoryPath = "/directory"
54 newAcctPath = "/acme/new-acct"
55 acctPath = "/acme/acct/"
56
57
58 authzPath = "/acme/authz-v3/"
59 challengePath = "/acme/chall-v3/"
60 certPath = "/acme/cert/"
61 revokeCertPath = "/acme/revoke-cert"
62 buildIDPath = "/build"
63 rolloverPath = "/acme/key-change"
64 newNoncePath = "/acme/new-nonce"
65 newOrderPath = "/acme/new-order"
66 orderPath = "/acme/order/"
67 finalizeOrderPath = "/acme/finalize/"
68
69 getAPIPrefix = "/get/"
70 getOrderPath = getAPIPrefix + "order/"
71 getAuthzPath = getAPIPrefix + "authz-v3/"
72 getChallengePath = getAPIPrefix + "chall-v3/"
73 getCertPath = getAPIPrefix + "cert/"
74
75
76 renewalInfoPath = "/draft-ietf-acme-ari-01/renewalInfo/"
77
78
79 aiaIssuerPath = "/aia/issuer/"
80 )
81
82 const (
83 headerRetryAfter = "Retry-After"
84
85
86
87 orderRetryAfter = 3
88 )
89
90 var errIncompleteGRPCResponse = errors.New("incomplete gRPC response message")
91
92
93
94
95
96 type WebFrontEndImpl struct {
97 ra rapb.RegistrationAuthorityClient
98 sa sapb.StorageAuthorityReadOnlyClient
99
100
101
102 gnc nonce.Getter
103
104
105
106 noncePrefixMap map[string]nonce.Redeemer
107
108
109
110
111
112 rnc nonce.Redeemer
113
114
115 rncKey string
116 accountGetter AccountGetter
117 log blog.Logger
118 clk clock.Clock
119 stats wfe2Stats
120
121
122
123
124
125 certificateChains map[issuance.IssuerNameID][][]byte
126
127
128
129
130 issuerCertificates map[issuance.IssuerNameID]*issuance.Certificate
131
132
133 SubscriberAgreementURL string
134
135
136
137
138 DirectoryCAAIdentity string
139
140
141
142 DirectoryWebsite string
143
144
145
146
147 LegacyKeyIDPrefix string
148
149
150 keyPolicy goodkey.KeyPolicy
151
152
153 AllowOrigins []string
154
155
156 requestTimeout time.Duration
157
158
159
160
161
162 staleTimeout time.Duration
163
164
165
166
167
168 authorizationLifetime time.Duration
169 pendingAuthorizationLifetime time.Duration
170 limiter *ratelimits.Limiter
171 }
172
173
174 func NewWebFrontEndImpl(
175 stats prometheus.Registerer,
176 clk clock.Clock,
177 keyPolicy goodkey.KeyPolicy,
178 certificateChains map[issuance.IssuerNameID][][]byte,
179 issuerCertificates map[issuance.IssuerNameID]*issuance.Certificate,
180 logger blog.Logger,
181 requestTimeout time.Duration,
182 staleTimeout time.Duration,
183 authorizationLifetime time.Duration,
184 pendingAuthorizationLifetime time.Duration,
185 rac rapb.RegistrationAuthorityClient,
186 sac sapb.StorageAuthorityReadOnlyClient,
187 gnc nonce.Getter,
188 noncePrefixMap map[string]nonce.Redeemer,
189 rnc nonce.Redeemer,
190 rncKey string,
191 accountGetter AccountGetter,
192 limiter *ratelimits.Limiter,
193 ) (WebFrontEndImpl, error) {
194 if len(issuerCertificates) == 0 {
195 return WebFrontEndImpl{}, errors.New("must provide at least one issuer certificate")
196 }
197
198 if len(certificateChains) == 0 {
199 return WebFrontEndImpl{}, errors.New("must provide at least one certificate chain")
200 }
201
202 if gnc == nil {
203 return WebFrontEndImpl{}, errors.New("must provide a service for nonce issuance")
204 }
205
206
207 if noncePrefixMap == nil && rnc == nil {
208 return WebFrontEndImpl{}, errors.New("must provide a service for nonce redemption")
209 }
210
211 wfe := WebFrontEndImpl{
212 log: logger,
213 clk: clk,
214 keyPolicy: keyPolicy,
215 certificateChains: certificateChains,
216 issuerCertificates: issuerCertificates,
217 stats: initStats(stats),
218 requestTimeout: requestTimeout,
219 staleTimeout: staleTimeout,
220 authorizationLifetime: authorizationLifetime,
221 pendingAuthorizationLifetime: pendingAuthorizationLifetime,
222 ra: rac,
223 sa: sac,
224 gnc: gnc,
225 noncePrefixMap: noncePrefixMap,
226 rnc: rnc,
227 rncKey: rncKey,
228 accountGetter: accountGetter,
229 limiter: limiter,
230 }
231
232 return wfe, nil
233 }
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253 func (wfe *WebFrontEndImpl) HandleFunc(mux *http.ServeMux, pattern string, h web.WFEHandlerFunc, methods ...string) {
254 methodsMap := make(map[string]bool)
255 for _, m := range methods {
256 methodsMap[m] = true
257 }
258 if methodsMap["GET"] && !methodsMap["HEAD"] {
259
260 methods = append(methods, "HEAD")
261 methodsMap["HEAD"] = true
262 }
263 methodsStr := strings.Join(methods, ", ")
264 handler := http.StripPrefix(pattern, web.NewTopHandler(wfe.log,
265 web.WFEHandlerFunc(func(ctx context.Context, logEvent *web.RequestEvent, response http.ResponseWriter, request *http.Request) {
266 span := trace.SpanFromContext(ctx)
267 span.SetName(pattern)
268
269 logEvent.Endpoint = pattern
270 if request.URL != nil {
271 logEvent.Slug = request.URL.Path
272 }
273 tls := request.Header.Get("TLS-Version")
274 if tls == "TLSv1" || tls == "TLSv1.1" {
275 wfe.sendError(response, logEvent, probs.Malformed("upgrade your ACME client to support TLSv1.2 or better"), nil)
276 return
277 }
278 if request.Method != "GET" || pattern == newNoncePath {
279 nonceMsg, err := wfe.gnc.Nonce(ctx, &emptypb.Empty{})
280 if err != nil {
281 wfe.sendError(response, logEvent, web.ProblemDetailsForError(err, "unable to get nonce"), err)
282 return
283 }
284 response.Header().Set("Replay-Nonce", nonceMsg.Nonce)
285 }
286
287
288
289 if pattern != directoryPath {
290 directoryURL := web.RelativeEndpoint(request, directoryPath)
291 response.Header().Add("Link", link(directoryURL, "index"))
292 }
293
294 switch request.Method {
295 case "HEAD":
296
297
298
299 case "OPTIONS":
300 wfe.Options(response, request, methodsStr, methodsMap)
301 return
302 }
303
304
305 addNoCacheHeader(response)
306
307 if !methodsMap[request.Method] {
308 response.Header().Set("Allow", methodsStr)
309 wfe.sendError(response, logEvent, probs.MethodNotAllowed(), nil)
310 return
311 }
312
313 wfe.setCORSHeaders(response, request, "")
314
315 timeout := wfe.requestTimeout
316 if timeout == 0 {
317 timeout = 5 * time.Minute
318 }
319 ctx, cancel := context.WithTimeout(ctx, timeout)
320
321
322 h(ctx, logEvent, response, request)
323 cancel()
324 }),
325 ))
326 mux.Handle(pattern, handler)
327 }
328
329 func marshalIndent(v interface{}) ([]byte, error) {
330 return json.MarshalIndent(v, "", " ")
331 }
332
333 func (wfe *WebFrontEndImpl) writeJsonResponse(response http.ResponseWriter, logEvent *web.RequestEvent, status int, v interface{}) error {
334 jsonReply, err := marshalIndent(v)
335 if err != nil {
336 return err
337 }
338
339 response.Header().Set("Content-Type", "application/json")
340 response.WriteHeader(status)
341 _, err = response.Write(jsonReply)
342 if err != nil {
343
344
345 wfe.log.Warningf("Could not write response: %s", err)
346 logEvent.AddError("failed to write response: %s", err)
347 }
348 return nil
349 }
350
351
352
353 func requestProto(request *http.Request) string {
354 proto := "http"
355
356
357 if request.TLS != nil {
358 proto = "https"
359 }
360
361
362
363 if specifiedProto := request.Header.Get("X-Forwarded-Proto"); specifiedProto != "" {
364 proto = specifiedProto
365 }
366
367 return proto
368 }
369
370 const randomDirKeyExplanationLink = "https://community.letsencrypt.org/t/adding-random-entries-to-the-directory/33417"
371
372 func (wfe *WebFrontEndImpl) relativeDirectory(request *http.Request, directory map[string]interface{}) ([]byte, error) {
373
374
375 relativeDir := make(map[string]interface{}, len(directory))
376
377
378
379 for k, v := range directory {
380 if v == randomDirKeyExplanationLink {
381 relativeDir[k] = v
382 continue
383 }
384 switch v := v.(type) {
385 case string:
386
387 relativeDir[k] = web.RelativeEndpoint(request, v)
388 default:
389
390 relativeDir[k] = v
391 }
392 }
393
394 directoryJSON, err := marshalIndent(relativeDir)
395
396 if err != nil {
397 return nil, err
398 }
399
400 return directoryJSON, nil
401 }
402
403
404
405 func (wfe *WebFrontEndImpl) Handler(stats prometheus.Registerer, oTelHTTPOptions ...otelhttp.Option) http.Handler {
406 m := http.NewServeMux()
407
408 wfe.HandleFunc(m, buildIDPath, wfe.BuildID, "GET")
409
410
411 wfe.HandleFunc(m, newAcctPath, wfe.NewAccount, "POST")
412 wfe.HandleFunc(m, acctPath, wfe.Account, "POST")
413 wfe.HandleFunc(m, revokeCertPath, wfe.RevokeCertificate, "POST")
414 wfe.HandleFunc(m, rolloverPath, wfe.KeyRollover, "POST")
415 wfe.HandleFunc(m, newOrderPath, wfe.NewOrder, "POST")
416 wfe.HandleFunc(m, finalizeOrderPath, wfe.FinalizeOrder, "POST")
417
418
419 wfe.HandleFunc(m, directoryPath, wfe.Directory, "GET", "POST")
420 wfe.HandleFunc(m, newNoncePath, wfe.Nonce, "GET", "POST")
421
422
423
424 wfe.HandleFunc(m, orderPath, wfe.GetOrder, "GET", "POST")
425 wfe.HandleFunc(m, authzPath, wfe.Authorization, "GET", "POST")
426 wfe.HandleFunc(m, challengePath, wfe.Challenge, "GET", "POST")
427 wfe.HandleFunc(m, certPath, wfe.Certificate, "GET", "POST")
428
429 wfe.HandleFunc(m, getOrderPath, wfe.GetOrder, "GET")
430 wfe.HandleFunc(m, getAuthzPath, wfe.Authorization, "GET")
431 wfe.HandleFunc(m, getChallengePath, wfe.Challenge, "GET")
432 wfe.HandleFunc(m, getCertPath, wfe.Certificate, "GET")
433
434
435 if features.Enabled(features.ServeRenewalInfo) {
436 wfe.HandleFunc(m, renewalInfoPath, wfe.RenewalInfo, "GET", "POST")
437 }
438
439
440 wfe.HandleFunc(m, aiaIssuerPath, wfe.Issuer, "GET")
441
442
443
444
445 m.Handle("/", web.NewTopHandler(wfe.log, web.WFEHandlerFunc(wfe.Index)))
446 return measured_http.New(m, wfe.clk, stats, oTelHTTPOptions...)
447 }
448
449
450
451
452 func (wfe *WebFrontEndImpl) Index(ctx context.Context, logEvent *web.RequestEvent, response http.ResponseWriter, request *http.Request) {
453
454
455
456
457 logEvent.Endpoint = "/"
458 logEvent.Slug = request.URL.Path[1:]
459
460
461
462
463 if request.URL.Path != "/" {
464 logEvent.AddError("Resource not found")
465 http.NotFound(response, request)
466 response.Header().Set("Content-Type", "application/problem+json")
467 return
468 }
469
470 if request.Method != "GET" {
471 response.Header().Set("Allow", "GET")
472 wfe.sendError(response, logEvent, probs.MethodNotAllowed(), errors.New("Bad method"))
473 return
474 }
475
476 addNoCacheHeader(response)
477 response.Header().Set("Content-Type", "text/html")
478 fmt.Fprintf(response, `<html>
479 <body>
480 This is an <a href="https://tools.ietf.org/html/rfc8555">ACME</a>
481 Certificate Authority running <a href="https://github.com/letsencrypt/boulder">Boulder</a>.
482 JSON directory is available at <a href="%s">%s</a>.
483 </body>
484 </html>
485 `, directoryPath, directoryPath)
486 }
487
488 func addNoCacheHeader(w http.ResponseWriter) {
489 w.Header().Add("Cache-Control", "public, max-age=0, no-cache")
490 }
491
492 func addRequesterHeader(w http.ResponseWriter, requester int64) {
493 if requester > 0 {
494 w.Header().Set("Boulder-Requester", strconv.FormatInt(requester, 10))
495 }
496 }
497
498
499
500
501 func (wfe *WebFrontEndImpl) Directory(
502 ctx context.Context,
503 logEvent *web.RequestEvent,
504 response http.ResponseWriter,
505 request *http.Request) {
506 directoryEndpoints := map[string]interface{}{
507 "newAccount": newAcctPath,
508 "newNonce": newNoncePath,
509 "revokeCert": revokeCertPath,
510 "newOrder": newOrderPath,
511 "keyChange": rolloverPath,
512 }
513
514 if features.Enabled(features.ServeRenewalInfo) {
515 directoryEndpoints["renewalInfo"] = renewalInfoPath
516 }
517
518 if request.Method == http.MethodPost {
519 acct, prob := wfe.validPOSTAsGETForAccount(request, ctx, logEvent)
520 if prob != nil {
521 wfe.sendError(response, logEvent, prob, nil)
522 return
523 }
524 logEvent.Requester = acct.ID
525 }
526
527
528
529
530 directoryEndpoints[core.RandomString(8)] = randomDirKeyExplanationLink
531
532
533
534
535 metaMap := map[string]interface{}{
536 "termsOfService": wfe.SubscriberAgreementURL,
537 }
538
539 if wfe.DirectoryCAAIdentity != "" {
540
541
542
543
544 metaMap["caaIdentities"] = []string{
545 wfe.DirectoryCAAIdentity,
546 }
547 }
548
549 if wfe.DirectoryWebsite != "" {
550 metaMap["website"] = wfe.DirectoryWebsite
551 }
552 directoryEndpoints["meta"] = metaMap
553
554 response.Header().Set("Content-Type", "application/json")
555
556 relDir, err := wfe.relativeDirectory(request, directoryEndpoints)
557 if err != nil {
558 marshalProb := probs.ServerInternal("unable to marshal JSON directory")
559 wfe.sendError(response, logEvent, marshalProb, nil)
560 return
561 }
562
563 logEvent.Suppress()
564 response.Write(relDir)
565 }
566
567
568
569
570 func (wfe *WebFrontEndImpl) Nonce(
571 ctx context.Context,
572 logEvent *web.RequestEvent,
573 response http.ResponseWriter,
574 request *http.Request) {
575 if request.Method == http.MethodPost {
576 acct, prob := wfe.validPOSTAsGETForAccount(request, ctx, logEvent)
577 if prob != nil {
578 wfe.sendError(response, logEvent, prob, nil)
579 return
580 }
581 logEvent.Requester = acct.ID
582 }
583
584 statusCode := http.StatusNoContent
585
586
587 if request.Method != "GET" {
588 statusCode = http.StatusOK
589 }
590 response.WriteHeader(statusCode)
591
592
593
594
595 response.Header().Set("Cache-Control", "no-store")
596 }
597
598
599 func (wfe *WebFrontEndImpl) sendError(response http.ResponseWriter, logEvent *web.RequestEvent, prob *probs.ProblemDetails, ierr error) {
600 var bErr *berrors.BoulderError
601 if errors.As(ierr, &bErr) {
602 retryAfterSeconds := int(bErr.RetryAfter.Round(time.Second).Seconds())
603 if retryAfterSeconds > 0 {
604 response.Header().Add(headerRetryAfter, strconv.Itoa(retryAfterSeconds))
605 if bErr.Type == berrors.RateLimit {
606 response.Header().Add("Link", link("https://letsencrypt.org/docs/rate-limits", "help"))
607 }
608 }
609 }
610 wfe.stats.httpErrorCount.With(prometheus.Labels{"type": string(prob.Type)}).Inc()
611 web.SendError(wfe.log, response, logEvent, prob, ierr)
612 }
613
614 func link(url, relation string) string {
615 return fmt.Sprintf("<%s>;rel=\"%s\"", url, relation)
616 }
617
618
619
620
621
622
623
624
625
626 func (wfe *WebFrontEndImpl) checkNewAccountLimits(ctx context.Context, ip net.IP) {
627 if wfe.limiter == nil {
628
629 return
630 }
631
632 warn := func(err error, limit ratelimits.Name) {
633 if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
634 return
635 }
636
637
638 wfe.log.Warningf("checking %s rate limit: %s", limit, err)
639 }
640
641 decision, err := wfe.limiter.Spend(ctx, ratelimits.NewRegistrationsPerIPAddress, ip.String(), 1)
642 if err != nil {
643 warn(err, ratelimits.NewRegistrationsPerIPAddress)
644 return
645 }
646 if !decision.Allowed || ip.To4() != nil {
647
648
649 return
650 }
651
652
653
654 ipMask := net.CIDRMask(48, 128)
655 ipNet := &net.IPNet{IP: ip.Mask(ipMask), Mask: ipMask}
656 _, err = wfe.limiter.Spend(ctx, ratelimits.NewRegistrationsPerIPv6Range, ipNet.String(), 1)
657 if err != nil {
658 warn(err, ratelimits.NewRegistrationsPerIPv6Range)
659 }
660 }
661
662
663
664
665
666 func (wfe *WebFrontEndImpl) refundNewAccountLimits(ctx context.Context, ip net.IP) {
667 if wfe.limiter == nil {
668
669 return
670 }
671
672 warn := func(err error, limit ratelimits.Name) {
673 if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
674 return
675 }
676
677
678 wfe.log.Warningf("refunding %s rate limit: %s", limit, err)
679 }
680
681 _, err := wfe.limiter.Refund(ctx, ratelimits.NewRegistrationsPerIPAddress, ip.String(), 1)
682 if err != nil {
683 warn(err, ratelimits.NewRegistrationsPerIPAddress)
684 return
685 }
686 if ip.To4() != nil {
687
688 return
689 }
690
691
692
693 ipMask := net.CIDRMask(48, 128)
694 ipNet := &net.IPNet{IP: ip.Mask(ipMask), Mask: ipMask}
695 _, err = wfe.limiter.Refund(ctx, ratelimits.NewRegistrationsPerIPv6Range, ipNet.String(), 1)
696 if err != nil {
697 warn(err, ratelimits.NewRegistrationsPerIPv6Range)
698 }
699 }
700
701
702 func (wfe *WebFrontEndImpl) NewAccount(
703 ctx context.Context,
704 logEvent *web.RequestEvent,
705 response http.ResponseWriter,
706 request *http.Request) {
707
708
709
710
711 body, key, prob := wfe.validSelfAuthenticatedPOST(ctx, request)
712 if prob != nil {
713
714 wfe.sendError(response, logEvent, prob, nil)
715 return
716 }
717
718 var accountCreateRequest struct {
719 Contact *[]string `json:"contact"`
720 TermsOfServiceAgreed bool `json:"termsOfServiceAgreed"`
721 OnlyReturnExisting bool `json:"onlyReturnExisting"`
722 }
723
724 err := json.Unmarshal(body, &accountCreateRequest)
725 if err != nil {
726 wfe.sendError(response, logEvent, probs.Malformed("Error unmarshaling JSON"), err)
727 return
728 }
729
730 returnExistingAcct := func(acctPB *corepb.Registration) {
731 if core.AcmeStatus(acctPB.Status) == core.StatusDeactivated {
732
733
734 wfe.sendError(response, logEvent, probs.Unauthorized(
735 "An account with the provided public key exists but is deactivated"), nil)
736 return
737 }
738
739 response.Header().Set("Location",
740 web.RelativeEndpoint(request, fmt.Sprintf("%s%d", acctPath, acctPB.Id)))
741 logEvent.Requester = acctPB.Id
742 addRequesterHeader(response, acctPB.Id)
743
744 acct, err := bgrpc.PbToRegistration(acctPB)
745 if err != nil {
746 wfe.sendError(response, logEvent, probs.ServerInternal("Error marshaling account"), err)
747 return
748 }
749 prepAccountForDisplay(&acct)
750
751 err = wfe.writeJsonResponse(response, logEvent, http.StatusOK, acct)
752 if err != nil {
753
754
755 wfe.sendError(response, logEvent, probs.ServerInternal("Error marshaling account"), err)
756 return
757 }
758 }
759
760 keyBytes, err := key.MarshalJSON()
761 if err != nil {
762 wfe.sendError(response, logEvent,
763 web.ProblemDetailsForError(err, "Error creating new account"), err)
764 return
765 }
766 existingAcct, err := wfe.sa.GetRegistrationByKey(ctx, &sapb.JSONWebKey{Jwk: keyBytes})
767 if err == nil {
768 returnExistingAcct(existingAcct)
769 return
770 } else if !errors.Is(err, berrors.NotFound) {
771 wfe.sendError(response, logEvent, web.ProblemDetailsForError(err, "failed check for existing account"), err)
772 return
773 }
774
775
776
777
778 if accountCreateRequest.OnlyReturnExisting {
779 wfe.sendError(response, logEvent, probs.AccountDoesNotExist(
780 "No account exists with the provided key"), nil)
781 return
782 }
783
784 if !accountCreateRequest.TermsOfServiceAgreed {
785 wfe.sendError(response, logEvent, probs.Malformed("must agree to terms of service"), nil)
786 return
787 }
788
789 ip, err := extractRequesterIP(request)
790 if err != nil {
791 wfe.sendError(
792 response,
793 logEvent,
794 probs.ServerInternal("couldn't parse the remote (that is, the client's) address"),
795 fmt.Errorf("Couldn't parse RemoteAddr: %s", request.RemoteAddr),
796 )
797 return
798 }
799
800
801 ipBytes, err := ip.MarshalText()
802 if err != nil {
803 wfe.sendError(response, logEvent,
804 web.ProblemDetailsForError(err, "Error creating new account"), err)
805 return
806 }
807 var contacts []string
808 var contactsPresent bool
809 if accountCreateRequest.Contact != nil {
810 contactsPresent = true
811 contacts = *accountCreateRequest.Contact
812 }
813
814
815 reg := corepb.Registration{
816 Contact: contacts,
817 ContactsPresent: contactsPresent,
818 Agreement: wfe.SubscriberAgreementURL,
819 Key: keyBytes,
820 InitialIP: ipBytes,
821 }
822
823
824
825 go wfe.checkNewAccountLimits(ctx, ip)
826 var newRegistrationSuccessful bool
827 defer func() {
828 if !newRegistrationSuccessful {
829 go wfe.refundNewAccountLimits(ctx, ip)
830 }
831 }()
832
833
834 acctPB, err := wfe.ra.NewRegistration(ctx, ®)
835 if err != nil {
836 if errors.Is(err, berrors.Duplicate) {
837 existingAcct, err := wfe.sa.GetRegistrationByKey(ctx, &sapb.JSONWebKey{Jwk: keyBytes})
838 if err == nil {
839 returnExistingAcct(existingAcct)
840 return
841 }
842
843
844 wfe.sendError(response, logEvent, web.ProblemDetailsForError(err, "checking for existing account"), err)
845 return
846 }
847 wfe.sendError(response, logEvent,
848 web.ProblemDetailsForError(err, "Error creating new account"), err)
849 return
850 }
851
852 registrationValid := func(reg *corepb.Registration) bool {
853 return !(len(reg.Key) == 0 || len(reg.InitialIP) == 0) && reg.Id != 0
854 }
855
856 if acctPB == nil || !registrationValid(acctPB) {
857 wfe.sendError(response, logEvent,
858 web.ProblemDetailsForError(err, "Error creating new account"), err)
859 return
860 }
861 acct, err := bgrpc.PbToRegistration(acctPB)
862 if err != nil {
863 wfe.sendError(response, logEvent,
864 web.ProblemDetailsForError(err, "Error creating new account"), err)
865 return
866 }
867 logEvent.Requester = acct.ID
868 addRequesterHeader(response, acct.ID)
869
870 acctURL := web.RelativeEndpoint(request, fmt.Sprintf("%s%d", acctPath, acct.ID))
871
872 response.Header().Add("Location", acctURL)
873 if len(wfe.SubscriberAgreementURL) > 0 {
874 response.Header().Add("Link", link(wfe.SubscriberAgreementURL, "terms-of-service"))
875 }
876
877 prepAccountForDisplay(&acct)
878
879 err = wfe.writeJsonResponse(response, logEvent, http.StatusCreated, acct)
880 if err != nil {
881
882
883 wfe.sendError(response, logEvent, probs.ServerInternal("Error marshaling account"), err)
884 return
885 }
886 newRegistrationSuccessful = true
887 }
888
889
890
891
892
893
894 func (wfe *WebFrontEndImpl) parseRevocation(
895 jwsBody []byte, logEvent *web.RequestEvent) (*x509.Certificate, revocation.Reason, *probs.ProblemDetails) {
896
897 var revokeRequest struct {
898 CertificateDER core.JSONBuffer `json:"certificate"`
899 Reason *revocation.Reason `json:"reason"`
900 }
901 err := json.Unmarshal(jwsBody, &revokeRequest)
902 if err != nil {
903 return nil, 0, probs.Malformed("Unable to JSON parse revoke request")
904 }
905
906
907 parsedCertificate, err := x509.ParseCertificate(revokeRequest.CertificateDER)
908 if err != nil {
909 return nil, 0, probs.Malformed("Unable to parse certificate DER")
910 }
911
912
913 serial := core.SerialToString(parsedCertificate.SerialNumber)
914 logEvent.Extra["CertificateSerial"] = serial
915 if revokeRequest.Reason != nil {
916 logEvent.Extra["RevocationReason"] = *revokeRequest.Reason
917 }
918
919
920
921 issuerNameID := issuance.GetIssuerNameID(parsedCertificate)
922 issuerCert, ok := wfe.issuerCertificates[issuerNameID]
923 if !ok || issuerCert == nil {
924 return nil, 0, probs.NotFound("Certificate from unrecognized issuer")
925 }
926 err = parsedCertificate.CheckSignatureFrom(issuerCert.Certificate)
927 if err != nil {
928 return nil, 0, probs.NotFound("No such certificate")
929 }
930 logEvent.DNSNames = parsedCertificate.DNSNames
931
932 if parsedCertificate.NotAfter.Before(wfe.clk.Now()) {
933 return nil, 0, probs.Unauthorized("Certificate is expired")
934 }
935
936
937 reason := revocation.Reason(0)
938 if revokeRequest.Reason != nil {
939 if _, present := revocation.UserAllowedReasons[*revokeRequest.Reason]; !present {
940 reasonStr, ok := revocation.ReasonToString[*revokeRequest.Reason]
941 if !ok {
942 reasonStr = "unknown"
943 }
944 return nil, 0, probs.BadRevocationReason(
945 "unsupported revocation reason code provided: %s (%d). Supported reasons: %s",
946 reasonStr,
947 *revokeRequest.Reason,
948 revocation.UserAllowedReasonsMessage)
949 }
950 reason = *revokeRequest.Reason
951 }
952
953 return parsedCertificate, reason, nil
954 }
955
956 type revocationEvidence struct {
957 Serial string
958 Reason revocation.Reason
959 RegID int64
960 Method string
961 }
962
963
964
965 func (wfe *WebFrontEndImpl) revokeCertBySubscriberKey(
966 ctx context.Context,
967 outerJWS *bJSONWebSignature,
968 request *http.Request,
969 logEvent *web.RequestEvent) error {
970
971
972 jwsBody, _, acct, prob := wfe.validJWSForAccount(outerJWS, request, ctx, logEvent)
973 if prob != nil {
974 return prob
975 }
976
977 cert, reason, prob := wfe.parseRevocation(jwsBody, logEvent)
978 if prob != nil {
979 return prob
980 }
981
982 wfe.log.AuditObject("Authenticated revocation", revocationEvidence{
983 Serial: core.SerialToString(cert.SerialNumber),
984 Reason: reason,
985 RegID: acct.ID,
986 Method: "applicant",
987 })
988
989
990
991
992 _, err := wfe.ra.RevokeCertByApplicant(ctx, &rapb.RevokeCertByApplicantRequest{
993 Cert: cert.Raw,
994 Code: int64(reason),
995 RegID: acct.ID,
996 })
997 if err != nil {
998 return err
999 }
1000
1001 return nil
1002 }
1003
1004
1005
1006
1007
1008 func (wfe *WebFrontEndImpl) revokeCertByCertKey(
1009 ctx context.Context,
1010 outerJWS *bJSONWebSignature,
1011 request *http.Request,
1012 logEvent *web.RequestEvent) error {
1013
1014
1015
1016
1017 jwsBody, jwk, prob := wfe.validSelfAuthenticatedJWS(ctx, outerJWS, request)
1018 if prob != nil {
1019 return prob
1020 }
1021
1022 cert, reason, prob := wfe.parseRevocation(jwsBody, logEvent)
1023 if prob != nil {
1024 return prob
1025 }
1026
1027
1028
1029
1030 if !core.KeyDigestEquals(jwk, cert.PublicKey) {
1031 return probs.Unauthorized(
1032 "JWK embedded in revocation request must be the same public key as the cert to be revoked")
1033 }
1034
1035 wfe.log.AuditObject("Authenticated revocation", revocationEvidence{
1036 Serial: core.SerialToString(cert.SerialNumber),
1037 Reason: reason,
1038 RegID: 0,
1039 Method: "privkey",
1040 })
1041
1042
1043
1044 _, err := wfe.ra.RevokeCertByKey(ctx, &rapb.RevokeCertByKeyRequest{
1045 Cert: cert.Raw,
1046 })
1047 if err != nil {
1048 return err
1049 }
1050
1051 return nil
1052 }
1053
1054
1055
1056
1057 func (wfe *WebFrontEndImpl) RevokeCertificate(
1058 ctx context.Context,
1059 logEvent *web.RequestEvent,
1060 response http.ResponseWriter,
1061 request *http.Request) {
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071 jws, prob := wfe.parseJWSRequest(request)
1072 if prob != nil {
1073 wfe.sendError(response, logEvent, prob, nil)
1074 return
1075 }
1076
1077
1078 authType, prob := checkJWSAuthType(jws.Signatures[0].Header)
1079 if prob != nil {
1080 wfe.sendError(response, logEvent, prob, nil)
1081 return
1082 }
1083
1084
1085
1086 var err error
1087 switch authType {
1088 case embeddedKeyID:
1089 err = wfe.revokeCertBySubscriberKey(ctx, jws, request, logEvent)
1090 case embeddedJWK:
1091 err = wfe.revokeCertByCertKey(ctx, jws, request, logEvent)
1092 default:
1093 err = berrors.MalformedError("Malformed JWS, no KeyID or embedded JWK")
1094 }
1095 if err != nil {
1096 wfe.sendError(response, logEvent, web.ProblemDetailsForError(err, "unable to revoke"), nil)
1097 return
1098 }
1099
1100 response.WriteHeader(http.StatusOK)
1101 }
1102
1103
1104
1105 func (wfe *WebFrontEndImpl) Challenge(
1106 ctx context.Context,
1107 logEvent *web.RequestEvent,
1108 response http.ResponseWriter,
1109 request *http.Request) {
1110 notFound := func() {
1111 wfe.sendError(response, logEvent, probs.NotFound("No such challenge"), nil)
1112 }
1113 slug := strings.Split(request.URL.Path, "/")
1114 if len(slug) != 2 {
1115 notFound()
1116 return
1117 }
1118 authorizationID, err := strconv.ParseInt(slug[0], 10, 64)
1119 if err != nil {
1120 wfe.sendError(response, logEvent, probs.Malformed("Invalid authorization ID"), nil)
1121 return
1122 }
1123 challengeID := slug[1]
1124 authzPB, err := wfe.sa.GetAuthorization2(ctx, &sapb.AuthorizationID2{Id: authorizationID})
1125 if err != nil {
1126 if errors.Is(err, berrors.NotFound) {
1127 notFound()
1128 } else {
1129 wfe.sendError(response, logEvent, web.ProblemDetailsForError(err, "Problem getting authorization"), err)
1130 }
1131 return
1132 }
1133
1134
1135 if authzPB.Id == "" || authzPB.Identifier == "" || authzPB.Status == "" || authzPB.ExpiresNS == 0 {
1136 wfe.sendError(response, logEvent, probs.ServerInternal("Problem getting authorization"), errIncompleteGRPCResponse)
1137 return
1138 }
1139
1140 authz, err := bgrpc.PBToAuthz(authzPB)
1141 if err != nil {
1142 wfe.sendError(response, logEvent, probs.ServerInternal("Problem getting authorization"), err)
1143 return
1144 }
1145 challengeIndex := authz.FindChallengeByStringID(challengeID)
1146 if challengeIndex == -1 {
1147 notFound()
1148 return
1149 }
1150
1151 if authz.Expires == nil || authz.Expires.Before(wfe.clk.Now()) {
1152 wfe.sendError(response, logEvent, probs.NotFound("Expired authorization"), nil)
1153 return
1154 }
1155
1156 if requiredStale(request, logEvent) {
1157 if prob := wfe.staleEnoughToGETAuthz(authzPB); prob != nil {
1158 wfe.sendError(response, logEvent, prob, nil)
1159 return
1160 }
1161 }
1162
1163 if authz.Identifier.Type == identifier.DNS {
1164 logEvent.DNSName = authz.Identifier.Value
1165 }
1166 logEvent.Status = string(authz.Status)
1167
1168 challenge := authz.Challenges[challengeIndex]
1169 switch request.Method {
1170 case "GET", "HEAD":
1171 wfe.getChallenge(response, request, authz, &challenge, logEvent)
1172
1173 case "POST":
1174 logEvent.ChallengeType = string(challenge.Type)
1175 wfe.postChallenge(ctx, response, request, authz, challengeIndex, logEvent)
1176 }
1177 }
1178
1179
1180
1181
1182
1183 func prepAccountForDisplay(acct *core.Registration) {
1184
1185
1186 acct.ID = 0
1187
1188
1189
1190
1191
1192
1193
1194 acct.Agreement = ""
1195 }
1196
1197
1198
1199 func (wfe *WebFrontEndImpl) prepChallengeForDisplay(request *http.Request, authz core.Authorization, challenge *core.Challenge) {
1200
1201 challenge.URL = web.RelativeEndpoint(request, fmt.Sprintf("%s%s/%s", challengePath, authz.ID, challenge.StringID()))
1202
1203
1204
1205 challenge.URI = ""
1206
1207
1208 challenge.ProvidedKeyAuthorization = ""
1209
1210
1211
1212
1213 if challenge.Error != nil {
1214 challenge.Error.Type = probs.ErrorNS + challenge.Error.Type
1215 }
1216
1217
1218
1219 if authz.Status == core.StatusInvalid {
1220 challenge.Status = authz.Status
1221 }
1222 }
1223
1224
1225
1226
1227 func (wfe *WebFrontEndImpl) prepAuthorizationForDisplay(request *http.Request, authz *core.Authorization) {
1228 for i := range authz.Challenges {
1229 wfe.prepChallengeForDisplay(request, *authz, &authz.Challenges[i])
1230 }
1231 authz.ID = ""
1232 authz.RegistrationID = 0
1233
1234
1235
1236
1237
1238
1239 if strings.HasPrefix(authz.Identifier.Value, "*.") {
1240 authz.Identifier.Value = strings.TrimPrefix(authz.Identifier.Value, "*.")
1241
1242
1243 authz.Wildcard = true
1244 }
1245 }
1246
1247 func (wfe *WebFrontEndImpl) getChallenge(
1248 response http.ResponseWriter,
1249 request *http.Request,
1250 authz core.Authorization,
1251 challenge *core.Challenge,
1252 logEvent *web.RequestEvent) {
1253
1254 wfe.prepChallengeForDisplay(request, authz, challenge)
1255
1256 authzURL := urlForAuthz(authz, request)
1257 response.Header().Add("Location", challenge.URL)
1258 response.Header().Add("Link", link(authzURL, "up"))
1259
1260 err := wfe.writeJsonResponse(response, logEvent, http.StatusOK, challenge)
1261 if err != nil {
1262
1263
1264 wfe.sendError(response, logEvent, probs.ServerInternal("Failed to marshal challenge"), err)
1265 return
1266 }
1267 }
1268
1269 func (wfe *WebFrontEndImpl) postChallenge(
1270 ctx context.Context,
1271 response http.ResponseWriter,
1272 request *http.Request,
1273 authz core.Authorization,
1274 challengeIndex int,
1275 logEvent *web.RequestEvent) {
1276 body, _, currAcct, prob := wfe.validPOSTForAccount(request, ctx, logEvent)
1277 addRequesterHeader(response, logEvent.Requester)
1278 if prob != nil {
1279
1280 wfe.sendError(response, logEvent, prob, nil)
1281 return
1282 }
1283
1284
1285
1286 if currAcct.ID != authz.RegistrationID {
1287 wfe.sendError(response,
1288 logEvent,
1289 probs.Unauthorized("User account ID doesn't match account ID in authorization"),
1290 nil,
1291 )
1292 return
1293 }
1294
1295
1296
1297 if string(body) == "" {
1298 challenge := authz.Challenges[challengeIndex]
1299 wfe.getChallenge(response, request, authz, &challenge, logEvent)
1300 return
1301 }
1302
1303
1304
1305
1306 var returnAuthz core.Authorization
1307 if authz.Status == core.StatusValid {
1308 returnAuthz = authz
1309 } else {
1310
1311
1312
1313
1314
1315
1316 var challengeUpdate struct{}
1317 err := json.Unmarshal(body, &challengeUpdate)
1318 if err != nil {
1319 wfe.sendError(response, logEvent, probs.Malformed("Error unmarshaling challenge response"), err)
1320 return
1321 }
1322
1323 authzPB, err := bgrpc.AuthzToPB(authz)
1324 if err != nil {
1325 wfe.sendError(response, logEvent, web.ProblemDetailsForError(err, "Unable to serialize authz"), err)
1326 return
1327 }
1328
1329 authzPB, err = wfe.ra.PerformValidation(ctx, &rapb.PerformValidationRequest{
1330 Authz: authzPB,
1331 ChallengeIndex: int64(challengeIndex),
1332 })
1333 if err != nil || authzPB == nil || authzPB.Id == "" || authzPB.Identifier == "" || authzPB.Status == "" || authzPB.ExpiresNS == 0 {
1334 wfe.sendError(response, logEvent, web.ProblemDetailsForError(err, "Unable to update challenge"), err)
1335 return
1336 }
1337
1338 updatedAuthz, err := bgrpc.PBToAuthz(authzPB)
1339 if err != nil {
1340 wfe.sendError(response, logEvent, web.ProblemDetailsForError(err, "Unable to deserialize authz"), err)
1341 return
1342 }
1343 returnAuthz = updatedAuthz
1344 }
1345
1346
1347 challenge := returnAuthz.Challenges[challengeIndex]
1348 wfe.prepChallengeForDisplay(request, authz, &challenge)
1349
1350 authzURL := urlForAuthz(authz, request)
1351 response.Header().Add("Location", challenge.URL)
1352 response.Header().Add("Link", link(authzURL, "up"))
1353
1354 err := wfe.writeJsonResponse(response, logEvent, http.StatusOK, challenge)
1355 if err != nil {
1356
1357 wfe.sendError(response, logEvent, probs.ServerInternal("Failed to marshal challenge"), err)
1358 return
1359 }
1360 }
1361
1362
1363 func (wfe *WebFrontEndImpl) Account(
1364 ctx context.Context,
1365 logEvent *web.RequestEvent,
1366 response http.ResponseWriter,
1367 request *http.Request) {
1368 body, _, currAcct, prob := wfe.validPOSTForAccount(request, ctx, logEvent)
1369 addRequesterHeader(response, logEvent.Requester)
1370 if prob != nil {
1371
1372 wfe.sendError(response, logEvent, prob, nil)
1373 return
1374 }
1375
1376
1377
1378 idStr := request.URL.Path
1379 id, err := strconv.ParseInt(idStr, 10, 64)
1380 if err != nil {
1381 wfe.sendError(response, logEvent, probs.Malformed("Account ID must be an integer"), err)
1382 return
1383 } else if id <= 0 {
1384 msg := fmt.Sprintf("Account ID must be a positive non-zero integer, was %d", id)
1385 wfe.sendError(response, logEvent, probs.Malformed(msg), nil)
1386 return
1387 } else if id != currAcct.ID {
1388 wfe.sendError(response, logEvent,
1389 probs.Unauthorized("Request signing key did not match account key"), nil)
1390 return
1391 }
1392
1393
1394 if string(body) != "" {
1395 currAcct, prob = wfe.updateAccount(ctx, body, currAcct)
1396 if prob != nil {
1397 wfe.sendError(response, logEvent, prob, nil)
1398 return
1399 }
1400 }
1401
1402 if len(wfe.SubscriberAgreementURL) > 0 {
1403 response.Header().Add("Link", link(wfe.SubscriberAgreementURL, "terms-of-service"))
1404 }
1405
1406 prepAccountForDisplay(currAcct)
1407
1408 err = wfe.writeJsonResponse(response, logEvent, http.StatusOK, currAcct)
1409 if err != nil {
1410
1411 wfe.sendError(response, logEvent,
1412 probs.ServerInternal("Failed to marshal account"), err)
1413 return
1414 }
1415 }
1416
1417
1418
1419
1420
1421
1422 func (wfe *WebFrontEndImpl) updateAccount(
1423 ctx context.Context,
1424 requestBody []byte,
1425 currAcct *core.Registration) (*core.Registration, *probs.ProblemDetails) {
1426
1427
1428 var accountUpdateRequest struct {
1429 Contact *[]string `json:"contact"`
1430 Status core.AcmeStatus `json:"status"`
1431 }
1432
1433 err := json.Unmarshal(requestBody, &accountUpdateRequest)
1434 if err != nil {
1435 return nil, probs.Malformed("Error unmarshaling account")
1436 }
1437
1438
1439 basePb, err := bgrpc.RegistrationToPB(*currAcct)
1440 if err != nil {
1441 return nil, probs.ServerInternal("Error updating account")
1442 }
1443
1444 var contacts []string
1445 var contactsPresent bool
1446 if accountUpdateRequest.Contact != nil {
1447 contactsPresent = true
1448 contacts = *accountUpdateRequest.Contact
1449 }
1450
1451
1452
1453
1454 updatePb := &corepb.Registration{
1455 Contact: contacts,
1456 ContactsPresent: contactsPresent,
1457 Status: string(accountUpdateRequest.Status),
1458 }
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468 if updatePb.Status != "" && updatePb.Status != basePb.Status {
1469 if updatePb.Status != string(core.StatusDeactivated) {
1470 return nil, probs.Malformed("Invalid value provided for status field")
1471 }
1472 _, err := wfe.ra.DeactivateRegistration(ctx, basePb)
1473 if err != nil {
1474 return nil, web.ProblemDetailsForError(err, "Unable to deactivate account")
1475 }
1476 currAcct.Status = core.StatusDeactivated
1477 return currAcct, nil
1478 }
1479
1480
1481
1482
1483
1484
1485 updatePb.Key = basePb.Key
1486
1487 updatedAcct, err := wfe.ra.UpdateRegistration(ctx, &rapb.UpdateRegistrationRequest{Base: basePb, Update: updatePb})
1488 if err != nil {
1489 return nil, web.ProblemDetailsForError(err, "Unable to update account")
1490 }
1491
1492
1493 updatedReg, err := bgrpc.PbToRegistration(updatedAcct)
1494 if err != nil {
1495 return nil, probs.ServerInternal("Error updating account")
1496 }
1497
1498 return &updatedReg, nil
1499 }
1500
1501
1502
1503
1504
1505
1506 func (wfe *WebFrontEndImpl) deactivateAuthorization(
1507 ctx context.Context,
1508 authzPB *corepb.Authorization,
1509 logEvent *web.RequestEvent,
1510 response http.ResponseWriter,
1511 body []byte) bool {
1512 var req struct {
1513 Status core.AcmeStatus
1514 }
1515 err := json.Unmarshal(body, &req)
1516 if err != nil {
1517 wfe.sendError(response, logEvent, probs.Malformed("Error unmarshaling JSON"), err)
1518 return false
1519 }
1520 if req.Status != core.StatusDeactivated {
1521 wfe.sendError(response, logEvent, probs.Malformed("Invalid status value"), err)
1522 return false
1523 }
1524 _, err = wfe.ra.DeactivateAuthorization(ctx, authzPB)
1525 if err != nil {
1526 wfe.sendError(response, logEvent, web.ProblemDetailsForError(err, "Error deactivating authorization"), err)
1527 return false
1528 }
1529
1530
1531
1532 authzPB.Status = string(core.StatusDeactivated)
1533 return true
1534 }
1535
1536 func (wfe *WebFrontEndImpl) Authorization(
1537 ctx context.Context,
1538 logEvent *web.RequestEvent,
1539 response http.ResponseWriter,
1540 request *http.Request) {
1541 var requestAccount *core.Registration
1542 var requestBody []byte
1543
1544
1545
1546 if request.Method == "POST" {
1547
1548 body, _, acct, prob := wfe.validPOSTForAccount(request, ctx, logEvent)
1549 addRequesterHeader(response, logEvent.Requester)
1550 if prob != nil {
1551 wfe.sendError(response, logEvent, prob, nil)
1552 return
1553 }
1554 requestAccount = acct
1555 requestBody = body
1556 }
1557
1558 authzID, err := strconv.ParseInt(request.URL.Path, 10, 64)
1559 if err != nil {
1560 wfe.sendError(response, logEvent, probs.Malformed("Invalid authorization ID"), nil)
1561 return
1562 }
1563
1564 authzPB, err := wfe.sa.GetAuthorization2(ctx, &sapb.AuthorizationID2{Id: authzID})
1565 if errors.Is(err, berrors.NotFound) {
1566 wfe.sendError(response, logEvent, probs.NotFound("No such authorization"), nil)
1567 return
1568 } else if errors.Is(err, berrors.Malformed) {
1569 wfe.sendError(response, logEvent, probs.Malformed(err.Error()), nil)
1570 return
1571 } else if err != nil {
1572 wfe.sendError(response, logEvent, web.ProblemDetailsForError(err, "Problem getting authorization"), err)
1573 return
1574 }
1575
1576
1577 if authzPB.Id == "" || authzPB.Identifier == "" || authzPB.Status == "" || authzPB.ExpiresNS == 0 {
1578 wfe.sendError(response, logEvent, probs.ServerInternal("Problem getting authorization"), errIncompleteGRPCResponse)
1579 return
1580 }
1581
1582 if identifier.IdentifierType(authzPB.Identifier) == identifier.DNS {
1583 logEvent.DNSName = authzPB.Identifier
1584 }
1585 logEvent.Status = authzPB.Status
1586
1587
1588 if time.Unix(0, authzPB.ExpiresNS).Before(wfe.clk.Now()) {
1589 wfe.sendError(response, logEvent, probs.NotFound("Expired authorization"), nil)
1590 return
1591 }
1592
1593 if requiredStale(request, logEvent) {
1594 if prob := wfe.staleEnoughToGETAuthz(authzPB); prob != nil {
1595 wfe.sendError(response, logEvent, prob, nil)
1596 return
1597 }
1598 }
1599
1600
1601
1602
1603 if requestAccount != nil && requestAccount.ID != authzPB.RegistrationID {
1604 wfe.sendError(response, logEvent,
1605 probs.Unauthorized("Account ID doesn't match ID for authorization"), nil)
1606 return
1607 }
1608
1609
1610
1611 if string(requestBody) != "" {
1612
1613
1614
1615 if !wfe.deactivateAuthorization(ctx, authzPB, logEvent, response, requestBody) {
1616 return
1617 }
1618 }
1619
1620 authz, err := bgrpc.PBToAuthz(authzPB)
1621 if err != nil {
1622 wfe.sendError(response, logEvent, probs.ServerInternal("Problem getting authorization"), err)
1623 return
1624 }
1625
1626 wfe.prepAuthorizationForDisplay(request, &authz)
1627
1628 err = wfe.writeJsonResponse(response, logEvent, http.StatusOK, authz)
1629 if err != nil {
1630
1631 wfe.sendError(response, logEvent, probs.ServerInternal("Failed to JSON marshal authz"), err)
1632 return
1633 }
1634 }
1635
1636
1637
1638 func (wfe *WebFrontEndImpl) Certificate(ctx context.Context, logEvent *web.RequestEvent, response http.ResponseWriter, request *http.Request) {
1639 var requesterAccount *core.Registration
1640
1641
1642 if request.Method == "POST" {
1643 acct, prob := wfe.validPOSTAsGETForAccount(request, ctx, logEvent)
1644 if prob != nil {
1645 wfe.sendError(response, logEvent, prob, nil)
1646 return
1647 }
1648 requesterAccount = acct
1649 }
1650
1651 requestedChain := 0
1652 serial := request.URL.Path
1653
1654
1655
1656
1657 serialAndChain := strings.SplitN(serial, "/", 2)
1658 if len(serialAndChain) == 2 {
1659 idx, err := strconv.Atoi(serialAndChain[1])
1660 if err != nil || idx < 0 {
1661 wfe.sendError(response, logEvent, probs.Malformed("Chain ID must be a non-negative integer"),
1662 fmt.Errorf("certificate chain id provided was not valid: %s", serialAndChain[1]))
1663 return
1664 }
1665 serial = serialAndChain[0]
1666 requestedChain = idx
1667 }
1668
1669
1670
1671 if !core.ValidSerial(serial) {
1672 wfe.sendError(
1673 response,
1674 logEvent,
1675 probs.NotFound("Certificate not found"),
1676 fmt.Errorf("certificate serial provided was not valid: %s", serial),
1677 )
1678 return
1679 }
1680 logEvent.Extra["RequestedSerial"] = serial
1681
1682 cert, err := wfe.sa.GetCertificate(ctx, &sapb.Serial{Serial: serial})
1683 if err != nil {
1684 if errors.Is(err, berrors.NotFound) {
1685 wfe.sendError(response, logEvent, probs.NotFound("Certificate not found"), nil)
1686 } else {
1687 wfe.sendError(response, logEvent, web.ProblemDetailsForError(err, "Failed to retrieve certificate"), err)
1688 }
1689 return
1690 }
1691
1692 if requiredStale(request, logEvent) {
1693 if prob := wfe.staleEnoughToGETCert(cert); prob != nil {
1694 wfe.sendError(response, logEvent, prob, nil)
1695 return
1696 }
1697 }
1698
1699
1700
1701
1702 if requesterAccount != nil && requesterAccount.ID != cert.RegistrationID {
1703 wfe.sendError(response, logEvent, probs.Unauthorized("Account in use did not issue specified certificate"), nil)
1704 return
1705 }
1706
1707 responsePEM, prob := func() ([]byte, *probs.ProblemDetails) {
1708 leafPEM := pem.EncodeToMemory(&pem.Block{
1709 Type: "CERTIFICATE",
1710 Bytes: cert.Der,
1711 })
1712
1713 parsedCert, err := x509.ParseCertificate(cert.Der)
1714 if err != nil {
1715
1716 return nil, probs.ServerInternal(
1717 fmt.Sprintf(
1718 "unable to parse Boulder issued certificate with serial %#v: %s",
1719 serial,
1720 err),
1721 )
1722 }
1723
1724 issuerNameID := issuance.GetIssuerNameID(parsedCert)
1725 availableChains, ok := wfe.certificateChains[issuerNameID]
1726 if !ok || len(availableChains) == 0 {
1727
1728
1729
1730
1731 if parsedCert.NotAfter.Before(wfe.clk.Now()) {
1732 return leafPEM, nil
1733 }
1734 return nil, probs.ServerInternal(
1735 fmt.Sprintf(
1736 "Certificate serial %#v has an unknown IssuerNameID %d - no PEM certificate chain associated.",
1737 serial,
1738 issuerNameID),
1739 )
1740 }
1741
1742
1743
1744 if requestedChain < 0 || requestedChain >= len(availableChains) {
1745 return nil, probs.NotFound("Unknown issuance chain")
1746 }
1747
1748
1749 err = parsedCert.CheckSignatureFrom(wfe.issuerCertificates[issuerNameID].Certificate)
1750 if err != nil {
1751 return nil, probs.ServerInternal(
1752 fmt.Sprintf(
1753 "Certificate serial %#v has a signature which cannot be verified from issuer %d.",
1754 serial,
1755 issuerNameID),
1756 )
1757 }
1758
1759
1760
1761 for chainID := range availableChains {
1762 if chainID == requestedChain {
1763 continue
1764 }
1765 chainURL := web.RelativeEndpoint(request,
1766 fmt.Sprintf("%s%s/%d", certPath, serial, chainID))
1767 response.Header().Add("Link", link(chainURL, "alternate"))
1768 }
1769
1770
1771 return append(leafPEM, availableChains[requestedChain]...), nil
1772 }()
1773 if prob != nil {
1774 wfe.sendError(response, logEvent, prob, nil)
1775 return
1776 }
1777
1778
1779
1780
1781
1782
1783 response.Header().Set("Content-Length", strconv.Itoa(len(responsePEM)))
1784 response.Header().Set("Content-Type", "application/pem-certificate-chain")
1785 response.WriteHeader(http.StatusOK)
1786 if _, err = response.Write(responsePEM); err != nil {
1787 wfe.log.Warningf("Could not write response: %s", err)
1788 }
1789 }
1790
1791
1792 func (wfe *WebFrontEndImpl) BuildID(ctx context.Context, logEvent *web.RequestEvent, response http.ResponseWriter, request *http.Request) {
1793 response.Header().Set("Content-Type", "text/plain")
1794 response.WriteHeader(http.StatusOK)
1795 detailsString := fmt.Sprintf("Boulder=(%s %s)", core.GetBuildID(), core.GetBuildTime())
1796 if _, err := fmt.Fprintln(response, detailsString); err != nil {
1797 wfe.log.Warningf("Could not write response: %s", err)
1798 }
1799 }
1800
1801
1802 func (wfe *WebFrontEndImpl) Options(response http.ResponseWriter, request *http.Request, methodsStr string, methodsMap map[string]bool) {
1803
1804 response.Header().Set("Allow", methodsStr)
1805
1806
1807
1808 reqMethod := request.Header.Get("Access-Control-Request-Method")
1809 if reqMethod == "" {
1810 reqMethod = "GET"
1811 }
1812 if methodsMap[reqMethod] {
1813 wfe.setCORSHeaders(response, request, methodsStr)
1814 }
1815 }
1816
1817
1818
1819
1820
1821 func (wfe *WebFrontEndImpl) setCORSHeaders(response http.ResponseWriter, request *http.Request, allowMethods string) {
1822 reqOrigin := request.Header.Get("Origin")
1823 if reqOrigin == "" {
1824
1825 return
1826 }
1827
1828
1829
1830
1831 allow := false
1832 for _, ao := range wfe.AllowOrigins {
1833 if ao == "*" {
1834 response.Header().Set("Access-Control-Allow-Origin", "*")
1835 allow = true
1836 break
1837 } else if ao == reqOrigin {
1838 response.Header().Set("Vary", "Origin")
1839 response.Header().Set("Access-Control-Allow-Origin", ao)
1840 allow = true
1841 break
1842 }
1843 }
1844 if !allow {
1845 return
1846 }
1847
1848 if allowMethods != "" {
1849
1850 response.Header().Set("Access-Control-Allow-Methods", allowMethods)
1851 }
1852
1853
1854
1855
1856
1857
1858
1859 response.Header().Set("Access-Control-Allow-Headers", "Content-Type")
1860 response.Header().Set("Access-Control-Expose-Headers", "Link, Replay-Nonce, Location")
1861 response.Header().Set("Access-Control-Max-Age", "86400")
1862 }
1863
1864
1865 func (wfe *WebFrontEndImpl) KeyRollover(
1866 ctx context.Context,
1867 logEvent *web.RequestEvent,
1868 response http.ResponseWriter,
1869 request *http.Request) {
1870
1871
1872 outerBody, outerJWS, acct, prob := wfe.validPOSTForAccount(request, ctx, logEvent)
1873 addRequesterHeader(response, logEvent.Requester)
1874 if prob != nil {
1875 wfe.sendError(response, logEvent, prob, nil)
1876 return
1877 }
1878 oldKey := acct.Key
1879
1880
1881 innerJWS, prob := wfe.parseJWS(outerBody)
1882 if prob != nil {
1883 wfe.sendError(response, logEvent, prob, nil)
1884 return
1885 }
1886
1887
1888 rolloverOperation, prob := wfe.validKeyRollover(ctx, outerJWS, innerJWS, oldKey)
1889 if prob != nil {
1890 wfe.sendError(response, logEvent, prob, nil)
1891 return
1892 }
1893 newKey := rolloverOperation.NewKey
1894
1895
1896
1897 header := outerJWS.Signatures[0].Header
1898 if rolloverOperation.Account != header.KeyID {
1899 wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "KeyRolloverMismatchedAccount"}).Inc()
1900 wfe.sendError(response, logEvent, probs.Malformed(
1901 fmt.Sprintf("Inner key rollover request specified Account %q, but outer JWS has Key ID %q",
1902 rolloverOperation.Account, header.KeyID)), nil)
1903 return
1904 }
1905
1906
1907
1908
1909
1910
1911 keysEqual, err := core.PublicKeysEqual(newKey.Key, oldKey.Key)
1912 if err != nil {
1913
1914 wfe.sendError(response, logEvent, probs.ServerInternal("Unable to compare new and old keys"), err)
1915 return
1916 }
1917 if keysEqual {
1918 wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "KeyRolloverUnchangedKey"}).Inc()
1919 wfe.sendError(response, logEvent, probs.Malformed(
1920 "New key specified by rollover request is the same as the old key"), nil)
1921 return
1922 }
1923
1924
1925 newKeyBytes, err := newKey.MarshalJSON()
1926 if err != nil {
1927 wfe.sendError(response, logEvent, probs.ServerInternal("Error marshaling new key"), err)
1928 }
1929
1930 existingAcct, err := wfe.sa.GetRegistrationByKey(ctx, &sapb.JSONWebKey{Jwk: newKeyBytes})
1931 if err == nil {
1932 response.Header().Set("Location",
1933 web.RelativeEndpoint(request, fmt.Sprintf("%s%d", acctPath, existingAcct.Id)))
1934 wfe.sendError(response, logEvent,
1935 probs.Conflict("New key is already in use for a different account"), err)
1936 return
1937 } else if !errors.Is(err, berrors.NotFound) {
1938 wfe.sendError(response, logEvent, web.ProblemDetailsForError(err, "Failed to lookup existing keys"), err)
1939 return
1940 }
1941
1942 regPb, err := bgrpc.RegistrationToPB(*acct)
1943 if err != nil {
1944 wfe.sendError(response, logEvent, probs.ServerInternal("Error marshaling Registration to proto"), err)
1945 return
1946 }
1947
1948
1949 updatePb := &corepb.Registration{Key: newKeyBytes}
1950
1951
1952 updatedAcctPb, err := wfe.ra.UpdateRegistration(ctx, &rapb.UpdateRegistrationRequest{Base: regPb, Update: updatePb})
1953 if err != nil {
1954 if errors.Is(err, berrors.Duplicate) {
1955
1956
1957
1958
1959 existingAcct, err := wfe.sa.GetRegistrationByKey(ctx, &sapb.JSONWebKey{Jwk: newKeyBytes})
1960 if err != nil {
1961 wfe.sendError(response, logEvent, web.ProblemDetailsForError(err, "looking up account by key"), err)
1962 return
1963 }
1964 response.Header().Set("Location",
1965 web.RelativeEndpoint(request, fmt.Sprintf("%s%d", acctPath, existingAcct.Id)))
1966 wfe.sendError(response, logEvent,
1967 probs.Conflict("New key is already in use for a different account"), err)
1968 return
1969 }
1970 wfe.sendError(response, logEvent,
1971 web.ProblemDetailsForError(err, "Unable to update account with new key"), err)
1972 return
1973 }
1974
1975 updatedAcct, err := bgrpc.PbToRegistration(updatedAcctPb)
1976 if err != nil {
1977 wfe.sendError(response, logEvent, probs.ServerInternal("Error marshaling proto to registration"), err)
1978 return
1979 }
1980 prepAccountForDisplay(&updatedAcct)
1981
1982 err = wfe.writeJsonResponse(response, logEvent, http.StatusOK, updatedAcct)
1983 if err != nil {
1984 wfe.sendError(response, logEvent, probs.ServerInternal("Failed to marshal updated account"), err)
1985 }
1986 }
1987
1988 type orderJSON struct {
1989 Status core.AcmeStatus `json:"status"`
1990 Expires time.Time `json:"expires"`
1991 Identifiers []identifier.ACMEIdentifier `json:"identifiers"`
1992 Authorizations []string `json:"authorizations"`
1993 Finalize string `json:"finalize"`
1994 Certificate string `json:"certificate,omitempty"`
1995 Error *probs.ProblemDetails `json:"error,omitempty"`
1996 }
1997
1998
1999
2000
2001
2002 func (wfe *WebFrontEndImpl) orderToOrderJSON(request *http.Request, order *corepb.Order) orderJSON {
2003 idents := make([]identifier.ACMEIdentifier, len(order.Names))
2004 for i, name := range order.Names {
2005 idents[i] = identifier.ACMEIdentifier{Type: identifier.DNS, Value: name}
2006 }
2007 finalizeURL := web.RelativeEndpoint(request,
2008 fmt.Sprintf("%s%d/%d", finalizeOrderPath, order.RegistrationID, order.Id))
2009 respObj := orderJSON{
2010 Status: core.AcmeStatus(order.Status),
2011 Expires: time.Unix(0, order.ExpiresNS).UTC(),
2012 Identifiers: idents,
2013 Finalize: finalizeURL,
2014 }
2015
2016 if order.Error != nil {
2017 prob, err := bgrpc.PBToProblemDetails(order.Error)
2018 if err != nil {
2019 wfe.log.AuditErrf("Internal error converting order ID %d "+
2020 "proto buf prob to problem details: %q", order.Id, err)
2021 }
2022 respObj.Error = prob
2023 respObj.Error.Type = probs.ErrorNS + respObj.Error.Type
2024 }
2025 for _, v2ID := range order.V2Authorizations {
2026 respObj.Authorizations = append(respObj.Authorizations, web.RelativeEndpoint(request, fmt.Sprintf("%s%d", authzPath, v2ID)))
2027 }
2028 if respObj.Status == core.StatusValid {
2029 certURL := web.RelativeEndpoint(request,
2030 fmt.Sprintf("%s%s", certPath, order.CertificateSerial))
2031 respObj.Certificate = certURL
2032 }
2033 return respObj
2034 }
2035
2036
2037
2038 func (wfe *WebFrontEndImpl) NewOrder(
2039 ctx context.Context,
2040 logEvent *web.RequestEvent,
2041 response http.ResponseWriter,
2042 request *http.Request) {
2043 body, _, acct, prob := wfe.validPOSTForAccount(request, ctx, logEvent)
2044 addRequesterHeader(response, logEvent.Requester)
2045 if prob != nil {
2046
2047 wfe.sendError(response, logEvent, prob, nil)
2048 return
2049 }
2050
2051
2052
2053
2054 var newOrderRequest struct {
2055 Identifiers []identifier.ACMEIdentifier `json:"identifiers"`
2056 NotBefore, NotAfter string
2057 }
2058 err := json.Unmarshal(body, &newOrderRequest)
2059 if err != nil {
2060 wfe.sendError(response, logEvent,
2061 probs.Malformed("Unable to unmarshal NewOrder request body"), err)
2062 return
2063 }
2064
2065 if len(newOrderRequest.Identifiers) == 0 {
2066 wfe.sendError(response, logEvent,
2067 probs.Malformed("NewOrder request did not specify any identifiers"), nil)
2068 return
2069 }
2070 if newOrderRequest.NotBefore != "" || newOrderRequest.NotAfter != "" {
2071 wfe.sendError(response, logEvent, probs.Malformed("NotBefore and NotAfter are not supported"), nil)
2072 return
2073 }
2074
2075 var hasValidCNLen bool
2076
2077
2078
2079
2080 names := make([]string, len(newOrderRequest.Identifiers))
2081 for i, ident := range newOrderRequest.Identifiers {
2082 if ident.Type != identifier.DNS {
2083 wfe.sendError(response, logEvent,
2084 probs.UnsupportedIdentifier("NewOrder request included invalid non-DNS type identifier: type %q, value %q",
2085 ident.Type, ident.Value),
2086 nil)
2087 return
2088 }
2089 if ident.Value == "" {
2090 wfe.sendError(response, logEvent, probs.Malformed("NewOrder request included empty domain name"), nil)
2091 return
2092 }
2093 names[i] = ident.Value
2094
2095
2096
2097 if len(names[i]) <= 64 {
2098 hasValidCNLen = true
2099 }
2100 }
2101 if !hasValidCNLen && features.Enabled(features.RequireCommonName) {
2102 wfe.sendError(response, logEvent,
2103 probs.RejectedIdentifier("NewOrder request did not include a SAN short enough to fit in CN"),
2104 nil)
2105 return
2106 }
2107
2108 logEvent.DNSNames = names
2109
2110 order, err := wfe.ra.NewOrder(ctx, &rapb.NewOrderRequest{
2111 RegistrationID: acct.ID,
2112 Names: names,
2113 })
2114 if err != nil || order == nil || order.Id == 0 || order.CreatedNS == 0 || order.RegistrationID == 0 || order.ExpiresNS == 0 || len(order.Names) == 0 {
2115 wfe.sendError(response, logEvent, web.ProblemDetailsForError(err, "Error creating new order"), err)
2116 return
2117 }
2118 logEvent.Created = fmt.Sprintf("%d", order.Id)
2119
2120 orderURL := web.RelativeEndpoint(request,
2121 fmt.Sprintf("%s%d/%d", orderPath, acct.ID, order.Id))
2122 response.Header().Set("Location", orderURL)
2123
2124 respObj := wfe.orderToOrderJSON(request, order)
2125 err = wfe.writeJsonResponse(response, logEvent, http.StatusCreated, respObj)
2126 if err != nil {
2127 wfe.sendError(response, logEvent, probs.ServerInternal("Error marshaling order"), err)
2128 return
2129 }
2130 }
2131
2132
2133 func (wfe *WebFrontEndImpl) GetOrder(ctx context.Context, logEvent *web.RequestEvent, response http.ResponseWriter, request *http.Request) {
2134 var requesterAccount *core.Registration
2135
2136
2137 if request.Method == http.MethodPost {
2138 acct, prob := wfe.validPOSTAsGETForAccount(request, ctx, logEvent)
2139 if prob != nil {
2140 wfe.sendError(response, logEvent, prob, nil)
2141 return
2142 }
2143 requesterAccount = acct
2144 }
2145
2146
2147 fields := strings.SplitN(request.URL.Path, "/", 2)
2148 if len(fields) != 2 {
2149 wfe.sendError(response, logEvent, probs.NotFound("Invalid request path"), nil)
2150 return
2151 }
2152 acctID, err := strconv.ParseInt(fields[0], 10, 64)
2153 if err != nil {
2154 wfe.sendError(response, logEvent, probs.Malformed("Invalid account ID"), err)
2155 return
2156 }
2157 orderID, err := strconv.ParseInt(fields[1], 10, 64)
2158 if err != nil {
2159 wfe.sendError(response, logEvent, probs.Malformed("Invalid order ID"), err)
2160 return
2161 }
2162
2163 order, err := wfe.sa.GetOrder(ctx, &sapb.OrderRequest{Id: orderID})
2164 if err != nil {
2165 if errors.Is(err, berrors.NotFound) {
2166 wfe.sendError(response, logEvent, probs.NotFound(fmt.Sprintf("No order for ID %d", orderID)), nil)
2167 return
2168 }
2169 wfe.sendError(response, logEvent, web.ProblemDetailsForError(err,
2170 fmt.Sprintf("Failed to retrieve order for ID %d", orderID)), err)
2171 return
2172 }
2173
2174 if order.Id == 0 || order.CreatedNS == 0 || order.Status == "" || order.RegistrationID == 0 || order.ExpiresNS == 0 || len(order.Names) == 0 {
2175 wfe.sendError(response, logEvent, probs.ServerInternal(fmt.Sprintf("Failed to retrieve order for ID %d", orderID)), errIncompleteGRPCResponse)
2176 return
2177 }
2178
2179 if requiredStale(request, logEvent) {
2180 if prob := wfe.staleEnoughToGETOrder(order); prob != nil {
2181 wfe.sendError(response, logEvent, prob, nil)
2182 return
2183 }
2184 }
2185
2186 if order.RegistrationID != acctID {
2187 wfe.sendError(response, logEvent, probs.NotFound(fmt.Sprintf("No order found for account ID %d", acctID)), nil)
2188 return
2189 }
2190
2191
2192
2193
2194 if requesterAccount != nil && order.RegistrationID != requesterAccount.ID {
2195 wfe.sendError(response, logEvent, probs.NotFound(fmt.Sprintf("No order found for account ID %d", acctID)), nil)
2196 return
2197 }
2198
2199 respObj := wfe.orderToOrderJSON(request, order)
2200
2201 if respObj.Status == core.StatusProcessing {
2202 response.Header().Set(headerRetryAfter, strconv.Itoa(orderRetryAfter))
2203 }
2204
2205 err = wfe.writeJsonResponse(response, logEvent, http.StatusOK, respObj)
2206 if err != nil {
2207 wfe.sendError(response, logEvent, probs.ServerInternal("Error marshaling order"), err)
2208 return
2209 }
2210 }
2211
2212
2213
2214
2215 func (wfe *WebFrontEndImpl) FinalizeOrder(ctx context.Context, logEvent *web.RequestEvent, response http.ResponseWriter, request *http.Request) {
2216
2217
2218 body, _, acct, prob := wfe.validPOSTForAccount(request, ctx, logEvent)
2219 addRequesterHeader(response, logEvent.Requester)
2220 if prob != nil {
2221 wfe.sendError(response, logEvent, prob, nil)
2222 return
2223 }
2224
2225
2226
2227 fields := strings.SplitN(request.URL.Path, "/", 2)
2228 if len(fields) != 2 {
2229 wfe.sendError(response, logEvent, probs.NotFound("Invalid request path"), nil)
2230 return
2231 }
2232 acctID, err := strconv.ParseInt(fields[0], 10, 64)
2233 if err != nil {
2234 wfe.sendError(response, logEvent, probs.Malformed("Invalid account ID"), nil)
2235 return
2236 }
2237 orderID, err := strconv.ParseInt(fields[1], 10, 64)
2238 if err != nil {
2239 wfe.sendError(response, logEvent, probs.Malformed("Invalid order ID"), nil)
2240 return
2241 }
2242
2243 order, err := wfe.sa.GetOrder(ctx, &sapb.OrderRequest{Id: orderID})
2244 if err != nil {
2245 if errors.Is(err, berrors.NotFound) {
2246 wfe.sendError(response, logEvent, probs.NotFound(fmt.Sprintf("No order for ID %d", orderID)), nil)
2247 return
2248 }
2249 wfe.sendError(response, logEvent, web.ProblemDetailsForError(err,
2250 fmt.Sprintf("Failed to retrieve order for ID %d", orderID)), err)
2251 return
2252 }
2253
2254 if order.Id == 0 || order.CreatedNS == 0 || order.Status == "" || order.RegistrationID == 0 || order.ExpiresNS == 0 || len(order.Names) == 0 {
2255 wfe.sendError(response, logEvent, probs.ServerInternal(fmt.Sprintf("Failed to retrieve order for ID %d", orderID)), errIncompleteGRPCResponse)
2256 return
2257 }
2258
2259 if order.RegistrationID != acctID {
2260 wfe.sendError(response, logEvent, probs.NotFound(fmt.Sprintf("No order found for account ID %d", acctID)), nil)
2261 return
2262 }
2263
2264
2265
2266 if acct.ID != order.RegistrationID {
2267 wfe.sendError(response, logEvent, probs.NotFound(fmt.Sprintf("No order found for account ID %d", acct.ID)), nil)
2268 return
2269 }
2270
2271
2272 if order.Status != string(core.StatusReady) {
2273 wfe.sendError(response, logEvent,
2274 probs.OrderNotReady(
2275 "Order's status (%q) is not acceptable for finalization",
2276 order.Status),
2277 nil)
2278 return
2279 }
2280
2281
2282 orderExpiry := time.Unix(order.ExpiresNS, 0)
2283 if orderExpiry.Before(wfe.clk.Now()) {
2284 wfe.sendError(response, logEvent, probs.NotFound(fmt.Sprintf("Order %d is expired", order.Id)), nil)
2285 return
2286 }
2287
2288
2289 var rawCSR core.RawCertificateRequest
2290 err = json.Unmarshal(body, &rawCSR)
2291 if err != nil {
2292 wfe.sendError(response, logEvent,
2293 probs.Malformed("Error unmarshaling finalize order request"), err)
2294 return
2295 }
2296
2297
2298 csr, err := x509.ParseCertificateRequest(rawCSR.CSR)
2299 if err != nil {
2300 wfe.sendError(response, logEvent, probs.Malformed("Error parsing certificate request: %s", err), err)
2301 return
2302 }
2303
2304 logEvent.DNSNames = order.Names
2305 logEvent.Extra["KeyType"] = web.KeyTypeToString(csr.PublicKey)
2306
2307 updatedOrder, err := wfe.ra.FinalizeOrder(ctx, &rapb.FinalizeOrderRequest{
2308 Csr: rawCSR.CSR,
2309 Order: order,
2310 })
2311 if err != nil {
2312 wfe.sendError(response, logEvent, web.ProblemDetailsForError(err, "Error finalizing order"), err)
2313 return
2314 }
2315 if updatedOrder == nil || order.Id == 0 || order.CreatedNS == 0 || order.RegistrationID == 0 || order.ExpiresNS == 0 || len(order.Names) == 0 {
2316 wfe.sendError(response, logEvent, web.ProblemDetailsForError(err, "Error validating order"), errIncompleteGRPCResponse)
2317 return
2318 }
2319
2320
2321 wfe.stats.csrSignatureAlgs.With(prometheus.Labels{"type": csr.SignatureAlgorithm.String()}).Inc()
2322
2323 orderURL := web.RelativeEndpoint(request,
2324 fmt.Sprintf("%s%d/%d", orderPath, acct.ID, updatedOrder.Id))
2325 response.Header().Set("Location", orderURL)
2326
2327 respObj := wfe.orderToOrderJSON(request, updatedOrder)
2328
2329 if respObj.Status == core.StatusProcessing {
2330 response.Header().Set(headerRetryAfter, strconv.Itoa(orderRetryAfter))
2331 }
2332
2333 err = wfe.writeJsonResponse(response, logEvent, http.StatusOK, respObj)
2334 if err != nil {
2335 wfe.sendError(response, logEvent, probs.ServerInternal("Unable to write finalize order response"), err)
2336 return
2337 }
2338 }
2339
2340
2341 type certID struct {
2342 HashAlgorithm pkix.AlgorithmIdentifier
2343 IssuerNameHash []byte
2344 IssuerKeyHash []byte
2345 SerialNumber *big.Int
2346 }
2347
2348
2349
2350 func (wfe *WebFrontEndImpl) RenewalInfo(ctx context.Context, logEvent *web.RequestEvent, response http.ResponseWriter, request *http.Request) {
2351 if !features.Enabled(features.ServeRenewalInfo) {
2352 wfe.sendError(response, logEvent, probs.NotFound("Feature not enabled"), nil)
2353 return
2354 }
2355
2356 if request.Method == http.MethodPost {
2357 wfe.UpdateRenewal(ctx, logEvent, response, request)
2358 return
2359 }
2360
2361 if len(request.URL.Path) == 0 {
2362 wfe.sendError(response, logEvent, probs.NotFound("Must specify a request path"), nil)
2363 return
2364 }
2365
2366
2367
2368 der, err := base64.RawURLEncoding.DecodeString(request.URL.Path)
2369 if err != nil {
2370 wfe.sendError(response, logEvent, probs.Malformed("Path was not base64url-encoded or had padding"), err)
2371 return
2372 }
2373
2374 var id certID
2375 rest, err := asn1.Unmarshal(der, &id)
2376 if err != nil || len(rest) != 0 {
2377 wfe.sendError(response, logEvent, probs.Malformed("Path was not a DER-encoded CertID sequence"), err)
2378 return
2379 }
2380
2381
2382 if !id.HashAlgorithm.Algorithm.Equal(asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 1}) {
2383 wfe.sendError(response, logEvent, probs.Malformed("Request used hash algorithm other than SHA-256"), err)
2384 return
2385 }
2386
2387
2388
2389 serial := core.SerialToString(id.SerialNumber)
2390 logEvent.Extra["RequestedSerial"] = serial
2391
2392 sendRI := func(ri core.RenewalInfo) {
2393 response.Header().Set(headerRetryAfter, fmt.Sprintf("%d", int(6*time.Hour/time.Second)))
2394 err = wfe.writeJsonResponse(response, logEvent, http.StatusOK, ri)
2395 if err != nil {
2396 wfe.sendError(response, logEvent, probs.ServerInternal("Error marshalling renewalInfo"), err)
2397 return
2398 }
2399 }
2400
2401
2402
2403 result, err := wfe.sa.IncidentsForSerial(ctx, &sapb.Serial{Serial: serial})
2404 if err != nil {
2405 wfe.sendError(response, logEvent, web.ProblemDetailsForError(err,
2406 "checking if certificate is impacted by an incident"), err)
2407 return
2408 }
2409
2410 if len(result.Incidents) > 0 {
2411 sendRI(core.RenewalInfoImmediate(wfe.clk.Now()))
2412 return
2413 }
2414
2415
2416
2417 status, err := wfe.sa.GetCertificateStatus(ctx, &sapb.Serial{Serial: serial})
2418 if err != nil {
2419 wfe.sendError(response, logEvent, web.ProblemDetailsForError(err,
2420 "checking if certificate has been revoked"), err)
2421 return
2422 }
2423
2424 if status.Status == string(core.OCSPStatusRevoked) {
2425 sendRI(core.RenewalInfoImmediate(wfe.clk.Now()))
2426 return
2427 }
2428
2429
2430
2431
2432 cert, err := wfe.sa.GetCertificate(ctx, &sapb.Serial{Serial: serial})
2433 if err != nil {
2434 if errors.Is(err, berrors.NotFound) {
2435 wfe.sendError(response, logEvent, probs.NotFound("Certificate not found"), nil)
2436 } else {
2437 wfe.sendError(response, logEvent, web.ProblemDetailsForError(err, "getting certificate"), err)
2438 }
2439 return
2440 }
2441
2442
2443
2444
2445
2446
2447 sendRI(core.RenewalInfoSimple(
2448 time.Unix(0, cert.IssuedNS).UTC(),
2449 time.Unix(0, cert.ExpiresNS).UTC()))
2450 }
2451
2452
2453
2454
2455 func (wfe *WebFrontEndImpl) UpdateRenewal(ctx context.Context, logEvent *web.RequestEvent, response http.ResponseWriter, request *http.Request) {
2456 if !features.Enabled(features.ServeRenewalInfo) {
2457 wfe.sendError(response, logEvent, probs.NotFound("Feature not enabled"), nil)
2458 return
2459 }
2460
2461 body, _, acct, prob := wfe.validPOSTForAccount(request, ctx, logEvent)
2462 addRequesterHeader(response, logEvent.Requester)
2463 if prob != nil {
2464
2465 wfe.sendError(response, logEvent, prob, nil)
2466 return
2467 }
2468
2469 var updateRenewalRequest struct {
2470 CertID string `json:"certID"`
2471 Replaced bool `json:"replaced"`
2472 }
2473 err := json.Unmarshal(body, &updateRenewalRequest)
2474 if err != nil {
2475 wfe.sendError(response, logEvent, probs.Malformed("Unable to unmarshal RenewalInfo POST request body"), err)
2476 return
2477 }
2478
2479 der, err := base64.RawURLEncoding.DecodeString(updateRenewalRequest.CertID)
2480 if err != nil {
2481 wfe.sendError(response, logEvent, probs.Malformed("certID was not base64url-encoded or contained padding"), err)
2482 return
2483 }
2484
2485 var id certID
2486 rest, err := asn1.Unmarshal(der, &id)
2487 if err != nil || len(rest) != 0 {
2488 wfe.sendError(response, logEvent, probs.Malformed("certID was not a DER-encoded CertID ASN.1 sequence"), err)
2489 return
2490 }
2491
2492 if !id.HashAlgorithm.Algorithm.Equal(asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 1}) {
2493 wfe.sendError(response, logEvent, probs.Malformed("Decoded CertID used a hashAlgorithm other than SHA-256"), err)
2494 return
2495 }
2496
2497
2498
2499 serial := core.SerialToString(id.SerialNumber)
2500 logEvent.Extra["RequestedSerial"] = serial
2501
2502 metadata, err := wfe.sa.GetSerialMetadata(ctx, &sapb.Serial{Serial: serial})
2503 if err != nil {
2504 wfe.sendError(response, logEvent, probs.NotFound("Certificate not found"), err)
2505 return
2506 }
2507
2508 if acct.ID != metadata.RegistrationID {
2509 wfe.sendError(response, logEvent, probs.Unauthorized("Account ID doesn't match ID for certificate"), err)
2510 return
2511 }
2512
2513
2514
2515 response.WriteHeader(http.StatusOK)
2516 }
2517
2518 func extractRequesterIP(req *http.Request) (net.IP, error) {
2519 ip := net.ParseIP(req.Header.Get("X-Real-IP"))
2520 if ip != nil {
2521 return ip, nil
2522 }
2523 host, _, err := net.SplitHostPort(req.RemoteAddr)
2524 if err != nil {
2525 return nil, err
2526 }
2527 return net.ParseIP(host), nil
2528 }
2529
2530 func urlForAuthz(authz core.Authorization, request *http.Request) string {
2531 return web.RelativeEndpoint(request, authzPath+authz.ID)
2532 }
2533
View as plain text