...

Source file src/github.com/letsencrypt/boulder/wfe2/wfe.go

Documentation: github.com/letsencrypt/boulder/wfe2

     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  	// 'grpc/noncebalancer' is imported for its init function.
    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  // Paths are the ACME-spec identified URL path-segments for various methods.
    49  // NOTE: In metrics/measured_http we make the assumption that these are all
    50  // lowercase plus hyphens. If you violate that assumption you should update
    51  // measured_http.
    52  const (
    53  	directoryPath = "/directory"
    54  	newAcctPath   = "/acme/new-acct"
    55  	acctPath      = "/acme/acct/"
    56  	// When we moved to authzv2, we used a "-v3" suffix to avoid confusion
    57  	// regarding ACMEv2.
    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  	// Draft or likely-to-change paths
    76  	renewalInfoPath = "/draft-ietf-acme-ari-01/renewalInfo/"
    77  
    78  	// Non-ACME paths
    79  	aiaIssuerPath = "/aia/issuer/"
    80  )
    81  
    82  const (
    83  	headerRetryAfter = "Retry-After"
    84  	// Our 99th percentile finalize latency is 2.3s. Asking clients to wait 3s
    85  	// before polling the order to get an updated status means that >99% of
    86  	// clients will fetch the updated order object exactly once,.
    87  	orderRetryAfter = 3
    88  )
    89  
    90  var errIncompleteGRPCResponse = errors.New("incomplete gRPC response message")
    91  
    92  // WebFrontEndImpl provides all the logic for Boulder's web-facing interface,
    93  // i.e., ACME.  Its members configure the paths for various ACME functions,
    94  // plus a few other data items used in ACME.  Its methods are primarily handlers
    95  // for HTTPS requests for the various ACME functions.
    96  type WebFrontEndImpl struct {
    97  	ra rapb.RegistrationAuthorityClient
    98  	sa sapb.StorageAuthorityReadOnlyClient
    99  	// gnc is a nonce-service client used exclusively for the issuance of
   100  	// nonces. It's configured to route requests to backends colocated with the
   101  	// WFE.
   102  	gnc nonce.Getter
   103  	// Register of anti-replay nonces
   104  	//
   105  	// Deprecated: See `rnc`, above.
   106  	noncePrefixMap map[string]nonce.Redeemer
   107  	// rnc is a nonce-service client used exclusively for the redemption of
   108  	// nonces. It uses a custom RPC load balancer which is configured to route
   109  	// requests to backends based on the prefix and HMAC key passed as in the
   110  	// context of the request. The HMAC and prefix are passed using context keys
   111  	// `nonce.HMACKeyCtxKey` and `nonce.PrefixCtxKey`.
   112  	rnc nonce.Redeemer
   113  	// rncKey is the HMAC key used to derive the prefix of nonce backends used
   114  	// for nonce redemption.
   115  	rncKey        string
   116  	accountGetter AccountGetter
   117  	log           blog.Logger
   118  	clk           clock.Clock
   119  	stats         wfe2Stats
   120  
   121  	// certificateChains maps IssuerNameIDs to slice of []byte containing a leading
   122  	// newline and one or more PEM encoded certificates separated by a newline,
   123  	// sorted from leaf to root. The first []byte is the default certificate chain,
   124  	// and any subsequent []byte is an alternate certificate chain.
   125  	certificateChains map[issuance.IssuerNameID][][]byte
   126  
   127  	// issuerCertificates is a map of IssuerNameIDs to issuer certificates built with the
   128  	// first entry from each of the certificateChains. These certificates are used
   129  	// to verify the signature of certificates provided in revocation requests.
   130  	issuerCertificates map[issuance.IssuerNameID]*issuance.Certificate
   131  
   132  	// URL to the current subscriber agreement (should contain some version identifier)
   133  	SubscriberAgreementURL string
   134  
   135  	// DirectoryCAAIdentity is used for the /directory response's "meta"
   136  	// element's "caaIdentities" field. It should match the VA's issuerDomain
   137  	// field value.
   138  	DirectoryCAAIdentity string
   139  
   140  	// DirectoryWebsite is used for the /directory response's "meta" element's
   141  	// "website" field.
   142  	DirectoryWebsite string
   143  
   144  	// Allowed prefix for legacy accounts used by verify.go's `lookupJWK`.
   145  	// See `cmd/boulder-wfe2/main.go`'s comment on the configuration field
   146  	// `LegacyKeyIDPrefix` for more information.
   147  	LegacyKeyIDPrefix string
   148  
   149  	// Key policy.
   150  	keyPolicy goodkey.KeyPolicy
   151  
   152  	// CORS settings
   153  	AllowOrigins []string
   154  
   155  	// requestTimeout is the per-request overall timeout.
   156  	requestTimeout time.Duration
   157  
   158  	// StaleTimeout determines the required staleness for resources allowed to be
   159  	// accessed via Boulder-specific GET-able APIs. Resources newer than
   160  	// staleTimeout must be accessed via POST-as-GET and the RFC 8555 ACME API. We
   161  	// do this to incentivize client developers to use the standard API.
   162  	staleTimeout time.Duration
   163  
   164  	// How long before authorizations and pending authorizations expire. The
   165  	// Boulder specific GET-able API uses these values to find the creation date
   166  	// of authorizations to determine if they are stale enough. The values should
   167  	// match the ones used by the RA.
   168  	authorizationLifetime        time.Duration
   169  	pendingAuthorizationLifetime time.Duration
   170  	limiter                      *ratelimits.Limiter
   171  }
   172  
   173  // NewWebFrontEndImpl constructs a web service for Boulder
   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  	// TODO(#6610): Remove the check for the map.
   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  // HandleFunc registers a handler at the given path. It's
   236  // http.HandleFunc(), but with a wrapper around the handler that
   237  // provides some generic per-request functionality:
   238  //
   239  // * Set a Replay-Nonce header.
   240  //
   241  // * Respond to OPTIONS requests, including CORS preflight requests.
   242  //
   243  // * Set a no cache header
   244  //
   245  // * Respond http.StatusMethodNotAllowed for HTTP methods other than
   246  // those listed.
   247  //
   248  // * Set CORS headers when responding to CORS "actual" requests.
   249  //
   250  // * Never send a body in response to a HEAD request. Anything
   251  // written by the handler will be discarded if the method is HEAD.
   252  // Also, all handlers that accept GET automatically accept HEAD.
   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  		// Allow HEAD for any resource that allows GET
   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  			// Per section 7.1 "Resources":
   287  			//   The "index" link relation is present on all resources other than the
   288  			//   directory and indicates the URL of the directory.
   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  				// Go's net/http (and httptest) servers will strip out the body
   297  				// of responses for us. This keeps the Content-Length for HEAD
   298  				// requests as the same as GET requests per the spec.
   299  			case "OPTIONS":
   300  				wfe.Options(response, request, methodsStr, methodsMap)
   301  				return
   302  			}
   303  
   304  			// No cache header is set for all requests, succeed or fail.
   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  			// Call the wrapped handler.
   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 // All callers are responsible for handling this error
   337  	}
   338  
   339  	response.Header().Set("Content-Type", "application/json")
   340  	response.WriteHeader(status)
   341  	_, err = response.Write(jsonReply)
   342  	if err != nil {
   343  		// Don't worry about returning this error because the caller will
   344  		// never handle it.
   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  // requestProto returns "http" for HTTP requests and "https" for HTTPS
   352  // requests. It supports the use of "X-Forwarded-Proto" to override the protocol.
   353  func requestProto(request *http.Request) string {
   354  	proto := "http"
   355  
   356  	// If the request was received via TLS, use `https://` for the protocol
   357  	if request.TLS != nil {
   358  		proto = "https"
   359  	}
   360  
   361  	// Allow upstream proxies  to specify the forwarded protocol. Allow this value
   362  	// to override our own guess.
   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  	// Create an empty map sized equal to the provided directory to store the
   374  	// relative-ized result
   375  	relativeDir := make(map[string]interface{}, len(directory))
   376  
   377  	// Copy each entry of the provided directory into the new relative map,
   378  	// prefixing it with the request protocol and host.
   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  			// Only relative-ize top level string values, e.g. not the "meta" element
   387  			relativeDir[k] = web.RelativeEndpoint(request, v)
   388  		default:
   389  			// If it isn't a string, put it into the results unmodified
   390  			relativeDir[k] = v
   391  		}
   392  	}
   393  
   394  	directoryJSON, err := marshalIndent(relativeDir)
   395  	// This should never happen since we are just marshalling known strings
   396  	if err != nil {
   397  		return nil, err
   398  	}
   399  
   400  	return directoryJSON, nil
   401  }
   402  
   403  // Handler returns an http.Handler that uses various functions for
   404  // various ACME-specified paths.
   405  func (wfe *WebFrontEndImpl) Handler(stats prometheus.Registerer, oTelHTTPOptions ...otelhttp.Option) http.Handler {
   406  	m := http.NewServeMux()
   407  	// Boulder specific endpoints
   408  	wfe.HandleFunc(m, buildIDPath, wfe.BuildID, "GET")
   409  
   410  	// POSTable ACME endpoints
   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  	// GETable and POST-as-GETable ACME endpoints
   419  	wfe.HandleFunc(m, directoryPath, wfe.Directory, "GET", "POST")
   420  	wfe.HandleFunc(m, newNoncePath, wfe.Nonce, "GET", "POST")
   421  	// POST-as-GETable ACME endpoints
   422  	// TODO(@cpu): After November 1st, 2020 support for "GET" to the following
   423  	// endpoints will be removed, leaving only POST-as-GET support.
   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  	// Boulder-specific GET-able resource endpoints
   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  	// Endpoint for draft-aaron-ari
   435  	if features.Enabled(features.ServeRenewalInfo) {
   436  		wfe.HandleFunc(m, renewalInfoPath, wfe.RenewalInfo, "GET", "POST")
   437  	}
   438  
   439  	// Non-ACME endpoints
   440  	wfe.HandleFunc(m, aiaIssuerPath, wfe.Issuer, "GET")
   441  
   442  	// We don't use our special HandleFunc for "/" because it matches everything,
   443  	// meaning we can wind up returning 405 when we mean to return 404. See
   444  	// https://github.com/letsencrypt/boulder/issues/717
   445  	m.Handle("/", web.NewTopHandler(wfe.log, web.WFEHandlerFunc(wfe.Index)))
   446  	return measured_http.New(m, wfe.clk, stats, oTelHTTPOptions...)
   447  }
   448  
   449  // Method implementations
   450  
   451  // Index serves a simple identification page. It is not part of the ACME spec.
   452  func (wfe *WebFrontEndImpl) Index(ctx context.Context, logEvent *web.RequestEvent, response http.ResponseWriter, request *http.Request) {
   453  	// All requests that are not handled by our ACME endpoints ends up
   454  	// here. Set the our logEvent endpoint to "/" and the slug to the path
   455  	// minus "/" to make sure that we properly set log information about
   456  	// the request, even in the case of a 404
   457  	logEvent.Endpoint = "/"
   458  	logEvent.Slug = request.URL.Path[1:]
   459  
   460  	// http://golang.org/pkg/net/http/#example_ServeMux_Handle
   461  	// The "/" pattern matches everything, so we need to check
   462  	// that we're at the root here.
   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  // Directory is an HTTP request handler that provides the directory
   499  // object stored in the WFE's DirectoryEndpoints member with paths prefixed
   500  // using the `request.Host` of the HTTP request.
   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  	// Add a random key to the directory in order to make sure that clients don't hardcode an
   528  	// expected set of keys. This ensures that we can properly extend the directory when we
   529  	// need to add a new endpoint or meta element.
   530  	directoryEndpoints[core.RandomString(8)] = randomDirKeyExplanationLink
   531  
   532  	// ACME since draft-02 describes an optional "meta" directory entry. The
   533  	// meta entry may optionally contain a "termsOfService" URI for the
   534  	// current ToS.
   535  	metaMap := map[string]interface{}{
   536  		"termsOfService": wfe.SubscriberAgreementURL,
   537  	}
   538  	// The "meta" directory entry may also include a []string of CAA identities
   539  	if wfe.DirectoryCAAIdentity != "" {
   540  		// The specification says caaIdentities is an array of strings. In
   541  		// practice Boulder's VA only allows configuring ONE CAA identity. Given
   542  		// that constraint it doesn't make sense to allow multiple directory CAA
   543  		// identities so we use just the `wfe.DirectoryCAAIdentity` alone.
   544  		metaMap["caaIdentities"] = []string{
   545  			wfe.DirectoryCAAIdentity,
   546  		}
   547  	}
   548  	// The "meta" directory entry may also include a string with a website URL
   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  // Nonce is an endpoint for getting a fresh nonce with an HTTP GET or HEAD
   568  // request. This endpoint only returns a status code header - the `HandleFunc`
   569  // wrapper ensures that a nonce is written in the correct response header.
   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  	// The ACME specification says GET requests should receive http.StatusNoContent
   586  	// and HEAD/POST-as-GET requests should receive http.StatusOK.
   587  	if request.Method != "GET" {
   588  		statusCode = http.StatusOK
   589  	}
   590  	response.WriteHeader(statusCode)
   591  
   592  	// The ACME specification says the server MUST include a Cache-Control header
   593  	// field with the "no-store" directive in responses for the newNonce resource,
   594  	// in order to prevent caching of this resource.
   595  	response.Header().Set("Cache-Control", "no-store")
   596  }
   597  
   598  // sendError wraps web.SendError
   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  // checkNewAccountLimits checks whether sufficient limit quota exists for the
   619  // creation of a new account from the given IP address. If so, that quota is
   620  // spent. If an error is encountered during the check, it is logged but not
   621  // returned.
   622  //
   623  // TODO(#5545): For now we're simply exercising the new rate limiter codepath.
   624  // This should eventually return a berrors.RateLimit error containing the retry
   625  // after duration among other information available in the ratelimits.Decision.
   626  func (wfe *WebFrontEndImpl) checkNewAccountLimits(ctx context.Context, ip net.IP) {
   627  	if wfe.limiter == nil {
   628  		// Limiter is disabled.
   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  		// TODO(#5545): Once key-value rate limits are authoritative this log
   637  		// line should be removed in favor of returning the error.
   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  		// This requester is being limited or the request was made from an IPv4
   648  		// address.
   649  		return
   650  	}
   651  
   652  	// See docs for ratelimits.NewRegistrationsPerIPv6Range for more information
   653  	// on the selection of a /48 block size for IPv6 ranges.
   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  // refundNewAccountLimits is typically called when a new account creation fails.
   663  // It refunds the limit quota consumed by the request, allowing the caller to
   664  // retry immediately. If an error is encountered during the refund, it is logged
   665  // but not returned.
   666  func (wfe *WebFrontEndImpl) refundNewAccountLimits(ctx context.Context, ip net.IP) {
   667  	if wfe.limiter == nil {
   668  		// Limiter is disabled.
   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  		// TODO(#5545): Once key-value rate limits are authoritative this log
   677  		// line should be removed in favor of returning the error.
   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  		// Request was made from an IPv4 address.
   688  		return
   689  	}
   690  
   691  	// See docs for ratelimits.NewRegistrationsPerIPv6Range for more information
   692  	// on the selection of a /48 block size for IPv6 ranges.
   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  // NewAccount is used by clients to submit a new account
   702  func (wfe *WebFrontEndImpl) NewAccount(
   703  	ctx context.Context,
   704  	logEvent *web.RequestEvent,
   705  	response http.ResponseWriter,
   706  	request *http.Request) {
   707  
   708  	// NewAccount uses `validSelfAuthenticatedPOST` instead of
   709  	// `validPOSTforAccount` because there is no account to authenticate against
   710  	// until after it is created!
   711  	body, key, prob := wfe.validSelfAuthenticatedPOST(ctx, request)
   712  	if prob != nil {
   713  		// validSelfAuthenticatedPOST handles its own setting of logEvent.Errors
   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  			// If there is an existing, but deactivated account, then return an unauthorized
   733  			// problem informing the user that this account was deactivated
   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  			// ServerInternal because we just created this account, and it
   754  			// should be OK.
   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  	// If the request included a true "OnlyReturnExisting" field and we did not
   776  	// find an existing registration with the key specified then we must return an
   777  	// error and not create a new account.
   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  	// Prepare account information to create corepb.Registration
   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  	// Create corepb.Registration from provided account information
   815  	reg := corepb.Registration{
   816  		Contact:         contacts,
   817  		ContactsPresent: contactsPresent,
   818  		Agreement:       wfe.SubscriberAgreementURL,
   819  		Key:             keyBytes,
   820  		InitialIP:       ipBytes,
   821  	}
   822  
   823  	// TODO(#5545): Spending and Refunding can be async until these rate limits
   824  	// are authoritative. This saves us from adding latency to each request.
   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  	// Send the registration to the RA via grpc
   834  	acctPB, err := wfe.ra.NewRegistration(ctx, &reg)
   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  			// return error even if berrors.NotFound, as the duplicate key error we got from
   843  			// ra.NewRegistration indicates it _does_ already exist.
   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  		// ServerInternal because we just created this account, and it
   882  		// should be OK.
   883  		wfe.sendError(response, logEvent, probs.ServerInternal("Error marshaling account"), err)
   884  		return
   885  	}
   886  	newRegistrationSuccessful = true
   887  }
   888  
   889  // parseRevocation accepts the payload for a revocation request and parses it
   890  // into both the certificate to be revoked and the requested revocation reason
   891  // (if any). Returns an error if any of the parsing fails, or if the given cert
   892  // or revocation reason don't pass simple static checks. Also populates some
   893  // metadata fields on the given logEvent.
   894  func (wfe *WebFrontEndImpl) parseRevocation(
   895  	jwsBody []byte, logEvent *web.RequestEvent) (*x509.Certificate, revocation.Reason, *probs.ProblemDetails) {
   896  	// Read the revoke request from the JWS payload
   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  	// Parse the provided certificate
   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  	// Compute and record the serial number of the provided certificate
   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  	// Try to validate the signature on the provided cert using its corresponding
   920  	// issuer certificate.
   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  	// Verify the revocation reason supplied is allowed
   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  // revokeCertBySubscriberKey processes an outer JWS as a revocation request that
   964  // is authenticated by a KeyID and the associated account.
   965  func (wfe *WebFrontEndImpl) revokeCertBySubscriberKey(
   966  	ctx context.Context,
   967  	outerJWS *bJSONWebSignature,
   968  	request *http.Request,
   969  	logEvent *web.RequestEvent) error {
   970  	// For Key ID revocations we authenticate the outer JWS by using
   971  	// `validJWSForAccount` similar to other WFE endpoints
   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  	// The RA will confirm that the authenticated account either originally
   990  	// issued the certificate, or has demonstrated control over all identifiers
   991  	// in the certificate.
   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  // revokeCertByCertKey processes an outer JWS as a revocation request that is
  1005  // authenticated by an embedded JWK. E.g. in the case where someone is
  1006  // requesting a revocation by using the keypair associated with the certificate
  1007  // to be revoked
  1008  func (wfe *WebFrontEndImpl) revokeCertByCertKey(
  1009  	ctx context.Context,
  1010  	outerJWS *bJSONWebSignature,
  1011  	request *http.Request,
  1012  	logEvent *web.RequestEvent) error {
  1013  	// For embedded JWK revocations we authenticate the outer JWS by using
  1014  	// `validSelfAuthenticatedJWS` similar to new-reg and key rollover.
  1015  	// We do *not* use `validSelfAuthenticatedPOST` here because we've already
  1016  	// read the HTTP request body in `parseJWSRequest` and it is now empty.
  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  	// For embedded JWK revocations we decide if a requester is able to revoke a specific
  1028  	// certificate by checking that to-be-revoked certificate has the same public
  1029  	// key as the JWK that was used to authenticate the request
  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  	// The RA assumes here that the WFE2 has validated the JWS as proving
  1043  	// control of the private key corresponding to this certificate.
  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  // RevokeCertificate is used by clients to request the revocation of a cert. The
  1055  // revocation request is handled uniquely based on the method of authentication
  1056  // used.
  1057  func (wfe *WebFrontEndImpl) RevokeCertificate(
  1058  	ctx context.Context,
  1059  	logEvent *web.RequestEvent,
  1060  	response http.ResponseWriter,
  1061  	request *http.Request) {
  1062  
  1063  	// The ACME specification handles the verification of revocation requests
  1064  	// differently from other endpoints. For this reason we do *not* immediately
  1065  	// call `wfe.validPOSTForAccount` like all of the other endpoints.
  1066  	// For this endpoint we need to accept a JWS with an embedded JWK, or a JWS
  1067  	// with an embedded key ID, handling each case differently in terms of which
  1068  	// certificates are authorized to be revoked by the requester
  1069  
  1070  	// Parse the JWS from the HTTP Request
  1071  	jws, prob := wfe.parseJWSRequest(request)
  1072  	if prob != nil {
  1073  		wfe.sendError(response, logEvent, prob, nil)
  1074  		return
  1075  	}
  1076  
  1077  	// Figure out which type of authentication this JWS uses
  1078  	authType, prob := checkJWSAuthType(jws.Signatures[0].Header)
  1079  	if prob != nil {
  1080  		wfe.sendError(response, logEvent, prob, nil)
  1081  		return
  1082  	}
  1083  
  1084  	// Handle the revocation request according to how it is authenticated, or if
  1085  	// the authentication type is unknown, error immediately
  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  // Challenge handles POST requests to challenge URLs.
  1104  // Such requests are clients' responses to the server's challenges.
  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  	// Ensure gRPC response is complete.
  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  // prepAccountForDisplay takes a core.Registration and mutates it to be ready
  1180  // for display in a JSON response. Primarily it papers over legacy ACME v1
  1181  // features or non-standard details internal to Boulder we don't want clients to
  1182  // rely on.
  1183  func prepAccountForDisplay(acct *core.Registration) {
  1184  	// Zero out the account ID so that it isn't marshalled. RFC 8555 specifies
  1185  	// using the Location header for learning the account ID.
  1186  	acct.ID = 0
  1187  
  1188  	// We populate the account Agreement field when creating a new response to
  1189  	// track which terms-of-service URL was in effect when an account with
  1190  	// "termsOfServiceAgreed":"true" is created. That said, we don't want to send
  1191  	// this value back to a V2 client. The "Agreement" field of an
  1192  	// account/registration is a V1 notion so we strip it here in the WFE2 before
  1193  	// returning the account.
  1194  	acct.Agreement = ""
  1195  }
  1196  
  1197  // prepChallengeForDisplay takes a core.Challenge and prepares it for display to
  1198  // the client by filling in its URL field and clearing its ID and URI fields.
  1199  func (wfe *WebFrontEndImpl) prepChallengeForDisplay(request *http.Request, authz core.Authorization, challenge *core.Challenge) {
  1200  	// Update the challenge URL to be relative to the HTTP request Host
  1201  	challenge.URL = web.RelativeEndpoint(request, fmt.Sprintf("%s%s/%s", challengePath, authz.ID, challenge.StringID()))
  1202  
  1203  	// Ensure the challenge URI isn't written by setting it to
  1204  	// a value that the JSON omitempty tag considers empty
  1205  	challenge.URI = ""
  1206  
  1207  	// ACMEv2 never sends the KeyAuthorization back in a challenge object.
  1208  	challenge.ProvidedKeyAuthorization = ""
  1209  
  1210  	// Internally, we store challenge error problems with just the short form
  1211  	// (e.g. "CAA") of the problem type. But for external display, we need to
  1212  	// prefix the error type with the RFC8555 ACME Error namespace.
  1213  	if challenge.Error != nil {
  1214  		challenge.Error.Type = probs.ErrorNS + challenge.Error.Type
  1215  	}
  1216  
  1217  	// If the authz has been marked invalid, consider all challenges on that authz
  1218  	// to be invalid as well.
  1219  	if authz.Status == core.StatusInvalid {
  1220  		challenge.Status = authz.Status
  1221  	}
  1222  }
  1223  
  1224  // prepAuthorizationForDisplay takes a core.Authorization and prepares it for
  1225  // display to the client by clearing its ID and RegistrationID fields, and
  1226  // preparing all its challenges.
  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  	// The ACME spec forbids allowing "*" in authorization identifiers. Boulder
  1235  	// allows this internally as a means of tracking when an authorization
  1236  	// corresponds to a wildcard request (e.g. to handle CAA properly). We strip
  1237  	// the "*." prefix from the Authz's Identifier's Value here to respect the law
  1238  	// of the protocol.
  1239  	if strings.HasPrefix(authz.Identifier.Value, "*.") {
  1240  		authz.Identifier.Value = strings.TrimPrefix(authz.Identifier.Value, "*.")
  1241  		// Mark that the authorization corresponds to a wildcard request since we've
  1242  		// now removed the wildcard prefix from the identifier.
  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  		// InternalServerError because this is a failure to decode data passed in
  1263  		// by the caller, which got it from the DB.
  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  		// validPOSTForAccount handles its own setting of logEvent.Errors
  1280  		wfe.sendError(response, logEvent, prob, nil)
  1281  		return
  1282  	}
  1283  
  1284  	// Check that the account ID matching the key used matches
  1285  	// the account ID on the authz object
  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  	// If the JWS body is empty then this POST is a POST-as-GET to retrieve
  1296  	// challenge details, not a POST to initiate a challenge
  1297  	if string(body) == "" {
  1298  		challenge := authz.Challenges[challengeIndex]
  1299  		wfe.getChallenge(response, request, authz, &challenge, logEvent)
  1300  		return
  1301  	}
  1302  
  1303  	// We can expect some clients to try and update a challenge for an authorization
  1304  	// that is already valid. In this case we don't need to process the challenge
  1305  	// update. It wouldn't be helpful, the overall authorization is already good!
  1306  	var returnAuthz core.Authorization
  1307  	if authz.Status == core.StatusValid {
  1308  		returnAuthz = authz
  1309  	} else {
  1310  
  1311  		// NOTE(@cpu): Historically a challenge update needed to include
  1312  		// a KeyAuthorization field. This is no longer the case, since both sides can
  1313  		// calculate the key authorization as needed. We unmarshal here only to check
  1314  		// that the POST body is valid JSON. Any data/fields included are ignored to
  1315  		// be kind to ACMEv2 implementations that still send a key authorization.
  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  	// assumption: PerformValidation does not modify order of challenges
  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  		// ServerInternal because we made the challenges, they should be OK
  1357  		wfe.sendError(response, logEvent, probs.ServerInternal("Failed to marshal challenge"), err)
  1358  		return
  1359  	}
  1360  }
  1361  
  1362  // Account is used by a client to submit an update to their account.
  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  		// validPOSTForAccount handles its own setting of logEvent.Errors
  1372  		wfe.sendError(response, logEvent, prob, nil)
  1373  		return
  1374  	}
  1375  
  1376  	// Requests to this handler should have a path that leads to a known
  1377  	// account
  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  	// If the body was not empty, then this is an account update request.
  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  		// ServerInternal because we just generated the account, it should be OK
  1411  		wfe.sendError(response, logEvent,
  1412  			probs.ServerInternal("Failed to marshal account"), err)
  1413  		return
  1414  	}
  1415  }
  1416  
  1417  // updateAccount unmarshals an account update request from the provided
  1418  // requestBody to update the given registration. Important: It is assumed the
  1419  // request has already been authenticated by the caller. If the request is
  1420  // a valid update the resulting updated account is returned, otherwise a problem
  1421  // is returned.
  1422  func (wfe *WebFrontEndImpl) updateAccount(
  1423  	ctx context.Context,
  1424  	requestBody []byte,
  1425  	currAcct *core.Registration) (*core.Registration, *probs.ProblemDetails) {
  1426  	// Only the Contact and Status fields of an account may be updated this way.
  1427  	// For key updates clients should be using the key change endpoint.
  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  	// Convert existing account to corepb.Registration
  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  	// Copy over the fields from the request to the registration object used for
  1452  	// the RA updates.
  1453  	// Create corepb.Registration from provided account information
  1454  	updatePb := &corepb.Registration{
  1455  		Contact:         contacts,
  1456  		ContactsPresent: contactsPresent,
  1457  		Status:          string(accountUpdateRequest.Status),
  1458  	}
  1459  
  1460  	// People *will* POST their full accounts to this endpoint, including
  1461  	// the 'valid' status, to avoid always failing out when that happens only
  1462  	// attempt to deactivate if the provided status is different from their current
  1463  	// status.
  1464  	//
  1465  	// If a user tries to send both a deactivation request and an update to their
  1466  	// contacts or subscriber agreement URL the deactivation will take place and
  1467  	// return before an update would be performed.
  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  	// Account objects contain a JWK object which are merged in UpdateRegistration
  1481  	// if it is different from the existing account key. Since this isn't how you
  1482  	// update the key we just copy the existing one into the update object here. This
  1483  	// ensures the key isn't changed and that we can cleanly serialize the update as
  1484  	// JSON to send via RPC to the RA.
  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  	// Convert proto to core.Registration for return
  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  // deactivateAuthorization processes the given JWS POST body as a request to
  1502  // deactivate the provided authorization. If an error occurs it is written to
  1503  // the response writer. Important: `deactivateAuthorization` does not check that
  1504  // the requester is authorized to deactivate the given authorization. It is
  1505  // assumed that this check is performed prior to calling deactivateAuthorzation.
  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  	// Since the authorization passed to DeactivateAuthorization isn't
  1530  	// mutated locally by the function we must manually set the status
  1531  	// here before displaying the authorization to the user
  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  	// If the request is a POST it is either:
  1544  	//   A) an update to an authorization to deactivate it
  1545  	//   B) a POST-as-GET to query the authorization details
  1546  	if request.Method == "POST" {
  1547  		// Both POST options need to be authenticated by an account
  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  	// Ensure gRPC response is complete.
  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  	// After expiring, authorizations are inaccessible
  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  	// If this was a POST that has an associated requestAccount and that account
  1601  	// doesn't own the authorization, abort before trying to deactivate the authz
  1602  	// or return its details
  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  	// If the body isn't empty we know it isn't a POST-as-GET and must be an
  1610  	// attempt to deactivate an authorization.
  1611  	if string(requestBody) != "" {
  1612  		// If the deactivation fails return early as errors and return codes
  1613  		// have already been set. Otherwise continue so that the user gets
  1614  		// sent the deactivated authorization.
  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  		// InternalServerError because this is a failure to decode from our DB.
  1631  		wfe.sendError(response, logEvent, probs.ServerInternal("Failed to JSON marshal authz"), err)
  1632  		return
  1633  	}
  1634  }
  1635  
  1636  // Certificate is used by clients to request a copy of their current certificate, or to
  1637  // request a reissuance of the certificate.
  1638  func (wfe *WebFrontEndImpl) Certificate(ctx context.Context, logEvent *web.RequestEvent, response http.ResponseWriter, request *http.Request) {
  1639  	var requesterAccount *core.Registration
  1640  	// Any POSTs to the Certificate endpoint should be POST-as-GET requests. There are
  1641  	// no POSTs with a body allowed for this endpoint.
  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  	// An alternate chain may be requested with the request path {serial}/{chain}, where chain
  1655  	// is a number - an index into the slice of chains for the issuer. If a specific chain is
  1656  	// not requested, then it defaults to zero - the default certificate chain for the issuer.
  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  	// Certificate paths consist of the CertBase path, plus exactly sixteen hex
  1670  	// digits.
  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  	// If there was a requesterAccount (e.g. because it was a POST-as-GET request)
  1700  	// then the requesting account must be the owner of the certificate, otherwise
  1701  	// return an unauthorized error.
  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  			// If we can't parse one of our own certs there's a serious problem
  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  			// If there is no wfe.certificateChains entry for the IssuerNameID then
  1728  			// we can't provide a chain for this cert. If the certificate is expired,
  1729  			// just return the bare cert. If the cert is still valid, then there is
  1730  			// a misconfiguration and we should treat it as an internal server error.
  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  		// If the requested chain is outside the bounds of the available chains,
  1743  		// then it is an error by the client - not found.
  1744  		if requestedChain < 0 || requestedChain >= len(availableChains) {
  1745  			return nil, probs.NotFound("Unknown issuance chain")
  1746  		}
  1747  
  1748  		// Double check that the signature validates.
  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  		// Add rel="alternate" links for every chain available for this issuer,
  1760  		// excluding the currently requested chain.
  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  		// Prepend the chain with the leaf certificate
  1771  		return append(leafPEM, availableChains[requestedChain]...), nil
  1772  	}()
  1773  	if prob != nil {
  1774  		wfe.sendError(response, logEvent, prob, nil)
  1775  		return
  1776  	}
  1777  
  1778  	// NOTE(@cpu): We must explicitly set the Content-Length header here. The Go
  1779  	// HTTP library will only add this header if the body is below a certain size
  1780  	// and with the addition of a PEM encoded certificate chain the body size of
  1781  	// this endpoint will exceed this threshold. Since we know the length we can
  1782  	// reliably set it ourselves and not worry.
  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  // BuildID tells the requester what build we're running.
  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  // Options responds to an HTTP OPTIONS request.
  1802  func (wfe *WebFrontEndImpl) Options(response http.ResponseWriter, request *http.Request, methodsStr string, methodsMap map[string]bool) {
  1803  	// Every OPTIONS request gets an Allow header with a list of supported methods.
  1804  	response.Header().Set("Allow", methodsStr)
  1805  
  1806  	// CORS preflight requests get additional headers. See
  1807  	// http://www.w3.org/TR/cors/#resource-preflight-requests
  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  // setCORSHeaders() tells the client that CORS is acceptable for this
  1818  // request. If allowMethods == "" the request is assumed to be a CORS
  1819  // actual request and no Access-Control-Allow-Methods header will be
  1820  // sent.
  1821  func (wfe *WebFrontEndImpl) setCORSHeaders(response http.ResponseWriter, request *http.Request, allowMethods string) {
  1822  	reqOrigin := request.Header.Get("Origin")
  1823  	if reqOrigin == "" {
  1824  		// This is not a CORS request.
  1825  		return
  1826  	}
  1827  
  1828  	// Allow CORS if the current origin (or "*") is listed as an
  1829  	// allowed origin in config. Otherwise, disallow by returning
  1830  	// without setting any CORS headers.
  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  		// For an OPTIONS request: allow all methods handled at this URL.
  1850  		response.Header().Set("Access-Control-Allow-Methods", allowMethods)
  1851  	}
  1852  	// NOTE(@cpu): "Content-Type" is considered a 'simple header' that doesn't
  1853  	// need to be explicitly allowed in 'access-control-allow-headers', but only
  1854  	// when the value is one of: `application/x-www-form-urlencoded`,
  1855  	// `multipart/form-data`, or `text/plain`. Since `application/jose+json` is
  1856  	// not one of these values we must be explicit in saying that `Content-Type`
  1857  	// is an allowed header. See MDN for more details:
  1858  	// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers
  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  // KeyRollover allows a user to change their signing key
  1865  func (wfe *WebFrontEndImpl) KeyRollover(
  1866  	ctx context.Context,
  1867  	logEvent *web.RequestEvent,
  1868  	response http.ResponseWriter,
  1869  	request *http.Request) {
  1870  	// Validate the outer JWS on the key rollover in standard fashion using
  1871  	// validPOSTForAccount
  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  	// Parse the inner JWS from the validated outer JWS body
  1881  	innerJWS, prob := wfe.parseJWS(outerBody)
  1882  	if prob != nil {
  1883  		wfe.sendError(response, logEvent, prob, nil)
  1884  		return
  1885  	}
  1886  
  1887  	// Validate the inner JWS as a key rollover request for the outer JWS
  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  	// Check that the rollover request's account URL matches the account URL used
  1896  	// to validate the outer JWS
  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  	// Check that the new key isn't the same as the old key. This would fail as
  1907  	// part of the subsequent `wfe.SA.GetRegistrationByKey` check since the new key
  1908  	// will find the old account if its equal to the old account key. We
  1909  	// check new key against old key explicitly to save an RPC round trip and a DB
  1910  	// query for this easy rejection case
  1911  	keysEqual, err := core.PublicKeysEqual(newKey.Key, oldKey.Key)
  1912  	if err != nil {
  1913  		// This should not happen - both the old and new key have been validated by now
  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  	// Marshal key to bytes
  1925  	newKeyBytes, err := newKey.MarshalJSON()
  1926  	if err != nil {
  1927  		wfe.sendError(response, logEvent, probs.ServerInternal("Error marshaling new key"), err)
  1928  	}
  1929  	// Check that the new key isn't already being used for an existing account
  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  	// Convert account to proto for grpc
  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  	// Copy new key into an empty registration to provide as the update
  1949  	updatePb := &corepb.Registration{Key: newKeyBytes}
  1950  
  1951  	// Update the account key to the new key
  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  			// It is possible that between checking for the existing key, and performing the update
  1956  			// a parallel update or new account request happened and claimed the key. In this case
  1957  			// just retrieve the account again, and return an error as we would above with a Location
  1958  			// header
  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  	// Convert proto to registration for display
  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  // orderToOrderJSON converts a *corepb.Order instance into an orderJSON struct
  1999  // that is returned in HTTP API responses. It will convert the order names to
  2000  // DNS type identifiers and additionally create absolute URLs for the finalize
  2001  // URL and the ceritificate URL as appropriate.
  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  	// If there is an order error, prefix its type with the V2 namespace
  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  // NewOrder is used by clients to create a new order object and a set of
  2037  // authorizations to fulfill for issuance.
  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  		// validPOSTForAccount handles its own setting of logEvent.Errors
  2047  		wfe.sendError(response, logEvent, prob, nil)
  2048  		return
  2049  	}
  2050  
  2051  	// We only allow specifying Identifiers in a new order request - if the
  2052  	// `notBefore` and/or `notAfter` fields described in Section 7.4 of acme-08
  2053  	// are sent we return a probs.Malformed as we do not support them
  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  	// Collect up all of the DNS identifier values into a []string for
  2077  	// subsequent layers to process. We reject anything with a non-DNS
  2078  	// type identifier here. Check to make sure one of the strings is
  2079  	// short enough to meet the max CN bytes requirement.
  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  		// The max length of a CommonName is 64 bytes. Check to make sure
  2095  		// at least one DNS name meets this requirement to be promoted to
  2096  		// the CN.
  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  // GetOrder is used to retrieve a existing order object
  2133  func (wfe *WebFrontEndImpl) GetOrder(ctx context.Context, logEvent *web.RequestEvent, response http.ResponseWriter, request *http.Request) {
  2134  	var requesterAccount *core.Registration
  2135  	// Any POSTs to the Order endpoint should be POST-as-GET requests. There are
  2136  	// no POSTs with a body allowed for this endpoint.
  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  	// Path prefix is stripped, so this should be like "<account ID>/<order ID>"
  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  	// If the requesterAccount is not nil then this was an authenticated
  2192  	// POST-as-GET request and we need to verify the requesterAccount is the
  2193  	// order's owner.
  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  // FinalizeOrder is used to request issuance for a existing order object.
  2213  // Most processing of the order details is handled by the RA but
  2214  // we do attempt to throw away requests with invalid CSRs here.
  2215  func (wfe *WebFrontEndImpl) FinalizeOrder(ctx context.Context, logEvent *web.RequestEvent, response http.ResponseWriter, request *http.Request) {
  2216  	// Validate the POST body signature and get the authenticated account for this
  2217  	// finalize order request
  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  	// Order URLs are like: /acme/finalize/<account>/<order>/. The prefix is
  2226  	// stripped by the time we get here.
  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  	// If the authenticated account ID doesn't match the order's registration ID
  2265  	// pretend it doesn't exist and abort.
  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  	// Only ready orders can be finalized.
  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  	// If the order is expired we can not finalize it and must return an error
  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  	// The authenticated finalize message body should be an encoded CSR
  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  	// Check for a malformed CSR early to avoid unnecessary RPCs
  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  	// Inc CSR signature algorithm counter
  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  // certID matches the ASN.1 structure of the CertID sequence defined by RFC6960.
  2341  type certID struct {
  2342  	HashAlgorithm  pkix.AlgorithmIdentifier
  2343  	IssuerNameHash []byte
  2344  	IssuerKeyHash  []byte
  2345  	SerialNumber   *big.Int
  2346  }
  2347  
  2348  // RenewalInfo is used to get information about the suggested renewal window
  2349  // for the given certificate. It only accepts unauthenticated GET requests.
  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  	// The path prefix has already been stripped, so request.URL.Path here is just
  2367  	// the base64url-encoded DER CertID sequence.
  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  	// Verify that the hash algorithm is SHA-256, so people don't use SHA-1 here.
  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  	// We can do all of our processing based just on the serial, because Boulder
  2388  	// does not re-use the same serial across multiple issuers.
  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  	// Check if the serial is part of an ongoing/active incident, in which case
  2402  	// the client should replace it now.
  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  	// Check if the serial is revoked, in which case the client should replace it
  2416  	// now.
  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  	// It's okay to use GetCertificate (vs trying to get a precertificate),
  2430  	// because we don't intend to serve ARI for certs that never made it past
  2431  	// the precert stage.
  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  	// TODO(#6033): Consider parsing cert.Der, using that to get its IssuerNameID,
  2443  	// using that to look up the actual issuer cert in wfe.issuerCertificates,
  2444  	// using that to compute the actual issuerNameHash and issuerKeyHash, and
  2445  	// comparing those to the ones in the request.
  2446  
  2447  	sendRI(core.RenewalInfoSimple(
  2448  		time.Unix(0, cert.IssuedNS).UTC(),
  2449  		time.Unix(0, cert.ExpiresNS).UTC()))
  2450  }
  2451  
  2452  // UpdateRenewal is used by the client to inform the server that they have
  2453  // replaced the certificate in question, so it can be safely revoked. All
  2454  // requests must be authenticated to the account which ordered the cert.
  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  		// validPOSTForAccount handles its own setting of logEvent.Errors
  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  	// We can do all of our processing based just on the serial, because Boulder
  2498  	// does not re-use the same serial across multiple issuers.
  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  	// TODO(#6732): Write the replaced status to persistent storage.
  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