Skip to content

Commit

Permalink
pki: fix tidy removal on revoked entries (#11367) (#11402)
Browse files Browse the repository at this point in the history
* pki: fix tidy removal on revoked entries

* add CL entry
  • Loading branch information
calvn authored Apr 19, 2021
1 parent 639b772 commit e7bd0b5
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 15 deletions.
75 changes: 70 additions & 5 deletions builtin/logical/pki/backend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"encoding/base64"
"encoding/pem"
"fmt"
"io/ioutil"
"math"
"math/big"
mathrand "math/rand"
Expand Down Expand Up @@ -2979,6 +2980,7 @@ func TestBackend_RevokePlusTidy_Intermediate(t *testing.T) {
secret, err = client.Logical().Write("pki/root/sign-intermediate", map[string]interface{}{
"permitted_dns_domains": ".myvault.com",
"csr": intermediateCSR,
"ttl": "10s",
})
if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -3017,14 +3019,77 @@ func TestBackend_RevokePlusTidy_Intermediate(t *testing.T) {
// Sleep a bit to make sure we're past the safety buffer
time.Sleep(2 * time.Second)

// Attempt to read the intermediate cert after revoke + tidy, and ensure
// that it's no longer present
secret, err = client.Logical().Read("pki/cert/" + intermediateCASerialColon)
// Get CRL and ensure the tidied cert is still in the list after the tidy
// operation since it's not past the NotAfter (ttl) value yet.
req := client.NewRequest("GET", "/v1/pki/crl")
resp, err := client.RawRequest(req)
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()

crlBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatalf("err: %s", err)
}
if len(crlBytes) == 0 {
t.Fatalf("expected CRL in response body")
}

crl, err := x509.ParseDERCRL(crlBytes)
if err != nil {
t.Fatal(err)
}

revokedCerts := crl.TBSCertList.RevokedCertificates
if len(revokedCerts) == 0 {
t.Fatal("expected CRL to be non-empty")
}

sn := certutil.GetHexFormatted(revokedCerts[0].SerialNumber.Bytes(), ":")
if sn != intermediateCertSerial {
t.Fatalf("expected: %v, got: %v", intermediateCertSerial, sn)
}

// Wait for cert to expire
time.Sleep(10 * time.Second)

// Issue a tidy on /pki
_, err = client.Logical().Write("pki/tidy", map[string]interface{}{
"tidy_cert_store": true,
"tidy_revoked_certs": true,
"safety_buffer": "1s",
})
if err != nil {
t.Fatal(err)
}

// Sleep a bit to make sure we're past the safety buffer
time.Sleep(2 * time.Second)

req = client.NewRequest("GET", "/v1/pki/crl")
resp, err = client.RawRequest(req)
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()

crlBytes, err = ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatalf("err: %s", err)
}
if len(crlBytes) == 0 {
t.Fatalf("expected CRL in response body")
}

crl, err = x509.ParseDERCRL(crlBytes)
if err != nil {
t.Fatal(err)
}
if secret != nil {
t.Fatalf("expected empty response data, got: %#v", secret.Data)

revokedCerts = crl.TBSCertList.RevokedCertificates
if len(revokedCerts) != 0 {
t.Fatal("expected CRL to be empty")
}

}
Expand Down
18 changes: 8 additions & 10 deletions builtin/logical/pki/path_tidy.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ func (b *backend) pathTidyWrite(ctx context.Context, req *logical.Request, d *fr
b.revokeStorageLock.Lock()
defer b.revokeStorageLock.Unlock()

tidiedRevoked := false
rebuildCRL := false

revokedSerials, err := req.Storage.List(ctx, "revoked/")
if err != nil {
Expand Down Expand Up @@ -178,24 +178,22 @@ func (b *backend) pathTidyWrite(ctx context.Context, req *logical.Request, d *fr
return errwrap.Wrapf(fmt.Sprintf("unable to parse stored revoked certificate with serial %q: {{err}}", serial), err)
}

// Remove the matched certificate entries from revoked/ and
// cert/ paths. We compare against both the NotAfter time
// within the cert itself and the time from the revocation
// entry, and perform tidy if either one tells us that the
// certificate has already been revoked.
now := time.Now()
if now.After(revokedCert.NotAfter.Add(bufferDuration)) || now.After(revInfo.RevocationTimeUTC.Add(bufferDuration)) {
// Only remove the entries from revoked/ and certs/ if we're
// past its NotAfter value. This is because we use the
// information on revoked/ to build the CRL and the
// information on certs/ for lookup.
if time.Now().After(revokedCert.NotAfter.Add(bufferDuration)) {
if err := req.Storage.Delete(ctx, "revoked/"+serial); err != nil {
return errwrap.Wrapf(fmt.Sprintf("error deleting serial %q from revoked list: {{err}}", serial), err)
}
if err := req.Storage.Delete(ctx, "certs/"+serial); err != nil {
return errwrap.Wrapf(fmt.Sprintf("error deleting serial %q from store when tidying revoked: {{err}}", serial), err)
}
tidiedRevoked = true
rebuildCRL = true
}
}

if tidiedRevoked {
if rebuildCRL {
if err := buildCRL(ctx, b, req, false); err != nil {
return err
}
Expand Down
3 changes: 3 additions & 0 deletions changelog/11367.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:bug
pki: Only remove revoked entry for certificates during tidy if they are past their NotAfter value
```

0 comments on commit e7bd0b5

Please sign in to comment.