Initial commit after restructure
This commit is contained in:
139
firewall/inspection/tls/verify/ocsp.go
Normal file
139
firewall/inspection/tls/verify/ocsp.go
Normal file
@@ -0,0 +1,139 @@
|
||||
// Copyright Safing ICS Technologies GmbH. Use of this source code is governed by the AGPL license that can be found in the LICENSE file.
|
||||
|
||||
package verify
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/ocsp"
|
||||
|
||||
"github.com/Safing/safing-core/crypto/hash"
|
||||
"github.com/Safing/safing-core/log"
|
||||
)
|
||||
|
||||
var (
|
||||
ocspOpts = ocsp.RequestOptions{
|
||||
Hash: crypto.SHA1,
|
||||
}
|
||||
|
||||
dupOcspReqMap = make(map[string]*sync.Mutex)
|
||||
dupOcspReqLock sync.Mutex
|
||||
)
|
||||
|
||||
func UpdateOCSP(rCert *Cert, cert, ca *x509.Certificate, caID string) (*Cert, error) {
|
||||
|
||||
if rCert.NextOCSPUpdate > time.Now().Unix() {
|
||||
return rCert, nil
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
// get CA if necessary
|
||||
if ca == nil {
|
||||
ca, err = GetOrFetchIssuer(cert)
|
||||
if err != nil {
|
||||
return rCert, err
|
||||
}
|
||||
}
|
||||
if caID == "" {
|
||||
caID = hash.Sum(ca.RawSubjectPublicKeyInfo, hash.SHA2_256).Safe64()
|
||||
}
|
||||
|
||||
// dedup requests
|
||||
dupOcspReqLock.Lock()
|
||||
mutex, requestActive := dupOcspReqMap[rCert.FmtKey()]
|
||||
if !requestActive {
|
||||
mutex = new(sync.Mutex)
|
||||
mutex.Lock()
|
||||
dupOcspReqMap[rCert.FmtKey()] = mutex
|
||||
dupOcspReqLock.Unlock()
|
||||
} else {
|
||||
dupOcspReqLock.Unlock()
|
||||
log.Tracef("waiting for duplicate OCSP request for %s to complete", rCert.FmtKey())
|
||||
mutex.Lock()
|
||||
// only wait until duplicate request is finished, then return
|
||||
mutex.Unlock()
|
||||
// refetch rCert
|
||||
rCert, err = GetRevokedCert(caID, cert.SerialNumber)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to refetch rCert %s: %s", rCert.FmtKey(), err)
|
||||
}
|
||||
return rCert, nil
|
||||
}
|
||||
defer func() {
|
||||
dupOcspReqLock.Lock()
|
||||
delete(dupOcspReqMap, rCert.FmtKey())
|
||||
dupOcspReqLock.Unlock()
|
||||
mutex.Unlock()
|
||||
}()
|
||||
|
||||
// create request
|
||||
ocspRequest, err := ocsp.CreateRequest(cert, ca, &ocspOpts)
|
||||
if err != nil {
|
||||
return rCert, err
|
||||
}
|
||||
|
||||
// fetch
|
||||
var resp *ocsp.Response
|
||||
for _, url := range cert.OCSPServer {
|
||||
resp, err = fetchOCSP(url, ocspRequest, cert, ca)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
rCert.NextOCSPUpdate = resp.NextUpdate.Unix()
|
||||
if rCert.NextOCSPUpdate > rCert.Expires {
|
||||
rCert.Expires = rCert.NextOCSPUpdate
|
||||
}
|
||||
rCert.RevokedWithOCSP = resp.Status != ocsp.Good
|
||||
rCert.OCSPFailed = false
|
||||
rCert.Save()
|
||||
return rCert, nil
|
||||
}
|
||||
|
||||
rCert.NextOCSPUpdate = time.Now().Add(120 * time.Second).Unix()
|
||||
rCert.OCSPFailed = true
|
||||
rCert.Save()
|
||||
return rCert, fmt.Errorf("all OCSP servers failed, last error: %s", err)
|
||||
}
|
||||
|
||||
// fetchOCSP attempts to request an OCSP response from the
|
||||
// server. The error only indicates a failure to *fetch* the
|
||||
// certificate, and *does not* mean the certificate is valid.
|
||||
func fetchOCSP(server string, req []byte, cert, ca *x509.Certificate) (*ocsp.Response, error) {
|
||||
|
||||
var resp *http.Response
|
||||
var err error
|
||||
if len(req) > 256 {
|
||||
buf := bytes.NewBuffer(req)
|
||||
resp, err = http.Post(server, "application/ocsp-request", buf)
|
||||
} else {
|
||||
reqURL := server + "/" + base64.StdEncoding.EncodeToString(req)
|
||||
resp, err = http.Get(reqURL)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, errors.New("failed to retrieve OSCP")
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
return ocsp.ParseResponseForCert(body, cert, ca)
|
||||
}
|
||||
Reference in New Issue
Block a user