From 1ba5ec0bf2707bea17c84c4e642fcfda33740480 Mon Sep 17 00:00:00 2001 From: Alex Vaghin Date: Fri, 19 Aug 2016 14:32:09 +0200 Subject: [PATCH] acme: add StatusValid to expected values for Authorize Let's Encrypt may respond with the "valid" value in status field to authorization requests: https://github.com/letsencrypt/boulder/blob/0543691d9e2cbcc1b94baf3b9fab4e64ca2750ef/ra/ra.go#L373-L402 The spec isn't clear on this: https://tools.ietf.org/html/draft-ietf-acme-acme-02#section-6.4 Either way, this change follows common sense and should not produce unexpected results. Change-Id: Iac55aa1fb17d458f6d62e8da875ed86e3e4afbc3 Reviewed-on: https://go-review.googlesource.com/27432 Reviewed-by: Brad Fitzpatrick --- acme/autocert/autocert.go | 5 +++++ acme/internal/acme/acme.go | 6 +++++- acme/internal/acme/acme_test.go | 17 +++++++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/acme/autocert/autocert.go b/acme/autocert/autocert.go index 77788d0..af2403d 100644 --- a/acme/autocert/autocert.go +++ b/acme/autocert/autocert.go @@ -404,6 +404,11 @@ func (m *Manager) verify(ctx context.Context, domain string) error { if err != nil { return err } + // maybe don't need to at all + if authz.Status == acme.StatusValid { + return nil + } + // pick a challenge: prefer tls-sni-02 over tls-sni-01 // TODO: consider authz.Combinations var chal *acme.Challenge diff --git a/acme/internal/acme/acme.go b/acme/internal/acme/acme.go index 93140af..cee8f3e 100644 --- a/acme/internal/acme/acme.go +++ b/acme/internal/acme/acme.go @@ -302,6 +302,10 @@ func (c *Client) UpdateReg(ctx context.Context, a *Account) (*Account, error) { // Authorize performs the initial step in an authorization flow. // The caller will then need to choose from and perform a set of returned // challenges using c.Accept in order to successfully complete authorization. +// +// If an authorization has been previously granted, the CA may return +// a valid authorization (Authorization.Status is StatusValid). If so, the caller +// need not fulfill any challenge and can proceed to requesting a certificate. func (c *Client) Authorize(ctx context.Context, domain string) (*Authorization, error) { if _, err := c.Discover(ctx); err != nil { return nil, err @@ -331,7 +335,7 @@ func (c *Client) Authorize(ctx context.Context, domain string) (*Authorization, if err := json.NewDecoder(res.Body).Decode(&v); err != nil { return nil, fmt.Errorf("acme: invalid response: %v", err) } - if v.Status != StatusPending { + if v.Status != StatusPending && v.Status != StatusValid { return nil, fmt.Errorf("acme: unexpected status: %s", v.Status) } return v.authorization(res.Header.Get("Location")), nil diff --git a/acme/internal/acme/acme_test.go b/acme/internal/acme/acme_test.go index 4edeadc..fc54079 100644 --- a/acme/internal/acme/acme_test.go +++ b/acme/internal/acme/acme_test.go @@ -368,6 +368,23 @@ func TestAuthorize(t *testing.T) { } } +func TestAuthorizeValid(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == "HEAD" { + w.Header().Set("replay-nonce", "nonce") + return + } + w.WriteHeader(http.StatusCreated) + w.Write([]byte(`{"status":"valid"}`)) + })) + defer ts.Close() + client := Client{Key: testKey, dir: &Directory{AuthzURL: ts.URL}} + _, err := client.Authorize(context.Background(), "example.com") + if err != nil { + t.Errorf("err = %v", err) + } +} + func TestPollAuthz(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != "GET" {