diff --git a/authority/provisioner/scep.go b/authority/provisioner/scep.go index e547deff..6f81a4d7 100644 --- a/authority/provisioner/scep.go +++ b/authority/provisioner/scep.go @@ -143,12 +143,14 @@ var ( // that case, the other webhooks will be skipped. If none of // the webhooks indicates the value of the challenge was accepted, // an error is returned. -func (c *challengeValidationController) Validate(ctx context.Context, challenge, transactionID string) error { +func (c *challengeValidationController) Validate(ctx context.Context, csr *x509.CertificateRequest, challenge, transactionID string) error { for _, wh := range c.webhooks { - req := &webhook.RequestBody{ - SCEPChallenge: challenge, - SCEPTransactionID: transactionID, + req, err := webhook.NewRequestBody(webhook.WithX509CertificateRequest(csr)) + if err != nil { + return fmt.Errorf("failed creating new webhook request: %w", err) } + req.SCEPChallenge = challenge + req.SCEPTransactionID = transactionID resp, err := wh.DoWithContext(ctx, c.client, req, nil) // TODO(hs): support templated URL? Requires some refactoring if err != nil { return fmt.Errorf("failed executing webhook request: %w", err) @@ -329,13 +331,13 @@ func (s *SCEP) GetContentEncryptionAlgorithm() int { // ValidateChallenge validates the provided challenge. It starts by // selecting the validation method to use, then performs validation // according to that method. -func (s *SCEP) ValidateChallenge(ctx context.Context, challenge, transactionID string) error { +func (s *SCEP) ValidateChallenge(ctx context.Context, csr *x509.CertificateRequest, challenge, transactionID string) error { if s.challengeValidationController == nil { return fmt.Errorf("provisioner %q wasn't initialized", s.Name) } switch s.selectValidationMethod() { case validationMethodWebhook: - return s.challengeValidationController.Validate(ctx, challenge, transactionID) + return s.challengeValidationController.Validate(ctx, csr, challenge, transactionID) default: if subtle.ConstantTimeCompare([]byte(s.ChallengePassword), []byte(challenge)) == 0 { return errors.New("invalid challenge password provided") diff --git a/authority/provisioner/scep_test.go b/authority/provisioner/scep_test.go index acf047fb..0c1049ca 100644 --- a/authority/provisioner/scep_test.go +++ b/authority/provisioner/scep_test.go @@ -141,7 +141,7 @@ func Test_challengeValidationController_Validate(t *testing.T) { } ctx := context.Background() - err := c.Validate(ctx, tt.args.challenge, tt.args.transactionID) + err := c.Validate(ctx, nil, tt.args.challenge, tt.args.transactionID) if tt.expErr != nil { assert.EqualError(t, err, tt.expErr.Error()) @@ -330,7 +330,7 @@ func TestSCEP_ValidateChallenge(t *testing.T) { require.NoError(t, err) ctx := context.Background() - err = tt.p.ValidateChallenge(ctx, tt.args.challenge, tt.args.transactionID) + err = tt.p.ValidateChallenge(ctx, nil, tt.args.challenge, tt.args.transactionID) if tt.expErr != nil { assert.EqualError(t, err, tt.expErr.Error()) return diff --git a/scep/api/api.go b/scep/api/api.go index b618607c..2ac496e4 100644 --- a/scep/api/api.go +++ b/scep/api/api.go @@ -315,7 +315,7 @@ func PKIOperation(ctx context.Context, req request) (Response, error) { // a certificate exists; then it will use RenewalReq. Adding the challenge check here may be a small breaking change for clients. // We'll have to see how it works out. if msg.MessageType == microscep.PKCSReq || msg.MessageType == microscep.RenewalReq { - if err := auth.ValidateChallenge(ctx, challengePassword, transactionID); err != nil { + if err := auth.ValidateChallenge(ctx, csr, challengePassword, transactionID); err != nil { if errors.Is(err, provisioner.ErrSCEPChallengeInvalid) { return createFailureResponse(ctx, csr, msg, microscep.BadRequest, err) } diff --git a/scep/authority.go b/scep/authority.go index 9b548e40..5f8231db 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -503,9 +503,9 @@ func (a *Authority) GetCACaps(ctx context.Context) []string { return caps } -func (a *Authority) ValidateChallenge(ctx context.Context, challenge, transactionID string) error { +func (a *Authority) ValidateChallenge(ctx context.Context, csr *x509.CertificateRequest, challenge, transactionID string) error { p := provisionerFromContext(ctx) - return p.ValidateChallenge(ctx, challenge, transactionID) + return p.ValidateChallenge(ctx, csr, challenge, transactionID) } func (a *Authority) selectDecrypter(ctx context.Context) (cert *x509.Certificate, decrypter crypto.Decrypter, err error) { diff --git a/scep/provisioner.go b/scep/provisioner.go index 3ef4eceb..7b8116af 100644 --- a/scep/provisioner.go +++ b/scep/provisioner.go @@ -20,7 +20,7 @@ type Provisioner interface { GetDecrypter() (*x509.Certificate, crypto.Decrypter) GetSigner() (*x509.Certificate, crypto.Signer) GetContentEncryptionAlgorithm() int - ValidateChallenge(ctx context.Context, challenge, transactionID string) error + ValidateChallenge(ctx context.Context, csr *x509.CertificateRequest, challenge, transactionID string) error } // provisionerKey is the key type for storing and searching a diff --git a/webhook/options.go b/webhook/options.go index 86923709..66eb87a5 100644 --- a/webhook/options.go +++ b/webhook/options.go @@ -24,6 +24,9 @@ func NewRequestBody(options ...RequestBodyOption) (*RequestBody, error) { func WithX509CertificateRequest(cr *x509.CertificateRequest) RequestBodyOption { return func(rb *RequestBody) error { + if cr == nil { + return nil + } rb.X509CertificateRequest = &X509CertificateRequest{ CertificateRequest: x509util.NewCertificateRequestFromX509(cr), PublicKeyAlgorithm: cr.PublicKeyAlgorithm.String(),