From e17ce39e3aed928bc1f1dde17d902f2c9272b2dc Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 15 Sep 2020 18:14:03 -0700 Subject: [PATCH] Add support for Revoke using CAS. --- authority/tls.go | 23 ++++++++++++++++++++++- db/db.go | 23 +++++++++++++++++++++++ db/simple.go | 5 +++++ 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/authority/tls.go b/authority/tls.go index dfb9b583..7405c1dc 100644 --- a/authority/tls.go +++ b/authority/tls.go @@ -352,7 +352,28 @@ func (a *Authority) Revoke(ctx context.Context, revokeOpts *RevokeOptions) error if provisioner.MethodFromContext(ctx) == provisioner.SSHRevokeMethod { err = a.db.RevokeSSH(rci) - } else { // default to revoke x509 + } else { + // Revoke an X.509 certificate using CAS. If the certificate is not + // provided we will try to read it from the db. + var revokedCert *x509.Certificate + if revokeOpts.Crt != nil { + revokedCert = revokeOpts.Crt + } else if rci.Serial != "" { + revokedCert, _ = a.db.GetCertificate(rci.Serial) + } + + // CAS operation, note that SoftCAS (default) is a noop. + // The revoke happens when this is stored in the db. + _, err = a.x509CAService.RevokeCertificate(&casapi.RevokeCertificateRequest{ + Certificate: revokedCert, + Reason: rci.Reason, + ReasonCode: rci.ReasonCode, + }) + if err != nil { + return errs.Wrap(http.StatusInternalServerError, err, "authority.Revoke", opts...) + } + + // Save as revoked in the Db. err = a.db.Revoke(rci) } switch err { diff --git a/db/db.go b/db/db.go index f6a15d92..77db7e97 100644 --- a/db/db.go +++ b/db/db.go @@ -47,6 +47,7 @@ type AuthDB interface { IsSSHRevoked(sn string) (bool, error) Revoke(rci *RevokedCertificateInfo) error RevokeSSH(rci *RevokedCertificateInfo) error + GetCertificate(serialNumber string) (*x509.Certificate, error) StoreCertificate(crt *x509.Certificate) error UseToken(id, tok string) (bool, error) IsSSHHost(name string) (bool, error) @@ -187,6 +188,19 @@ func (db *DB) RevokeSSH(rci *RevokedCertificateInfo) error { } } +// GetCertificate retrieves a certificate by the serial number. +func (db *DB) GetCertificate(serialNumber string) (*x509.Certificate, error) { + ans1Data, err := db.Get(certsTable, []byte(serialNumber)) + if err != nil { + return nil, errors.Wrap(err, "database Get error") + } + cert, err := x509.ParseCertificate(ans1Data) + if err != nil { + return nil, errors.Wrapf(err, "error parsing certificate with serial number %s", serialNumber) + } + return cert, nil +} + // StoreCertificate stores a certificate PEM. func (db *DB) StoreCertificate(crt *x509.Certificate) error { if err := db.Set(certsTable, []byte(crt.SerialNumber.String()), crt.Raw); err != nil { @@ -288,6 +302,7 @@ type MockAuthDB struct { MIsSSHRevoked func(string) (bool, error) MRevoke func(rci *RevokedCertificateInfo) error MRevokeSSH func(rci *RevokedCertificateInfo) error + MGetCertificate func(serialNumber string) (*x509.Certificate, error) MStoreCertificate func(crt *x509.Certificate) error MUseToken func(id, tok string) (bool, error) MIsSSHHost func(principal string) (bool, error) @@ -339,6 +354,14 @@ func (m *MockAuthDB) RevokeSSH(rci *RevokedCertificateInfo) error { return m.Err } +// GetCertificate mock. +func (m *MockAuthDB) GetCertificate(serialNumber string) (*x509.Certificate, error) { + if m.MGetCertificate != nil { + return m.MGetCertificate(serialNumber) + } + return m.Ret1.(*x509.Certificate), m.Err +} + // StoreCertificate mock. func (m *MockAuthDB) StoreCertificate(crt *x509.Certificate) error { if m.MStoreCertificate != nil { diff --git a/db/simple.go b/db/simple.go index 05626497..0e5426ec 100644 --- a/db/simple.go +++ b/db/simple.go @@ -46,6 +46,11 @@ func (s *SimpleDB) RevokeSSH(rci *RevokedCertificateInfo) error { return ErrNotImplemented } +// GetCertificate returns a "NotImplemented" error. +func (s *SimpleDB) GetCertificate(serialNumber string) (*x509.Certificate, error) { + return nil, ErrNotImplemented +} + // StoreCertificate returns a "NotImplemented" error. func (s *SimpleDB) StoreCertificate(crt *x509.Certificate) error { return ErrNotImplemented