configs.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  1. // Copyright (C) 2019-2023 Nicola Murino
  2. //
  3. // This program is free software: you can redistribute it and/or modify
  4. // it under the terms of the GNU Affero General Public License as published
  5. // by the Free Software Foundation, version 3.
  6. //
  7. // This program is distributed in the hope that it will be useful,
  8. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. // GNU Affero General Public License for more details.
  11. //
  12. // You should have received a copy of the GNU Affero General Public License
  13. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  14. package dataprovider
  15. import (
  16. "encoding/json"
  17. "fmt"
  18. "strings"
  19. "golang.org/x/crypto/ssh"
  20. "github.com/drakkan/sftpgo/v2/internal/kms"
  21. "github.com/drakkan/sftpgo/v2/internal/logger"
  22. "github.com/drakkan/sftpgo/v2/internal/util"
  23. )
  24. // Supported values for host keys, KEXs, ciphers, MACs
  25. var (
  26. supportedHostKeyAlgos = []string{ssh.KeyAlgoRSA, ssh.CertAlgoRSAv01}
  27. supportedKexAlgos = []string{
  28. "diffie-hellman-group16-sha512", "diffie-hellman-group18-sha512",
  29. "diffie-hellman-group14-sha1", "diffie-hellman-group1-sha1",
  30. "diffie-hellman-group-exchange-sha256", "diffie-hellman-group-exchange-sha1",
  31. }
  32. supportedCiphers = []string{
  33. "aes128-cbc", "aes192-cbc", "aes256-cbc",
  34. "3des-cbc",
  35. }
  36. supportedMACs = []string{
  37. "[email protected]", "hmac-sha2-512",
  38. "hmac-sha1", "hmac-sha1-96",
  39. }
  40. )
  41. // SFTPDConfigs defines configurations for SFTPD
  42. type SFTPDConfigs struct {
  43. HostKeyAlgos []string `json:"host_key_algos,omitempty"`
  44. Moduli []string `json:"moduli,omitempty"`
  45. KexAlgorithms []string `json:"kex_algorithms,omitempty"`
  46. Ciphers []string `json:"ciphers,omitempty"`
  47. MACs []string `json:"macs,omitempty"`
  48. }
  49. func (c *SFTPDConfigs) isEmpty() bool {
  50. if len(c.HostKeyAlgos) > 0 {
  51. return false
  52. }
  53. if len(c.Moduli) > 0 {
  54. return false
  55. }
  56. if len(c.KexAlgorithms) > 0 {
  57. return false
  58. }
  59. if len(c.Ciphers) > 0 {
  60. return false
  61. }
  62. if len(c.MACs) > 0 {
  63. return false
  64. }
  65. return true
  66. }
  67. // GetSupportedHostKeyAlgos returns the supported legacy host key algos
  68. func (*SFTPDConfigs) GetSupportedHostKeyAlgos() []string {
  69. return supportedHostKeyAlgos
  70. }
  71. // GetSupportedKEXAlgos returns the supported KEX algos
  72. func (*SFTPDConfigs) GetSupportedKEXAlgos() []string {
  73. return supportedKexAlgos
  74. }
  75. // GetSupportedCiphers returns the supported ciphers
  76. func (*SFTPDConfigs) GetSupportedCiphers() []string {
  77. return supportedCiphers
  78. }
  79. // GetSupportedMACs returns the supported MACs algos
  80. func (*SFTPDConfigs) GetSupportedMACs() []string {
  81. return supportedMACs
  82. }
  83. // GetModuliAsString returns moduli files as comma separated string
  84. func (c *SFTPDConfigs) GetModuliAsString() string {
  85. return strings.Join(c.Moduli, ",")
  86. }
  87. func (c *SFTPDConfigs) validate() error {
  88. for _, algo := range c.HostKeyAlgos {
  89. if !util.Contains(supportedHostKeyAlgos, algo) {
  90. return util.NewValidationError(fmt.Sprintf("unsupported host key algorithm %q", algo))
  91. }
  92. }
  93. for _, algo := range c.KexAlgorithms {
  94. if !util.Contains(supportedKexAlgos, algo) {
  95. return util.NewValidationError(fmt.Sprintf("unsupported KEX algorithm %q", algo))
  96. }
  97. }
  98. for _, cipher := range c.Ciphers {
  99. if !util.Contains(supportedCiphers, cipher) {
  100. return util.NewValidationError(fmt.Sprintf("unsupported cipher %q", cipher))
  101. }
  102. }
  103. for _, mac := range c.MACs {
  104. if !util.Contains(supportedMACs, mac) {
  105. return util.NewValidationError(fmt.Sprintf("unsupported MAC algorithm %q", mac))
  106. }
  107. }
  108. return nil
  109. }
  110. func (c *SFTPDConfigs) getACopy() *SFTPDConfigs {
  111. hostKeys := make([]string, len(c.HostKeyAlgos))
  112. copy(hostKeys, c.HostKeyAlgos)
  113. moduli := make([]string, len(c.Moduli))
  114. copy(moduli, c.Moduli)
  115. kexs := make([]string, len(c.KexAlgorithms))
  116. copy(kexs, c.KexAlgorithms)
  117. ciphers := make([]string, len(c.Ciphers))
  118. copy(ciphers, c.Ciphers)
  119. macs := make([]string, len(c.MACs))
  120. copy(macs, c.MACs)
  121. return &SFTPDConfigs{
  122. HostKeyAlgos: hostKeys,
  123. Moduli: moduli,
  124. KexAlgorithms: kexs,
  125. Ciphers: ciphers,
  126. MACs: macs,
  127. }
  128. }
  129. func validateSMTPSecret(secret *kms.Secret, name string) error {
  130. if secret.IsRedacted() {
  131. return util.NewValidationError(fmt.Sprintf("cannot save a redacted smtp %s", name))
  132. }
  133. if secret.IsEncrypted() && !secret.IsValid() {
  134. return util.NewValidationError(fmt.Sprintf("invalid encrypted smtp %s", name))
  135. }
  136. if !secret.IsEmpty() && !secret.IsValidInput() {
  137. return util.NewValidationError(fmt.Sprintf("invalid smtp %s", name))
  138. }
  139. if secret.IsPlain() {
  140. secret.SetAdditionalData("smtp")
  141. if err := secret.Encrypt(); err != nil {
  142. return util.NewValidationError(fmt.Sprintf("could not encrypt smtp %s: %v", name, err))
  143. }
  144. }
  145. return nil
  146. }
  147. // SMTPOAuth2 defines the SMTP related OAuth2 configurations
  148. type SMTPOAuth2 struct {
  149. Provider int `json:"provider,omitempty"`
  150. Tenant string `json:"tenant,omitempty"`
  151. ClientID string `json:"client_id,omitempty"`
  152. ClientSecret *kms.Secret `json:"client_secret,omitempty"`
  153. RefreshToken *kms.Secret `json:"refresh_token,omitempty"`
  154. }
  155. func (c *SMTPOAuth2) validate() error {
  156. if c.Provider < 0 || c.Provider > 1 {
  157. return util.NewValidationError("smtp oauth2: unsupported provider")
  158. }
  159. if c.ClientID == "" {
  160. return util.NewValidationError("smtp oauth2: client id is required")
  161. }
  162. if c.ClientSecret == nil {
  163. return util.NewValidationError("smtp oauth2: client secret is required")
  164. }
  165. if c.RefreshToken == nil {
  166. return util.NewValidationError("smtp oauth2: refresh token is required")
  167. }
  168. if err := validateSMTPSecret(c.ClientSecret, "oauth2 client secret"); err != nil {
  169. return err
  170. }
  171. return validateSMTPSecret(c.RefreshToken, "oauth2 refresh token")
  172. }
  173. func (c *SMTPOAuth2) getACopy() SMTPOAuth2 {
  174. var clientSecret, refreshToken *kms.Secret
  175. if c.ClientSecret != nil {
  176. clientSecret = c.ClientSecret.Clone()
  177. }
  178. if c.RefreshToken != nil {
  179. refreshToken = c.RefreshToken.Clone()
  180. }
  181. return SMTPOAuth2{
  182. Provider: c.Provider,
  183. Tenant: c.Tenant,
  184. ClientID: c.ClientID,
  185. ClientSecret: clientSecret,
  186. RefreshToken: refreshToken,
  187. }
  188. }
  189. // SMTPConfigs defines configuration for SMTP
  190. type SMTPConfigs struct {
  191. Host string `json:"host,omitempty"`
  192. Port int `json:"port,omitempty"`
  193. From string `json:"from,omitempty"`
  194. User string `json:"user,omitempty"`
  195. Password *kms.Secret `json:"password,omitempty"`
  196. AuthType int `json:"auth_type,omitempty"`
  197. Encryption int `json:"encryption,omitempty"`
  198. Domain string `json:"domain,omitempty"`
  199. Debug int `json:"debug,omitempty"`
  200. OAuth2 SMTPOAuth2 `json:"oauth2"`
  201. }
  202. // IsEmpty returns true if the configuration is empty
  203. func (c *SMTPConfigs) IsEmpty() bool {
  204. return c.Host == ""
  205. }
  206. func (c *SMTPConfigs) validate() error {
  207. if c.IsEmpty() {
  208. return nil
  209. }
  210. if c.Port <= 0 || c.Port > 65535 {
  211. return util.NewValidationError(fmt.Sprintf("smtp: invalid port %d", c.Port))
  212. }
  213. if c.Password != nil && c.AuthType != 3 {
  214. if err := validateSMTPSecret(c.Password, "password"); err != nil {
  215. return err
  216. }
  217. }
  218. if c.User == "" && c.From == "" {
  219. return util.NewValidationError("smtp: from address and user cannot both be empty")
  220. }
  221. if c.AuthType < 0 || c.AuthType > 3 {
  222. return util.NewValidationError(fmt.Sprintf("smtp: invalid auth type %d", c.AuthType))
  223. }
  224. if c.Encryption < 0 || c.Encryption > 2 {
  225. return util.NewValidationError(fmt.Sprintf("smtp: invalid encryption %d", c.Encryption))
  226. }
  227. if c.AuthType == 3 {
  228. c.Password = kms.NewEmptySecret()
  229. return c.OAuth2.validate()
  230. }
  231. c.OAuth2 = SMTPOAuth2{}
  232. return nil
  233. }
  234. // TryDecrypt tries to decrypt the encrypted secrets
  235. func (c *SMTPConfigs) TryDecrypt() error {
  236. if c.Password == nil {
  237. c.Password = kms.NewEmptySecret()
  238. }
  239. if c.OAuth2.ClientSecret == nil {
  240. c.OAuth2.ClientSecret = kms.NewEmptySecret()
  241. }
  242. if c.OAuth2.RefreshToken == nil {
  243. c.OAuth2.RefreshToken = kms.NewEmptySecret()
  244. }
  245. if err := c.Password.TryDecrypt(); err != nil {
  246. return fmt.Errorf("unable to decrypt smtp password: %w", err)
  247. }
  248. if err := c.OAuth2.ClientSecret.TryDecrypt(); err != nil {
  249. return fmt.Errorf("unable to decrypt smtp oauth2 client secret: %w", err)
  250. }
  251. if err := c.OAuth2.RefreshToken.TryDecrypt(); err != nil {
  252. return fmt.Errorf("unable to decrypt smtp oauth2 refresh token: %w", err)
  253. }
  254. return nil
  255. }
  256. func (c *SMTPConfigs) getACopy() *SMTPConfigs {
  257. var password *kms.Secret
  258. if c.Password != nil {
  259. password = c.Password.Clone()
  260. }
  261. return &SMTPConfigs{
  262. Host: c.Host,
  263. Port: c.Port,
  264. From: c.From,
  265. User: c.User,
  266. Password: password,
  267. AuthType: c.AuthType,
  268. Encryption: c.Encryption,
  269. Domain: c.Domain,
  270. Debug: c.Debug,
  271. OAuth2: c.OAuth2.getACopy(),
  272. }
  273. }
  274. // ACMEHTTP01Challenge defines the configuration for HTTP-01 challenge type
  275. type ACMEHTTP01Challenge struct {
  276. Port int `json:"port"`
  277. }
  278. // ACMEConfigs defines ACME related configuration
  279. type ACMEConfigs struct {
  280. Domain string `json:"domain"`
  281. Email string `json:"email"`
  282. HTTP01Challenge ACMEHTTP01Challenge `json:"http01_challenge"`
  283. // apply the certificate for the specified protocols:
  284. //
  285. // 1 means HTTP
  286. // 2 means FTP
  287. // 4 means WebDAV
  288. //
  289. // Protocols can be combined
  290. Protocols int `json:"protocols"`
  291. }
  292. func (c *ACMEConfigs) isEmpty() bool {
  293. return c.Domain == ""
  294. }
  295. func (c *ACMEConfigs) validate() error {
  296. if c.Domain == "" {
  297. return nil
  298. }
  299. if c.Email == "" && !util.IsEmailValid(c.Email) {
  300. return util.NewValidationError(fmt.Sprintf("acme: invalid email %q", c.Email))
  301. }
  302. if c.HTTP01Challenge.Port <= 0 || c.HTTP01Challenge.Port > 65535 {
  303. return util.NewValidationError(fmt.Sprintf("acme: invalid HTTP-01 challenge port %d", c.HTTP01Challenge.Port))
  304. }
  305. return nil
  306. }
  307. // HasProtocol returns true if the ACME certificate must be used for the specified protocol
  308. func (c *ACMEConfigs) HasProtocol(protocol string) bool {
  309. switch protocol {
  310. case protocolHTTP:
  311. return c.Protocols&1 != 0
  312. case protocolFTP:
  313. return c.Protocols&2 != 0
  314. case protocolWebDAV:
  315. return c.Protocols&4 != 0
  316. default:
  317. return false
  318. }
  319. }
  320. func (c *ACMEConfigs) getACopy() *ACMEConfigs {
  321. return &ACMEConfigs{
  322. Email: c.Email,
  323. Domain: c.Domain,
  324. HTTP01Challenge: ACMEHTTP01Challenge{Port: c.HTTP01Challenge.Port},
  325. Protocols: c.Protocols,
  326. }
  327. }
  328. // Configs allows to set configuration keys disabled by default without
  329. // modifying the config file or setting env vars
  330. type Configs struct {
  331. SFTPD *SFTPDConfigs `json:"sftpd,omitempty"`
  332. SMTP *SMTPConfigs `json:"smtp,omitempty"`
  333. ACME *ACMEConfigs `json:"acme,omitempty"`
  334. UpdatedAt int64 `json:"updated_at,omitempty"`
  335. }
  336. func (c *Configs) validate() error {
  337. if c.SFTPD != nil {
  338. if err := c.SFTPD.validate(); err != nil {
  339. return err
  340. }
  341. }
  342. if c.SMTP != nil {
  343. if err := c.SMTP.validate(); err != nil {
  344. return err
  345. }
  346. }
  347. if c.ACME != nil {
  348. if err := c.ACME.validate(); err != nil {
  349. return err
  350. }
  351. }
  352. return nil
  353. }
  354. // PrepareForRendering prepares configs for rendering.
  355. // It hides confidential data and set to nil the empty structs/secrets
  356. // so they are not serialized
  357. func (c *Configs) PrepareForRendering() {
  358. if c.SFTPD != nil && c.SFTPD.isEmpty() {
  359. c.SFTPD = nil
  360. }
  361. if c.SMTP != nil && c.SMTP.IsEmpty() {
  362. c.SMTP = nil
  363. }
  364. if c.ACME != nil && c.ACME.isEmpty() {
  365. c.ACME = nil
  366. }
  367. if c.SMTP != nil {
  368. if c.SMTP.Password != nil {
  369. c.SMTP.Password.Hide()
  370. if c.SMTP.Password.IsEmpty() {
  371. c.SMTP.Password = nil
  372. }
  373. }
  374. if c.SMTP.OAuth2.ClientSecret != nil {
  375. c.SMTP.OAuth2.ClientSecret.Hide()
  376. if c.SMTP.OAuth2.ClientSecret.IsEmpty() {
  377. c.SMTP.OAuth2.ClientSecret = nil
  378. }
  379. }
  380. if c.SMTP.OAuth2.RefreshToken != nil {
  381. c.SMTP.OAuth2.RefreshToken.Hide()
  382. if c.SMTP.OAuth2.RefreshToken.IsEmpty() {
  383. c.SMTP.OAuth2.RefreshToken = nil
  384. }
  385. }
  386. }
  387. }
  388. // SetNilsToEmpty sets nil fields to empty
  389. func (c *Configs) SetNilsToEmpty() {
  390. if c.SFTPD == nil {
  391. c.SFTPD = &SFTPDConfigs{}
  392. }
  393. if c.SMTP == nil {
  394. c.SMTP = &SMTPConfigs{}
  395. }
  396. if c.SMTP.Password == nil {
  397. c.SMTP.Password = kms.NewEmptySecret()
  398. }
  399. if c.SMTP.OAuth2.ClientSecret == nil {
  400. c.SMTP.OAuth2.ClientSecret = kms.NewEmptySecret()
  401. }
  402. if c.SMTP.OAuth2.RefreshToken == nil {
  403. c.SMTP.OAuth2.RefreshToken = kms.NewEmptySecret()
  404. }
  405. if c.ACME == nil {
  406. c.ACME = &ACMEConfigs{}
  407. }
  408. }
  409. // RenderAsJSON implements the renderer interface used within plugins
  410. func (c *Configs) RenderAsJSON(reload bool) ([]byte, error) {
  411. if reload {
  412. config, err := provider.getConfigs()
  413. if err != nil {
  414. providerLog(logger.LevelError, "unable to reload config overrides before rendering as json: %v", err)
  415. return nil, err
  416. }
  417. config.PrepareForRendering()
  418. return json.Marshal(config)
  419. }
  420. c.PrepareForRendering()
  421. return json.Marshal(c)
  422. }
  423. func (c *Configs) getACopy() Configs {
  424. var result Configs
  425. if c.SFTPD != nil {
  426. result.SFTPD = c.SFTPD.getACopy()
  427. }
  428. if c.SMTP != nil {
  429. result.SMTP = c.SMTP.getACopy()
  430. }
  431. if c.ACME != nil {
  432. result.ACME = c.ACME.getACopy()
  433. }
  434. result.UpdatedAt = c.UpdatedAt
  435. return result
  436. }