revoke.go 8.3 KB

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