2020-07-24 21:39:38 +00:00
|
|
|
package common
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/tls"
|
2020-12-28 18:48:23 +00:00
|
|
|
"crypto/x509"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"path/filepath"
|
2020-07-24 21:39:38 +00:00
|
|
|
"sync"
|
|
|
|
|
|
|
|
"github.com/drakkan/sftpgo/logger"
|
2020-12-28 18:48:23 +00:00
|
|
|
"github.com/drakkan/sftpgo/utils"
|
2020-07-24 21:39:38 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// CertManager defines a TLS certificate manager
|
|
|
|
type CertManager struct {
|
|
|
|
certPath string
|
|
|
|
keyPath string
|
|
|
|
sync.RWMutex
|
2020-12-28 18:48:23 +00:00
|
|
|
cert *tls.Certificate
|
|
|
|
rootCAs *x509.CertPool
|
2020-07-24 21:39:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// LoadCertificate loads the configured x509 key pair
|
|
|
|
func (m *CertManager) LoadCertificate(logSender string) error {
|
|
|
|
newCert, err := tls.LoadX509KeyPair(m.certPath, m.keyPath)
|
|
|
|
if err != nil {
|
2020-07-29 19:56:56 +00:00
|
|
|
logger.Warn(logSender, "", "unable to load X509 key pair, cert file %#v key file %#v error: %v",
|
2020-07-24 21:39:38 +00:00
|
|
|
m.certPath, m.keyPath, err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
logger.Debug(logSender, "", "TLS certificate %#v successfully loaded", m.certPath)
|
|
|
|
m.Lock()
|
|
|
|
defer m.Unlock()
|
|
|
|
m.cert = &newCert
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetCertificateFunc returns the loaded certificate
|
|
|
|
func (m *CertManager) GetCertificateFunc() func(*tls.ClientHelloInfo) (*tls.Certificate, error) {
|
|
|
|
return func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
|
|
|
m.RLock()
|
|
|
|
defer m.RUnlock()
|
|
|
|
return m.cert, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-28 18:48:23 +00:00
|
|
|
// GetRootCAs returns the set of root certificate authorities that servers
|
|
|
|
// use if required to verify a client certificate
|
|
|
|
func (m *CertManager) GetRootCAs() *x509.CertPool {
|
|
|
|
return m.rootCAs
|
|
|
|
}
|
|
|
|
|
|
|
|
// LoadRootCAs tries to load root CA certificate authorities from the given paths
|
|
|
|
func (m *CertManager) LoadRootCAs(caCertificates []string, configDir string) error {
|
|
|
|
if len(caCertificates) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
rootCAs := x509.NewCertPool()
|
|
|
|
|
|
|
|
for _, rootCA := range caCertificates {
|
|
|
|
if !utils.IsFileInputValid(rootCA) {
|
|
|
|
return fmt.Errorf("invalid root CA certificate %#v", rootCA)
|
|
|
|
}
|
|
|
|
if rootCA != "" && !filepath.IsAbs(rootCA) {
|
|
|
|
rootCA = filepath.Join(configDir, rootCA)
|
|
|
|
}
|
|
|
|
crt, err := ioutil.ReadFile(rootCA)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if rootCAs.AppendCertsFromPEM(crt) {
|
|
|
|
logger.Debug(logSender, "", "TLS certificate authority %#v successfully loaded", rootCA)
|
|
|
|
} else {
|
|
|
|
err := fmt.Errorf("unable to load TLS certificate authority %#v", rootCA)
|
|
|
|
logger.Debug(logSender, "", "%v", err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m.rootCAs = rootCAs
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-07-24 21:39:38 +00:00
|
|
|
// NewCertManager creates a new certificate manager
|
|
|
|
func NewCertManager(certificateFile, certificateKeyFile, logSender string) (*CertManager, error) {
|
|
|
|
manager := &CertManager{
|
|
|
|
cert: nil,
|
|
|
|
certPath: certificateFile,
|
|
|
|
keyPath: certificateKeyFile,
|
|
|
|
}
|
|
|
|
err := manager.LoadCertificate(logSender)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return manager, nil
|
|
|
|
}
|