add overriding of ARI response (#501)
Fixes #486 This moves the GetCertificateBySerial call earlier, which means that call needs to succeed even for revoked certificates. So this also follows up on #252 by keeping revoked certs in the primary certificatesByID map (while still adding them to the revokedCertificatesByID map).
This commit is contained in:
		
							parent
							
								
									39dbb64e14
								
							
						
					
					
						commit
						d52948ce25
					
				| 
						 | 
				
			
			@ -149,6 +149,8 @@ type Certificate struct {
 | 
			
		|||
	DER          []byte
 | 
			
		||||
	IssuerChains [][]*Certificate
 | 
			
		||||
	AccountID    string
 | 
			
		||||
	// When non-empty, this is the ARI response sent for this certificate.
 | 
			
		||||
	ARIResponse string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c Certificate) PEM() []byte {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -413,7 +413,6 @@ func (m *MemoryStore) RevokeCertificate(cert *core.RevokedCertificate) {
 | 
			
		|||
	m.Lock()
 | 
			
		||||
	defer m.Unlock()
 | 
			
		||||
	m.revokedCertificatesByID[cert.Certificate.ID] = cert
 | 
			
		||||
	delete(m.certificatesByID, cert.Certificate.ID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
| 
						 | 
				
			
			@ -549,3 +548,19 @@ func (m *MemoryStore) IsDomainBlocked(name string) bool {
 | 
			
		|||
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetARIResponse looks up a certificate by serial number and sets its ARI response field
 | 
			
		||||
func (m *MemoryStore) SetARIResponse(serial *big.Int, ariResponse string) error {
 | 
			
		||||
	m.Lock()
 | 
			
		||||
	defer m.Unlock()
 | 
			
		||||
 | 
			
		||||
	for _, cert := range m.certificatesByID {
 | 
			
		||||
		if cert.Cert.SerialNumber.Cmp(serial) == 0 {
 | 
			
		||||
			cert.ARIResponse = ariResponse
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Certificate not found
 | 
			
		||||
	return fmt.Errorf("certificate with serial number %s not found", serial.String())
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										69
									
								
								wfe/wfe.go
								
								
								
								
							
							
						
						
									
										69
									
								
								wfe/wfe.go
								
								
								
								
							| 
						 | 
				
			
			@ -57,7 +57,7 @@ const (
 | 
			
		|||
	// Draft or likely-to-change paths
 | 
			
		||||
	renewalInfoPath = "/draft-ietf-acme-ari-03/renewalInfo/"
 | 
			
		||||
 | 
			
		||||
	// Theses entrypoints are not a part of the standard ACME endpoints,
 | 
			
		||||
	// These entrypoints are not a part of the standard ACME endpoints,
 | 
			
		||||
	// and are exposed by Pebble as an integration test tool. We export
 | 
			
		||||
	// RootCertPath so that the pebble binary can reference it.
 | 
			
		||||
	RootCertPath         = "/roots/"
 | 
			
		||||
| 
						 | 
				
			
			@ -65,6 +65,10 @@ const (
 | 
			
		|||
	intermediateCertPath = "/intermediates/"
 | 
			
		||||
	intermediateKeyPath  = "/intermediate-keys/"
 | 
			
		||||
	certStatusBySerial   = "/cert-status-by-serial/"
 | 
			
		||||
	// Post certificate PEM and desired literal response for renewal info
 | 
			
		||||
	// (the renewal info response is not validated so may be intentionally
 | 
			
		||||
	// malformed).
 | 
			
		||||
	setRenewalInfoPath = "/set-renewal-info/"
 | 
			
		||||
 | 
			
		||||
	// How long do pending authorizations last before expiring?
 | 
			
		||||
	pendingAuthzExpire = time.Hour
 | 
			
		||||
| 
						 | 
				
			
			@ -542,6 +546,9 @@ func (wfe *WebFrontEndImpl) ManagementHandler() http.Handler {
 | 
			
		|||
	wfe.HandleManagementFunc(m, intermediateCertPath, wfe.handleCert(wfe.ca.GetIntermediateCert, intermediateCertPath))
 | 
			
		||||
	wfe.HandleManagementFunc(m, intermediateKeyPath, wfe.handleKey(wfe.ca.GetIntermediateKey, intermediateKeyPath))
 | 
			
		||||
	wfe.HandleManagementFunc(m, certStatusBySerial, wfe.handleCertStatusBySerial)
 | 
			
		||||
 | 
			
		||||
	// POST only handlers
 | 
			
		||||
	wfe.HandleFunc(m, setRenewalInfoPath, wfe.SetRenewalInfo, http.MethodPost)
 | 
			
		||||
	return m
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1903,7 +1910,18 @@ func (wfe *WebFrontEndImpl) RenewalInfo(_ context.Context, response http.Respons
 | 
			
		|||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	renewalInfo, err := wfe.determineARIWindow(certID)
 | 
			
		||||
	cert := wfe.db.GetCertificateBySerial(certID.SerialNumber)
 | 
			
		||||
	if cert == nil {
 | 
			
		||||
		wfe.sendError(acme.NotFoundProblem("failed to retrieve existing certificate serial"), response)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if cert.ARIResponse != "" {
 | 
			
		||||
		_, _ = response.Write([]byte(cert.ARIResponse))
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	renewalInfo, err := wfe.determineARIWindow(certID, cert)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		wfe.sendError(acme.InternalErrorProblem(fmt.Sprintf("Error determining renewal window: %s", err)), response)
 | 
			
		||||
		return
 | 
			
		||||
| 
						 | 
				
			
			@ -1917,7 +1935,47 @@ func (wfe *WebFrontEndImpl) RenewalInfo(_ context.Context, response http.Respons
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (wfe *WebFrontEndImpl) determineARIWindow(id *core.CertID) (*core.RenewalInfo, error) {
 | 
			
		||||
// SetRenewalInfo overrides the default ARI response for a certificate.
 | 
			
		||||
func (wfe *WebFrontEndImpl) SetRenewalInfo(_ context.Context, response http.ResponseWriter, request *http.Request) {
 | 
			
		||||
	body, err := io.ReadAll(request.Body)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		wfe.sendError(acme.InternalErrorProblem("Error reading body"), response)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var reqJSON struct {
 | 
			
		||||
		Certificate string // in PEM form
 | 
			
		||||
		ARIResponse string // can be anything, even malformed JSON, so that users can test client response to malformed data
 | 
			
		||||
	}
 | 
			
		||||
	err = json.Unmarshal(body, &reqJSON)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		wfe.sendError(acme.MalformedProblem("Error unmarshaling request body"), response)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Decode and parse the PEM certificate
 | 
			
		||||
	block, _ := pem.Decode([]byte(reqJSON.Certificate))
 | 
			
		||||
	if block == nil || block.Type != "CERTIFICATE" {
 | 
			
		||||
		wfe.sendError(acme.MalformedProblem("Error decoding certificate PEM"), response)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cert, err := x509.ParseCertificate(block.Bytes)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		wfe.sendError(acme.MalformedProblem("Error parsing certificate"), response)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Look up certificate by serial number and update response
 | 
			
		||||
	err = wfe.db.SetARIResponse(cert.SerialNumber, reqJSON.ARIResponse)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		wfe.sendError(acme.NotFoundProblem(err.Error()), response)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	response.WriteHeader(http.StatusOK)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (wfe *WebFrontEndImpl) determineARIWindow(id *core.CertID, cert *core.Certificate) (*core.RenewalInfo, error) {
 | 
			
		||||
	if id == nil {
 | 
			
		||||
		return nil, errors.New("CertID was nil")
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -1928,11 +1986,6 @@ func (wfe *WebFrontEndImpl) determineARIWindow(id *core.CertID) (*core.RenewalIn
 | 
			
		|||
		return core.RenewalInfoImmediate(time.Now().In(time.UTC)), nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cert := wfe.db.GetCertificateBySerial(id.SerialNumber)
 | 
			
		||||
	if cert == nil {
 | 
			
		||||
		return nil, errors.New("failed to retrieve existing certificate serial")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return core.RenewalInfoSimple(cert.Cert.NotBefore, cert.Cert.NotAfter), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue