tlsutils.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. package common
  2. import (
  3. "crypto/tls"
  4. "crypto/x509"
  5. "crypto/x509/pkix"
  6. "fmt"
  7. "os"
  8. "path/filepath"
  9. "sync"
  10. "time"
  11. "github.com/drakkan/sftpgo/v2/logger"
  12. "github.com/drakkan/sftpgo/v2/util"
  13. )
  14. // CertManager defines a TLS certificate manager
  15. type CertManager struct {
  16. certPath string
  17. keyPath string
  18. configDir string
  19. logSender string
  20. sync.RWMutex
  21. caCertificates []string
  22. caRevocationLists []string
  23. cert *tls.Certificate
  24. rootCAs *x509.CertPool
  25. crls []*pkix.CertificateList
  26. }
  27. // Reload tries to reload certificate and CRLs
  28. func (m *CertManager) Reload() error {
  29. errCrt := m.loadCertificate()
  30. errCRLs := m.LoadCRLs()
  31. if errCrt != nil {
  32. return errCrt
  33. }
  34. return errCRLs
  35. }
  36. // LoadCertificate loads the configured x509 key pair
  37. func (m *CertManager) loadCertificate() error {
  38. newCert, err := tls.LoadX509KeyPair(m.certPath, m.keyPath)
  39. if err != nil {
  40. logger.Warn(m.logSender, "", "unable to load X509 key pair, cert file %#v key file %#v error: %v",
  41. m.certPath, m.keyPath, err)
  42. return err
  43. }
  44. logger.Debug(m.logSender, "", "TLS certificate %#v successfully loaded", m.certPath)
  45. m.Lock()
  46. defer m.Unlock()
  47. m.cert = &newCert
  48. return nil
  49. }
  50. // GetCertificateFunc returns the loaded certificate
  51. func (m *CertManager) GetCertificateFunc() func(*tls.ClientHelloInfo) (*tls.Certificate, error) {
  52. return func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
  53. m.RLock()
  54. defer m.RUnlock()
  55. return m.cert, nil
  56. }
  57. }
  58. // IsRevoked returns true if the specified certificate has been revoked
  59. func (m *CertManager) IsRevoked(crt *x509.Certificate, caCrt *x509.Certificate) bool {
  60. m.RLock()
  61. defer m.RUnlock()
  62. if crt == nil || caCrt == nil {
  63. logger.Warn(m.logSender, "", "unable to verify crt %v ca crt %v", crt, caCrt)
  64. return len(m.crls) > 0
  65. }
  66. for _, crl := range m.crls {
  67. if !crl.HasExpired(time.Now()) && caCrt.CheckCRLSignature(crl) == nil {
  68. for _, rc := range crl.TBSCertList.RevokedCertificates {
  69. if rc.SerialNumber.Cmp(crt.SerialNumber) == 0 {
  70. return true
  71. }
  72. }
  73. }
  74. }
  75. return false
  76. }
  77. // LoadCRLs tries to load certificate revocation lists from the given paths
  78. func (m *CertManager) LoadCRLs() error {
  79. if len(m.caRevocationLists) == 0 {
  80. return nil
  81. }
  82. var crls []*pkix.CertificateList
  83. for _, revocationList := range m.caRevocationLists {
  84. if !util.IsFileInputValid(revocationList) {
  85. return fmt.Errorf("invalid root CA revocation list %#v", revocationList)
  86. }
  87. if revocationList != "" && !filepath.IsAbs(revocationList) {
  88. revocationList = filepath.Join(m.configDir, revocationList)
  89. }
  90. crlBytes, err := os.ReadFile(revocationList)
  91. if err != nil {
  92. logger.Warn(m.logSender, "unable to read revocation list %#v", revocationList)
  93. return err
  94. }
  95. crl, err := x509.ParseCRL(crlBytes)
  96. if err != nil {
  97. logger.Warn(m.logSender, "unable to parse revocation list %#v", revocationList)
  98. return err
  99. }
  100. logger.Debug(m.logSender, "", "CRL %#v successfully loaded", revocationList)
  101. crls = append(crls, crl)
  102. }
  103. m.Lock()
  104. defer m.Unlock()
  105. m.crls = crls
  106. return nil
  107. }
  108. // GetRootCAs returns the set of root certificate authorities that servers
  109. // use if required to verify a client certificate
  110. func (m *CertManager) GetRootCAs() *x509.CertPool {
  111. m.RLock()
  112. defer m.RUnlock()
  113. return m.rootCAs
  114. }
  115. // LoadRootCAs tries to load root CA certificate authorities from the given paths
  116. func (m *CertManager) LoadRootCAs() error {
  117. if len(m.caCertificates) == 0 {
  118. return nil
  119. }
  120. rootCAs := x509.NewCertPool()
  121. for _, rootCA := range m.caCertificates {
  122. if !util.IsFileInputValid(rootCA) {
  123. return fmt.Errorf("invalid root CA certificate %#v", rootCA)
  124. }
  125. if rootCA != "" && !filepath.IsAbs(rootCA) {
  126. rootCA = filepath.Join(m.configDir, rootCA)
  127. }
  128. crt, err := os.ReadFile(rootCA)
  129. if err != nil {
  130. return err
  131. }
  132. if rootCAs.AppendCertsFromPEM(crt) {
  133. logger.Debug(m.logSender, "", "TLS certificate authority %#v successfully loaded", rootCA)
  134. } else {
  135. err := fmt.Errorf("unable to load TLS certificate authority %#v", rootCA)
  136. logger.Warn(m.logSender, "", "%v", err)
  137. return err
  138. }
  139. }
  140. m.Lock()
  141. defer m.Unlock()
  142. m.rootCAs = rootCAs
  143. return nil
  144. }
  145. // SetCACertificates sets the root CA authorities file paths.
  146. // This should not be changed at runtime
  147. func (m *CertManager) SetCACertificates(caCertificates []string) {
  148. m.caCertificates = caCertificates
  149. }
  150. // SetCARevocationLists sets the CA revocation lists file paths.
  151. // This should not be changed at runtime
  152. func (m *CertManager) SetCARevocationLists(caRevocationLists []string) {
  153. m.caRevocationLists = caRevocationLists
  154. }
  155. // NewCertManager creates a new certificate manager
  156. func NewCertManager(certificateFile, certificateKeyFile, configDir, logSender string) (*CertManager, error) {
  157. manager := &CertManager{
  158. cert: nil,
  159. certPath: certificateFile,
  160. keyPath: certificateKeyFile,
  161. configDir: configDir,
  162. logSender: logSender,
  163. }
  164. err := manager.loadCertificate()
  165. if err != nil {
  166. return nil, err
  167. }
  168. return manager, nil
  169. }