diff --git a/config/crl.go b/config/crl.go new file mode 100644 index 00000000..52966840 --- /dev/null +++ b/config/crl.go @@ -0,0 +1,75 @@ +// Copyright 2016 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file is built for It provides functionalities to parse raw CRLs, +// check their validity and verify their signatures against provided +// certificates (CAs). It also allows checking the revocation status of the +// provided certificates. + +// It is built for Go versions after and include Go version 1.19 as there +// are new functionality related to CRL handling. + +//go:build go1.19 +// +build go1.19 + +package config + +import ( + "crypto/x509" + "encoding/pem" + "fmt" + "time" +) + +// Parse all CRLs and return a slice of valid CRLs. +func parseCRLs(rawCRL []byte, cAs []*x509.Certificate) ([]*x509.RevocationList, error) { + var crls []*x509.RevocationList + for p, r := pem.Decode(rawCRL); p != nil; p, r = pem.Decode(r) { + if p.Type != "X509 CRL" { + return nil, fmt.Errorf("unable to decode raw certificate revocation list") + } + crl, err := x509.ParseRevocationList(p.Bytes) + if err != nil { + return nil, err + } + + // Check CRL exipry status. + if crl.NextUpdate.Before(time.Now()) { + return nil, fmt.Errorf("certificate revocation list is outdated") + } + + // Check each CRL is signed by any CA, if not, ignore the CRL. + // Otherwise, append to the valid slice of CRL. + for _, ca := range cAs { + err = crl.CheckSignatureFrom(ca) + if err == nil { + crls = append(crls, crl) + break + } + } + } + return crls, nil +} + +func validRevocationStatus(cAs []*x509.Certificate, cRLs []*x509.RevocationList) error { + for _, cert := range cAs { + for _, crl := range cRLs { + for _, revokedCertificate := range crl.RevokedCertificates { + if revokedCertificate.SerialNumber.Cmp(cert.SerialNumber) == 0 { + return fmt.Errorf("certificate was revoked") + } + } + } + } + return nil +} diff --git a/config/crl_deprecated.go b/config/crl_deprecated.go new file mode 100644 index 00000000..ffa6e483 --- /dev/null +++ b/config/crl_deprecated.go @@ -0,0 +1,76 @@ +// Copyright 2016 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file is built for It provides functionalities to parse raw CRLs, +// check their validity and verify their signatures against provided +// certificates (CAs). It also allows checking the revocation status of the +// provided certificates. + +// It is built for Go versions before 1.19 as there are deprecated +// functionality related to CRL handling. + +//go:build !go1.19 +// +build !go1.19 + +package config + +import ( + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "fmt" + "time" +) + +// Parse all CRLs and return a slice of valid CRLs. +func parseCRLs(rawCRL []byte, cAs []*x509.Certificate) ([]*pkix.CertificateList, error) { + var crls []*pkix.CertificateList + for p, r := pem.Decode(rawCRL); p != nil; p, r = pem.Decode(r) { + if p.Type != "X509 CRL" { + return nil, fmt.Errorf("unable to decode raw certificate revocation list") + } + crl, err := x509.ParseCRL(p.Bytes) + if err != nil { + return nil, err + } + + // Check CRL exipry status. + if crl.TBSCertList.NextUpdate.Before(time.Now()) { + return nil, fmt.Errorf("certificate revocation list is outdated") + } + + // Check each CRL is signed by any CA, if not, ignore the CRL. + // Otherwise, append to the valid slice of CRL. + for _, ca := range cAs { + err = ca.CheckCRLSignature(crl) + if err == nil { + crls = append(crls, crl) + break + } + } + } + return crls, nil +} + +func validRevocationStatus(cAs []*x509.Certificate, cRLs []*pkix.CertificateList) error { + for _, cert := range cAs { + for _, crl := range cRLs { + for _, revokedCertificate := range crl.TBSCertList.RevokedCertificates { + if revokedCertificate.SerialNumber.Cmp(cert.SerialNumber) == 0 { + return fmt.Errorf("certificate was revoked") + } + } + } + } + return nil +} diff --git a/config/http_config.go b/config/http_config.go index c632f9e3..65977710 100644 --- a/config/http_config.go +++ b/config/http_config.go @@ -1356,49 +1356,14 @@ func (c *TLSConfig) verifyPeerCertificate(rawCerts [][]byte, verifiedChains [][] // against valid CRLs. cAs = append(cAs, verifiedChains[0][0]) - for _, cert := range cAs { - for _, crl := range crlsList { - for _, revokedCertificate := range crl.RevokedCertificates { - if revokedCertificate.SerialNumber.Cmp(cert.SerialNumber) == 0 { - return fmt.Errorf("certificate was revoked") - } - } - } + err = validRevocationStatus(cAs, crlsList) + if err != nil { + return err } return nil } -// Parse all CRLs and return a slice of valid CRLs. -func parseCRLs(rawCRL []byte, cAs []*x509.Certificate) ([]*x509.RevocationList, error) { - var crls []*x509.RevocationList - for p, r := pem.Decode(rawCRL); p != nil; p, r = pem.Decode(r) { - if p.Type != "X509 CRL" { - return nil, fmt.Errorf("unable to decode raw certificate revocation list") - } - crl, err := x509.ParseRevocationList(p.Bytes) - if err != nil { - return nil, err - } - - // Check CRL exipry status. - if crl.NextUpdate.Before(time.Now()) { - return nil, fmt.Errorf("certificate revocation list is outdated") - } - - // Check each CRL is signed by any CA, if not, ignore the CRL. - // Otherwise, append to the valid slice of CRL. - for _, ca := range cAs { - err = crl.CheckSignatureFrom(ca) - if err == nil { - crls = append(crls, crl) - break - } - } - } - return crls, nil -} - // Parse raw certificates with padding structure. func parseCerts(rawCerts []byte) ([]*x509.Certificate, error) { var certList []*x509.Certificate diff --git a/config/http_config_test.go b/config/http_config_test.go index 5ee94f3d..efcfe6ba 100644 --- a/config/http_config_test.go +++ b/config/http_config_test.go @@ -2139,7 +2139,7 @@ func TestNewClientFromRevokedCertConfig(t *testing.T) { handler: func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, ExpectedMessage) }, - }, { // Full chain of CA and the single root CA revoke the intermediate CA certificate. + }, { // Full chain of CA and CRL, the single root CA revoke the intermediate CA certificate. clientConfig: HTTPClientConfig{ TLSConfig: TLSConfig{ CAFile: TLSCAChainPath, @@ -2152,7 +2152,8 @@ func TestNewClientFromRevokedCertConfig(t *testing.T) { handler: func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, ExpectedMessage) }, - }, { // Missing root in the CA Chain and the full chain of CRLs, the Intermediate CA revoke the peer certificate. + }, { // Missing root in the CA Chain and the full chain of CRLs, + // the Intermediate CA revoke the peer certificate. clientConfig: HTTPClientConfig{ TLSConfig: TLSConfig{ CAFile: TLSCACHainNoRootPath,