github.com/hellobchain/third_party@v0.0.0-20230331131523-deb0478a2e52/cloudflare/cfssl/revoke/revoke.go (about) 1 // Package revoke provides functionality for checking the validity of 2 // a cert. Specifically, the temporal validity of the certificate is 3 // checked first, then any CRL and OCSP url in the cert is checked. 4 package revoke 5 6 import ( 7 "bytes" 8 "encoding/base64" 9 "encoding/pem" 10 "errors" 11 "github.com/hellobchain/newcryptosm" 12 "github.com/hellobchain/newcryptosm/http" 13 "github.com/hellobchain/newcryptosm/x509" 14 "github.com/hellobchain/newcryptosm/x509/pkix" 15 "io" 16 "io/ioutil" 17 neturl "net/url" 18 "sync" 19 "time" 20 21 "github.com/hellobchain/third_party/ocsp" 22 23 "github.com/hellobchain/third_party/cloudflare/cfssl/helpers" 24 "github.com/hellobchain/third_party/cloudflare/cfssl/log" 25 ) 26 27 // HardFail determines whether the failure to check the revocation 28 // status of a certificate (i.e. due to network failure) causes 29 // verification to fail (a hard failure). 30 var HardFail = false 31 32 // CRLSet associates a PKIX certificate list with the URL the CRL is 33 // fetched from. 34 var CRLSet = map[string]*pkix.CertificateList{} 35 var crlLock = new(sync.Mutex) 36 37 // We can't handle LDAP certificates, so this checks to see if the 38 // URL string points to an LDAP resource so that we can ignore it. 39 func ldapURL(url string) bool { 40 u, err := neturl.Parse(url) 41 if err != nil { 42 log.Warningf("error parsing url %s: %v", url, err) 43 return false 44 } 45 if u.Scheme == "ldap" { 46 return true 47 } 48 return false 49 } 50 51 // revCheck should check the certificate for any revocations. It 52 // returns a pair of booleans: the first indicates whether the certificate 53 // is revoked, the second indicates whether the revocations were 54 // successfully checked.. This leads to the following combinations: 55 // 56 // false, false: an error was encountered while checking revocations. 57 // 58 // false, true: the certificate was checked successfully and 59 // it is not revoked. 60 // 61 // true, true: the certificate was checked successfully and 62 // it is revoked. 63 // 64 // true, false: failure to check revocation status causes 65 // verification to fail 66 func revCheck(cert *x509.Certificate) (revoked, ok bool) { 67 for _, url := range cert.CRLDistributionPoints { 68 if ldapURL(url) { 69 log.Infof("skipping LDAP CRL: %s", url) 70 continue 71 } 72 73 if revoked, ok := certIsRevokedCRL(cert, url); !ok { 74 log.Warning("error checking revocation via CRL") 75 if HardFail { 76 return true, false 77 } 78 return false, false 79 } else if revoked { 80 log.Info("certificate is revoked via CRL") 81 return true, true 82 } 83 } 84 85 if revoked, ok := certIsRevokedOCSP(cert, HardFail); !ok { 86 log.Warning("error checking revocation via OCSP") 87 if HardFail { 88 return true, false 89 } 90 return false, false 91 } else if revoked { 92 log.Info("certificate is revoked via OCSP") 93 return true, true 94 } 95 96 return false, true 97 } 98 99 // fetchCRL fetches and parses a CRL. 100 func fetchCRL(url string) (*pkix.CertificateList, error) { 101 resp, err := http.Get(url) 102 if err != nil { 103 return nil, err 104 } else if resp.StatusCode >= 300 { 105 return nil, errors.New("failed to retrieve CRL") 106 } 107 108 body, err := crlRead(resp.Body) 109 if err != nil { 110 return nil, err 111 } 112 resp.Body.Close() 113 114 return x509.ParseCRL(body) 115 } 116 117 func getIssuer(cert *x509.Certificate) *x509.Certificate { 118 var issuer *x509.Certificate 119 var err error 120 for _, issuingCert := range cert.IssuingCertificateURL { 121 issuer, err = fetchRemote(issuingCert) 122 if err != nil { 123 continue 124 } 125 break 126 } 127 128 return issuer 129 130 } 131 132 // check a cert against a specific CRL. Returns the same bool pair 133 // as revCheck. 134 func certIsRevokedCRL(cert *x509.Certificate, url string) (revoked, ok bool) { 135 crl, ok := CRLSet[url] 136 if ok && crl == nil { 137 ok = false 138 crlLock.Lock() 139 delete(CRLSet, url) 140 crlLock.Unlock() 141 } 142 143 var shouldFetchCRL = true 144 if ok { 145 if !crl.HasExpired(time.Now()) { 146 shouldFetchCRL = false 147 } 148 } 149 150 issuer := getIssuer(cert) 151 152 if shouldFetchCRL { 153 var err error 154 crl, err = fetchCRL(url) 155 if err != nil { 156 log.Warningf("failed to fetch CRL: %v", err) 157 return false, false 158 } 159 160 // check CRL signature 161 if issuer != nil { 162 err = issuer.CheckCRLSignature(crl) 163 if err != nil { 164 log.Warningf("failed to verify CRL: %v", err) 165 return false, false 166 } 167 } 168 169 crlLock.Lock() 170 CRLSet[url] = crl 171 crlLock.Unlock() 172 } 173 174 for _, revoked := range crl.TBSCertList.RevokedCertificates { 175 if cert.SerialNumber.Cmp(revoked.SerialNumber) == 0 { 176 log.Info("Serial number match: intermediate is revoked.") 177 return true, true 178 } 179 } 180 181 return false, true 182 } 183 184 // VerifyCertificate ensures that the certificate passed in hasn't 185 // expired and checks the CRL for the server. 186 func VerifyCertificate(cert *x509.Certificate) (revoked, ok bool) { 187 if !time.Now().Before(cert.NotAfter) { 188 log.Infof("Certificate expired %s\n", cert.NotAfter) 189 return true, true 190 } else if !time.Now().After(cert.NotBefore) { 191 log.Infof("Certificate isn't valid until %s\n", cert.NotBefore) 192 return true, true 193 } 194 195 return revCheck(cert) 196 } 197 198 func fetchRemote(url string) (*x509.Certificate, error) { 199 resp, err := http.Get(url) 200 if err != nil { 201 return nil, err 202 } 203 204 in, err := remoteRead(resp.Body) 205 if err != nil { 206 return nil, err 207 } 208 resp.Body.Close() 209 210 p, _ := pem.Decode(in) 211 if p != nil { 212 return helpers.ParseCertificatePEM(in) 213 } 214 215 return x509.ParseCertificate(in) 216 } 217 218 var ocspOpts = ocsp.RequestOptions{ 219 Hash: newcryptosm.SHA1, 220 } 221 222 func certIsRevokedOCSP(leaf *x509.Certificate, strict bool) (revoked, ok bool) { 223 var err error 224 225 ocspURLs := leaf.OCSPServer 226 if len(ocspURLs) == 0 { 227 // OCSP not enabled for this certificate. 228 return false, true 229 } 230 231 issuer := getIssuer(leaf) 232 233 if issuer == nil { 234 return false, false 235 } 236 237 ocspRequest, err := ocsp.CreateRequest(leaf, issuer, &ocspOpts) 238 if err != nil { 239 return 240 } 241 242 for _, server := range ocspURLs { 243 resp, err := sendOCSPRequest(server, ocspRequest, leaf, issuer) 244 if err != nil { 245 if strict { 246 return 247 } 248 continue 249 } 250 251 // There wasn't an error fetching the OCSP status. 252 ok = true 253 254 if resp.Status != ocsp.Good { 255 // The certificate was revoked. 256 revoked = true 257 } 258 259 return 260 } 261 return 262 } 263 264 // sendOCSPRequest attempts to request an OCSP response from the 265 // server. The error only indicates a failure to *fetch* the 266 // certificate, and *does not* mean the certificate is valid. 267 func sendOCSPRequest(server string, req []byte, leaf, issuer *x509.Certificate) (*ocsp.Response, error) { 268 var resp *http.Response 269 var err error 270 if len(req) > 256 { 271 buf := bytes.NewBuffer(req) 272 resp, err = http.Post(server, "application/ocsp-request", buf) 273 } else { 274 reqURL := server + "/" + base64.StdEncoding.EncodeToString(req) 275 resp, err = http.Get(reqURL) 276 } 277 278 if err != nil { 279 return nil, err 280 } 281 282 if resp.StatusCode != http.StatusOK { 283 return nil, errors.New("failed to retrieve OSCP") 284 } 285 286 body, err := ocspRead(resp.Body) 287 if err != nil { 288 return nil, err 289 } 290 resp.Body.Close() 291 292 switch { 293 case bytes.Equal(body, ocsp.UnauthorizedErrorResponse): 294 return nil, errors.New("OSCP unauthorized") 295 case bytes.Equal(body, ocsp.MalformedRequestErrorResponse): 296 return nil, errors.New("OSCP malformed") 297 case bytes.Equal(body, ocsp.InternalErrorErrorResponse): 298 return nil, errors.New("OSCP internal error") 299 case bytes.Equal(body, ocsp.TryLaterErrorResponse): 300 return nil, errors.New("OSCP try later") 301 case bytes.Equal(body, ocsp.SigRequredErrorResponse): 302 return nil, errors.New("OSCP signature required") 303 } 304 305 return ocsp.ParseResponseForCert(body, leaf, issuer) 306 } 307 308 var crlRead = ioutil.ReadAll 309 310 // SetCRLFetcher sets the function to use to read from the http response body 311 func SetCRLFetcher(fn func(io.Reader) ([]byte, error)) { 312 crlRead = fn 313 } 314 315 var remoteRead = ioutil.ReadAll 316 317 // SetRemoteFetcher sets the function to use to read from the http response body 318 func SetRemoteFetcher(fn func(io.Reader) ([]byte, error)) { 319 remoteRead = fn 320 } 321 322 var ocspRead = ioutil.ReadAll 323 324 // SetOCSPFetcher sets the function to use to read from the http response body 325 func SetOCSPFetcher(fn func(io.Reader) ([]byte, error)) { 326 ocspRead = fn 327 }