diff --git a/builtin/logical/pki/cert_util.go b/builtin/logical/pki/cert_util.go index 21f6db8150..63c40c530f 100644 --- a/builtin/logical/pki/cert_util.go +++ b/builtin/logical/pki/cert_util.go @@ -171,8 +171,103 @@ func fetchCertBySerialBigInt(sc *storageContext, prefix string, serial *big.Int) return fetchCertBySerial(sc, prefix, serialFromBigInt(serial)) } +// fetchCertBySerial allows fetching certificates from the backend; it handles the slightly +// separate pathing for CRL, and revoked certificates. +// +// Support for fetching CA certificates was removed, due to the new issuers +// changes. func fetchCertBySerial(sc pki_backend.StorageContext, prefix, serial string) (*logical.StorageEntry, error) { - return issuing.FetchCertBySerial(sc, prefix, serial) + var path, legacyPath string + var err error + var certEntry *logical.StorageEntry + + hyphenSerial := parsing.NormalizeSerialForStorage(serial) + colonSerial := strings.ReplaceAll(strings.ToLower(serial), "-", ":") + + switch { + // Revoked goes first as otherwise crl get hardcoded paths which fail if + // we actually want revocation info + case strings.HasPrefix(prefix, "revoked/"): + legacyPath = "revoked/" + colonSerial + path = "revoked/" + hyphenSerial + case serial == issuing.LegacyCRLPath || serial == issuing.DeltaCRLPath || serial == issuing.UnifiedCRLPath || serial == issuing.UnifiedDeltaCRLPath: + warnings, err := sc.CrlBuilder().RebuildIfForced(sc) + if err != nil { + return nil, err + } + if len(warnings) > 0 { + msg := "During rebuild of CRL for cert fetch, got the following warnings:" + for index, warning := range warnings { + msg = fmt.Sprintf("%v\n %d. %v", msg, index+1, warning) + } + sc.Logger().Warn(msg) + } + + unified := serial == issuing.UnifiedCRLPath || serial == issuing.UnifiedDeltaCRLPath + path, err = issuing.ResolveIssuerCRLPath(sc.GetContext(), sc.GetStorage(), sc.UseLegacyBundleCaStorage(), issuing.DefaultRef, unified) + if err != nil { + return nil, err + } + + if serial == issuing.DeltaCRLPath || serial == issuing.UnifiedDeltaCRLPath { + if sc.UseLegacyBundleCaStorage() { + return nil, fmt.Errorf("refusing to serve delta CRL with legacy CA bundle") + } + + path += issuing.DeltaCRLPathSuffix + } + default: + legacyPath = issuing.PathCerts + colonSerial + path = issuing.PathCerts + hyphenSerial + } + + certEntry, err = sc.GetStorage().Get(sc.GetContext(), path) + if err != nil { + return nil, errutil.InternalError{Err: fmt.Sprintf("error fetching certificate %s: %s", serial, err)} + } + if certEntry != nil { + if certEntry.Value == nil || len(certEntry.Value) == 0 { + return nil, errutil.InternalError{Err: fmt.Sprintf("returned certificate bytes for serial %s were empty", serial)} + } + return certEntry, nil + } + + // If legacyPath is unset, it's going to be a CA or CRL; return immediately + if legacyPath == "" { + return nil, nil + } + + // Retrieve the old-style path. We disregard errors here because they + // always manifest on Windows, and thus the initial check for a revoked + // cert fails would return an error when the cert isn't revoked, preventing + // the happy path from working. + certEntry, _ = sc.GetStorage().Get(sc.GetContext(), legacyPath) + if certEntry == nil { + return nil, nil + } + if certEntry.Value == nil || len(certEntry.Value) == 0 { + return nil, errutil.InternalError{Err: fmt.Sprintf("returned certificate bytes for serial %s were empty", serial)} + } + + // Update old-style paths to new-style paths + certEntry.Key = path + certCounter := sc.GetCertificateCounter() + certsCounted := certCounter.IsInitialized() + if err = sc.GetStorage().Put(sc.GetContext(), certEntry); err != nil { + return nil, errutil.InternalError{Err: fmt.Sprintf("error saving certificate with serial %s to new location: %s", serial, err)} + } + if err = sc.GetStorage().Delete(sc.GetContext(), legacyPath); err != nil { + // If we fail here, we have an extra (copy) of a cert in storage, add to metrics: + switch { + case strings.HasPrefix(prefix, "revoked/"): + certCounter.IncrementTotalRevokedCertificatesCount(certsCounted, path) + default: + certCounter.IncrementTotalCertificatesCount(certsCounted, path) + } + return nil, errutil.InternalError{Err: fmt.Sprintf("error deleting certificate with serial %s from old location", serial)} + } + + return certEntry, nil } // Given a URI SAN, verify that it is allowed. diff --git a/builtin/logical/pki/crl_util.go b/builtin/logical/pki/crl_util.go index 9cd4c1ccce..def00a5f11 100644 --- a/builtin/logical/pki/crl_util.go +++ b/builtin/logical/pki/crl_util.go @@ -975,7 +975,7 @@ func revokeCert(sc *storageContext, config *pki_backend.CrlConfig, cert *x509.Ce } } - curRevInfo, err := revocation.FetchRevocationInfo(sc, colonSerial) + curRevInfo, err := fetchRevocationInfo(sc, colonSerial) if err != nil { return nil, err } diff --git a/builtin/logical/pki/issuing/issuers.go b/builtin/logical/pki/issuing/issuers.go index 440ef2b307..f2c9897ea1 100644 --- a/builtin/logical/pki/issuing/issuers.go +++ b/builtin/logical/pki/issuing/issuers.go @@ -14,7 +14,6 @@ import ( "github.com/hashicorp/vault/builtin/logical/pki/managed_key" "github.com/hashicorp/vault/builtin/logical/pki/parsing" - "github.com/hashicorp/vault/builtin/logical/pki/pki_backend" "github.com/hashicorp/vault/sdk/helper/certutil" "github.com/hashicorp/vault/sdk/helper/errutil" "github.com/hashicorp/vault/sdk/logical" @@ -564,102 +563,3 @@ func ResolveIssuerCRLPath(ctx context.Context, storage logical.Storage, useLegac return "crl", fmt.Errorf("unable to find CRL for issuer: id:%v/ref:%v", issuer, reference) } - -// FetchCertBySerial allows fetching certificates from the backend; it handles the slightly -// separate pathing for CRL, and revoked certificates. -// -// Support for fetching CA certificates was removed, due to the new issuers -// changes. -func FetchCertBySerial(sc pki_backend.StorageContext, prefix, serial string) (*logical.StorageEntry, error) { - var path, legacyPath string - var err error - var certEntry *logical.StorageEntry - - hyphenSerial := parsing.NormalizeSerialForStorage(serial) - colonSerial := strings.ReplaceAll(strings.ToLower(serial), "-", ":") - - switch { - // Revoked goes first as otherwise crl get hardcoded paths which fail if - // we actually want revocation info - case strings.HasPrefix(prefix, "revoked/"): - legacyPath = "revoked/" + colonSerial - path = "revoked/" + hyphenSerial - case serial == LegacyCRLPath || serial == DeltaCRLPath || serial == UnifiedCRLPath || serial == UnifiedDeltaCRLPath: - warnings, err := sc.CrlBuilder().RebuildIfForced(sc) - if err != nil { - return nil, err - } - if len(warnings) > 0 { - msg := "During rebuild of CRL for cert fetch, got the following warnings:" - for index, warning := range warnings { - msg = fmt.Sprintf("%v\n %d. %v", msg, index+1, warning) - } - sc.Logger().Warn(msg) - } - - unified := serial == UnifiedCRLPath || serial == UnifiedDeltaCRLPath - path, err = ResolveIssuerCRLPath(sc.GetContext(), sc.GetStorage(), sc.UseLegacyBundleCaStorage(), DefaultRef, unified) - if err != nil { - return nil, err - } - - if serial == DeltaCRLPath || serial == UnifiedDeltaCRLPath { - if sc.UseLegacyBundleCaStorage() { - return nil, fmt.Errorf("refusing to serve delta CRL with legacy CA bundle") - } - - path += DeltaCRLPathSuffix - } - default: - legacyPath = PathCerts + colonSerial - path = PathCerts + hyphenSerial - } - - certEntry, err = sc.GetStorage().Get(sc.GetContext(), path) - if err != nil { - return nil, errutil.InternalError{Err: fmt.Sprintf("error fetching certificate %s: %s", serial, err)} - } - if certEntry != nil { - if certEntry.Value == nil || len(certEntry.Value) == 0 { - return nil, errutil.InternalError{Err: fmt.Sprintf("returned certificate bytes for serial %s were empty", serial)} - } - return certEntry, nil - } - - // If legacyPath is unset, it's going to be a CA or CRL; return immediately - if legacyPath == "" { - return nil, nil - } - - // Retrieve the old-style path. We disregard errors here because they - // always manifest on Windows, and thus the initial check for a revoked - // cert fails would return an error when the cert isn't revoked, preventing - // the happy path from working. - certEntry, _ = sc.GetStorage().Get(sc.GetContext(), legacyPath) - if certEntry == nil { - return nil, nil - } - if certEntry.Value == nil || len(certEntry.Value) == 0 { - return nil, errutil.InternalError{Err: fmt.Sprintf("returned certificate bytes for serial %s were empty", serial)} - } - - // Update old-style paths to new-style paths - certEntry.Key = path - certCounter := sc.GetCertificateCounter() - certsCounted := certCounter.IsInitialized() - if err = sc.GetStorage().Put(sc.GetContext(), certEntry); err != nil { - return nil, errutil.InternalError{Err: fmt.Sprintf("error saving certificate with serial %s to new location: %s", serial, err)} - } - if err = sc.GetStorage().Delete(sc.GetContext(), legacyPath); err != nil { - // If we fail here, we have an extra (copy) of a cert in storage, add to metrics: - switch { - case strings.HasPrefix(prefix, "revoked/"): - certCounter.IncrementTotalRevokedCertificatesCount(certsCounted, path) - default: - certCounter.IncrementTotalCertificatesCount(certsCounted, path) - } - return nil, errutil.InternalError{Err: fmt.Sprintf("error deleting certificate with serial %s from old location", serial)} - } - - return certEntry, nil -} diff --git a/builtin/logical/pki/periodic.go b/builtin/logical/pki/periodic.go index 1d39fe1b54..05c37b660e 100644 --- a/builtin/logical/pki/periodic.go +++ b/builtin/logical/pki/periodic.go @@ -295,7 +295,7 @@ func doUnifiedTransferMissingDeltaWALSerials(sc *storageContext, clusterId strin func readRevocationEntryAndTransfer(sc *storageContext, serial string) error { hyphenSerial := normalizeSerial(serial) - revInfo, err := revocation.FetchRevocationInfo(sc, hyphenSerial) + revInfo, err := fetchRevocationInfo(sc, hyphenSerial) if err != nil { return fmt.Errorf("failed loading revocation entry for serial: %s: %w", serial, err) } diff --git a/builtin/logical/pki/revocation/revoke.go b/builtin/logical/pki/revocation/revoke.go index 5fc9fe4e7a..67272786e8 100644 --- a/builtin/logical/pki/revocation/revoke.go +++ b/builtin/logical/pki/revocation/revoke.go @@ -82,19 +82,3 @@ func FetchIssuerMapForRevocationChecking(sc pki_backend.StorageContext) (map[iss return issuerIDCertMap, nil } - -func FetchRevocationInfo(sc pki_backend.StorageContext, serial string) (*RevocationInfo, error) { - var revInfo *RevocationInfo - revEntry, err := issuing.FetchCertBySerial(sc, RevokedPath, serial) - if err != nil { - return nil, err - } - if revEntry != nil { - err = revEntry.DecodeJSON(&revInfo) - if err != nil { - return nil, fmt.Errorf("error decoding existing revocation info: %w", err) - } - } - - return revInfo, nil -} diff --git a/builtin/logical/pki/storage.go b/builtin/logical/pki/storage.go index 5070d2344e..44dd1ce7f5 100644 --- a/builtin/logical/pki/storage.go +++ b/builtin/logical/pki/storage.go @@ -19,6 +19,7 @@ import ( "github.com/hashicorp/vault/builtin/logical/pki/issuing" "github.com/hashicorp/vault/builtin/logical/pki/managed_key" "github.com/hashicorp/vault/builtin/logical/pki/pki_backend" + "github.com/hashicorp/vault/builtin/logical/pki/revocation" "github.com/hashicorp/vault/helper/constants" "github.com/hashicorp/vault/sdk/helper/certutil" "github.com/hashicorp/vault/sdk/helper/errutil" @@ -767,3 +768,19 @@ func (sc *storageContext) writeClusterConfig(config *issuing.ClusterConfigEntry) return sc.Storage.Put(sc.Context, entry) } + +func fetchRevocationInfo(sc pki_backend.StorageContext, serial string) (*revocation.RevocationInfo, error) { + var revInfo *revocation.RevocationInfo + revEntry, err := fetchCertBySerial(sc, revocation.RevokedPath, serial) + if err != nil { + return nil, err + } + if revEntry != nil { + err = revEntry.DecodeJSON(&revInfo) + if err != nil { + return nil, fmt.Errorf("error decoding existing revocation info: %w", err) + } + } + + return revInfo, nil +}