server.go 48 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313
  1. // Copyright (C) 2019 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 sftpd
  15. import (
  16. "bytes"
  17. "encoding/hex"
  18. "encoding/json"
  19. "errors"
  20. "fmt"
  21. "io"
  22. "io/fs"
  23. "net"
  24. "os"
  25. "path/filepath"
  26. "runtime/debug"
  27. "slices"
  28. "strings"
  29. "sync"
  30. "time"
  31. "github.com/pkg/sftp"
  32. "github.com/sftpgo/sdk/plugin/notifier"
  33. "golang.org/x/crypto/ssh"
  34. "github.com/drakkan/sftpgo/v2/internal/common"
  35. "github.com/drakkan/sftpgo/v2/internal/dataprovider"
  36. "github.com/drakkan/sftpgo/v2/internal/logger"
  37. "github.com/drakkan/sftpgo/v2/internal/metric"
  38. "github.com/drakkan/sftpgo/v2/internal/plugin"
  39. "github.com/drakkan/sftpgo/v2/internal/util"
  40. "github.com/drakkan/sftpgo/v2/internal/version"
  41. "github.com/drakkan/sftpgo/v2/internal/vfs"
  42. )
  43. const (
  44. defaultPrivateRSAKeyName = "id_rsa"
  45. defaultPrivateECDSAKeyName = "id_ecdsa"
  46. defaultPrivateEd25519KeyName = "id_ed25519"
  47. sourceAddressCriticalOption = "source-address"
  48. keyExchangeCurve25519SHA256LibSSH = "[email protected]"
  49. )
  50. var (
  51. supportedAlgos = ssh.SupportedAlgorithms()
  52. insecureAlgos = ssh.InsecureAlgorithms()
  53. sftpExtensions = []string{"[email protected]"}
  54. supportedHostKeyAlgos = append(supportedAlgos.HostKeys, insecureAlgos.HostKeys...)
  55. preferredHostKeyAlgos = []string{
  56. ssh.KeyAlgoRSASHA256, ssh.KeyAlgoRSASHA512,
  57. ssh.KeyAlgoECDSA256, ssh.KeyAlgoECDSA384, ssh.KeyAlgoECDSA521,
  58. ssh.KeyAlgoED25519,
  59. }
  60. supportedPublicKeyAlgos = append(supportedAlgos.PublicKeyAuths, insecureAlgos.PublicKeyAuths...)
  61. preferredPublicKeyAlgos = supportedAlgos.PublicKeyAuths
  62. supportedKexAlgos = append(supportedAlgos.KeyExchanges, insecureAlgos.KeyExchanges...)
  63. preferredKexAlgos = supportedAlgos.KeyExchanges
  64. supportedCiphers = append(supportedAlgos.Ciphers, insecureAlgos.Ciphers...)
  65. preferredCiphers = supportedAlgos.Ciphers
  66. supportedMACs = append(supportedAlgos.MACs, insecureAlgos.MACs...)
  67. preferredMACs = []string{
  68. ssh.HMACSHA256ETM, ssh.HMACSHA256,
  69. }
  70. revokedCertManager = revokedCertificates{
  71. certs: map[string]bool{},
  72. }
  73. sftpAuthError = newAuthenticationError(nil, "", "")
  74. )
  75. // Binding defines the configuration for a network listener
  76. type Binding struct {
  77. // The address to listen on. A blank value means listen on all available network interfaces.
  78. Address string `json:"address" mapstructure:"address"`
  79. // The port used for serving requests
  80. Port int `json:"port" mapstructure:"port"`
  81. // Apply the proxy configuration, if any, for this binding
  82. ApplyProxyConfig bool `json:"apply_proxy_config" mapstructure:"apply_proxy_config"`
  83. }
  84. // GetAddress returns the binding address
  85. func (b *Binding) GetAddress() string {
  86. return fmt.Sprintf("%s:%d", b.Address, b.Port)
  87. }
  88. // IsValid returns true if the binding port is > 0
  89. func (b *Binding) IsValid() bool {
  90. return b.Port > 0
  91. }
  92. // HasProxy returns true if the proxy protocol is active for this binding
  93. func (b *Binding) HasProxy() bool {
  94. return b.ApplyProxyConfig && common.Config.ProxyProtocol > 0
  95. }
  96. // Configuration for the SFTP server
  97. type Configuration struct {
  98. // Addresses and ports to bind to
  99. Bindings []Binding `json:"bindings" mapstructure:"bindings"`
  100. // Maximum number of authentication attempts permitted per connection.
  101. // If set to a negative number, the number of attempts is unlimited.
  102. // If set to zero, the number of attempts are limited to 6.
  103. MaxAuthTries int `json:"max_auth_tries" mapstructure:"max_auth_tries"`
  104. // HostKeys define the daemon's private host keys.
  105. // Each host key can be defined as a path relative to the configuration directory or an absolute one.
  106. // If empty or missing, the daemon will search or try to generate "id_rsa" and "id_ecdsa" host keys
  107. // inside the configuration directory.
  108. HostKeys []string `json:"host_keys" mapstructure:"host_keys"`
  109. // HostCertificates defines public host certificates.
  110. // Each certificate can be defined as a path relative to the configuration directory or an absolute one.
  111. // Certificate's public key must match a private host key otherwise it will be silently ignored.
  112. HostCertificates []string `json:"host_certificates" mapstructure:"host_certificates"`
  113. // HostKeyAlgorithms lists the public key algorithms that the server will accept for host
  114. // key authentication.
  115. HostKeyAlgorithms []string `json:"host_key_algorithms" mapstructure:"host_key_algorithms"`
  116. // KexAlgorithms specifies the available KEX (Key Exchange) algorithms in
  117. // preference order.
  118. KexAlgorithms []string `json:"kex_algorithms" mapstructure:"kex_algorithms"`
  119. // MinDHGroupExchangeKeySize defines the minimum key size to allow for the
  120. // key exchanges when using diffie-ellman-group-exchange-sha1 or sha256 key
  121. // exchange algorithms.
  122. MinDHGroupExchangeKeySize int `json:"min_dh_group_exchange_key_size" mapstructure:"min_dh_group_exchange_key_size"`
  123. // Ciphers specifies the ciphers allowed
  124. Ciphers []string `json:"ciphers" mapstructure:"ciphers"`
  125. // MACs Specifies the available MAC (message authentication code) algorithms
  126. // in preference order
  127. MACs []string `json:"macs" mapstructure:"macs"`
  128. // PublicKeyAlgorithms lists the supported public key algorithms for client authentication.
  129. PublicKeyAlgorithms []string `json:"public_key_algorithms" mapstructure:"public_key_algorithms"`
  130. // TrustedUserCAKeys specifies a list of public keys paths of certificate authorities
  131. // that are trusted to sign user certificates for authentication.
  132. // The paths can be absolute or relative to the configuration directory
  133. TrustedUserCAKeys []string `json:"trusted_user_ca_keys" mapstructure:"trusted_user_ca_keys"`
  134. // Path to a file containing the revoked user certificates.
  135. // This file must contain a JSON list with the public key fingerprints of the revoked certificates.
  136. // Example content:
  137. // ["SHA256:bsBRHC/xgiqBJdSuvSTNpJNLTISP/G356jNMCRYC5Es","SHA256:119+8cL/HH+NLMawRsJx6CzPF1I3xC+jpM60bQHXGE8"]
  138. RevokedUserCertsFile string `json:"revoked_user_certs_file" mapstructure:"revoked_user_certs_file"`
  139. // LoginBannerFile the contents of the specified file, if any, are sent to
  140. // the remote user before authentication is allowed.
  141. LoginBannerFile string `json:"login_banner_file" mapstructure:"login_banner_file"`
  142. // List of enabled SSH commands.
  143. // We support the following SSH commands:
  144. // - "scp". SCP is an experimental feature, we have our own SCP implementation since
  145. // we can't rely on scp system command to proper handle permissions, quota and
  146. // user's home dir restrictions.
  147. // The SCP protocol is quite simple but there is no official docs about it,
  148. // so we need more testing and feedbacks before enabling it by default.
  149. // We may not handle some borderline cases or have sneaky bugs.
  150. // Please do accurate tests yourself before enabling SCP and let us known
  151. // if something does not work as expected for your use cases.
  152. // SCP between two remote hosts is supported using the `-3` scp option.
  153. // - "md5sum", "sha1sum", "sha256sum", "sha384sum", "sha512sum". Useful to check message
  154. // digests for uploaded files. These commands are implemented inside SFTPGo so they
  155. // work even if the matching system commands are not available, for example on Windows.
  156. // - "cd", "pwd". Some mobile SFTP clients does not support the SFTP SSH_FXP_REALPATH and so
  157. // they use "cd" and "pwd" SSH commands to get the initial directory.
  158. // Currently `cd` do nothing and `pwd` always returns the "/" path.
  159. //
  160. // The following SSH commands are enabled by default: "md5sum", "sha1sum", "cd", "pwd".
  161. // "*" enables all supported SSH commands.
  162. EnabledSSHCommands []string `json:"enabled_ssh_commands" mapstructure:"enabled_ssh_commands"`
  163. // KeyboardInteractiveAuthentication specifies whether keyboard interactive authentication is allowed.
  164. // If no keyboard interactive hook or auth plugin is defined the default is to prompt for the user password and then the
  165. // one time authentication code, if defined.
  166. KeyboardInteractiveAuthentication bool `json:"keyboard_interactive_authentication" mapstructure:"keyboard_interactive_authentication"`
  167. // Absolute path to an external program or an HTTP URL to invoke for keyboard interactive authentication.
  168. // Leave empty to disable this authentication mode.
  169. KeyboardInteractiveHook string `json:"keyboard_interactive_auth_hook" mapstructure:"keyboard_interactive_auth_hook"`
  170. // PasswordAuthentication specifies whether password authentication is allowed.
  171. PasswordAuthentication bool `json:"password_authentication" mapstructure:"password_authentication"`
  172. certChecker *ssh.CertChecker
  173. parsedUserCAKeys []ssh.PublicKey
  174. }
  175. type authenticationError struct {
  176. err error
  177. loginMethod string
  178. username string
  179. }
  180. func (e *authenticationError) Error() string {
  181. return fmt.Sprintf("Authentication error: %v", e.err)
  182. }
  183. // Is reports if target matches
  184. func (e *authenticationError) Is(target error) bool {
  185. _, ok := target.(*authenticationError)
  186. return ok
  187. }
  188. // Unwrap returns the wrapped error
  189. func (e *authenticationError) Unwrap() error {
  190. return e.err
  191. }
  192. func (e *authenticationError) getLoginMethod() string {
  193. return e.loginMethod
  194. }
  195. func (e *authenticationError) getUsername() string {
  196. return e.username
  197. }
  198. func newAuthenticationError(err error, loginMethod, username string) *authenticationError {
  199. return &authenticationError{err: err, loginMethod: loginMethod, username: username}
  200. }
  201. // ShouldBind returns true if there is at least a valid binding
  202. func (c *Configuration) ShouldBind() bool {
  203. for _, binding := range c.Bindings {
  204. if binding.IsValid() {
  205. return true
  206. }
  207. }
  208. return false
  209. }
  210. func (c *Configuration) getServerConfig() *ssh.ServerConfig {
  211. serverConfig := &ssh.ServerConfig{
  212. NoClientAuth: false,
  213. MaxAuthTries: c.MaxAuthTries,
  214. PublicKeyCallback: func(conn ssh.ConnMetadata, pubKey ssh.PublicKey) (*ssh.Permissions, error) {
  215. sp, err := c.validatePublicKeyCredentials(conn, pubKey)
  216. var partialSuccess *ssh.PartialSuccessError
  217. if errors.As(err, &partialSuccess) {
  218. return sp, err
  219. }
  220. if err != nil {
  221. return nil, newAuthenticationError(fmt.Errorf("could not validate public key credentials: %w", err),
  222. dataprovider.SSHLoginMethodPublicKey, conn.User())
  223. }
  224. return sp, nil
  225. },
  226. ServerVersion: fmt.Sprintf("SSH-2.0-%s", version.GetServerVersion("_", false)),
  227. }
  228. if c.PasswordAuthentication {
  229. serverConfig.PasswordCallback = func(conn ssh.ConnMetadata, password []byte) (*ssh.Permissions, error) {
  230. return c.validatePasswordCredentials(conn, password, dataprovider.LoginMethodPassword)
  231. }
  232. serviceStatus.Authentications = append(serviceStatus.Authentications, dataprovider.LoginMethodPassword)
  233. }
  234. serviceStatus.Authentications = append(serviceStatus.Authentications, dataprovider.SSHLoginMethodPublicKey)
  235. return serverConfig
  236. }
  237. func (c *Configuration) updateSupportedAuthentications() {
  238. serviceStatus.Authentications = util.RemoveDuplicates(serviceStatus.Authentications, false)
  239. if slices.Contains(serviceStatus.Authentications, dataprovider.LoginMethodPassword) &&
  240. slices.Contains(serviceStatus.Authentications, dataprovider.SSHLoginMethodPublicKey) {
  241. serviceStatus.Authentications = append(serviceStatus.Authentications, dataprovider.SSHLoginMethodKeyAndPassword)
  242. }
  243. if slices.Contains(serviceStatus.Authentications, dataprovider.SSHLoginMethodKeyboardInteractive) &&
  244. slices.Contains(serviceStatus.Authentications, dataprovider.SSHLoginMethodPublicKey) {
  245. serviceStatus.Authentications = append(serviceStatus.Authentications, dataprovider.SSHLoginMethodKeyAndKeyboardInt)
  246. }
  247. }
  248. func (c *Configuration) loadFromProvider() error {
  249. configs, err := dataprovider.GetConfigs()
  250. if err != nil {
  251. return fmt.Errorf("unable to load config from provider: %w", err)
  252. }
  253. configs.SetNilsToEmpty()
  254. if len(configs.SFTPD.HostKeyAlgos) > 0 {
  255. if len(c.HostKeyAlgorithms) == 0 {
  256. c.HostKeyAlgorithms = preferredHostKeyAlgos
  257. }
  258. c.HostKeyAlgorithms = append(c.HostKeyAlgorithms, configs.SFTPD.HostKeyAlgos...)
  259. }
  260. if len(configs.SFTPD.PublicKeyAlgos) > 0 {
  261. if len(c.PublicKeyAlgorithms) == 0 {
  262. c.PublicKeyAlgorithms = preferredPublicKeyAlgos
  263. }
  264. c.PublicKeyAlgorithms = append(c.PublicKeyAlgorithms, configs.SFTPD.PublicKeyAlgos...)
  265. }
  266. if len(configs.SFTPD.KexAlgorithms) > 0 {
  267. if len(c.KexAlgorithms) == 0 {
  268. c.KexAlgorithms = preferredKexAlgos
  269. }
  270. c.KexAlgorithms = append(c.KexAlgorithms, configs.SFTPD.KexAlgorithms...)
  271. }
  272. if len(configs.SFTPD.Ciphers) > 0 {
  273. if len(c.Ciphers) == 0 {
  274. c.Ciphers = preferredCiphers
  275. }
  276. c.Ciphers = append(c.Ciphers, configs.SFTPD.Ciphers...)
  277. }
  278. if len(configs.SFTPD.MACs) > 0 {
  279. if len(c.MACs) == 0 {
  280. c.MACs = preferredMACs
  281. }
  282. c.MACs = append(c.MACs, configs.SFTPD.MACs...)
  283. }
  284. return nil
  285. }
  286. // Initialize the SFTP server and add a persistent listener to handle inbound SFTP connections.
  287. func (c *Configuration) Initialize(configDir string) error {
  288. if err := c.loadFromProvider(); err != nil {
  289. return fmt.Errorf("unable to load configs from provider: %w", err)
  290. }
  291. serviceStatus = ServiceStatus{}
  292. serverConfig := c.getServerConfig()
  293. if !c.ShouldBind() {
  294. return common.ErrNoBinding
  295. }
  296. ssh.SetDHKexServerMinBits(uint32(c.MinDHGroupExchangeKeySize))
  297. logger.Debug(logSender, "", "minimum key size allowed for diffie-ellman-group-exchange: %d",
  298. ssh.GetDHKexServerMinBits())
  299. sftp.SetSFTPExtensions(sftpExtensions...) //nolint:errcheck // we configure valid SFTP Extensions so we cannot get an error
  300. sftp.MaxFilelist = 250
  301. if err := c.configureSecurityOptions(serverConfig); err != nil {
  302. return err
  303. }
  304. if err := c.checkAndLoadHostKeys(configDir, serverConfig); err != nil {
  305. serviceStatus.HostKeys = nil
  306. return err
  307. }
  308. if err := c.initializeCertChecker(configDir); err != nil {
  309. return err
  310. }
  311. c.configureKeyboardInteractiveAuth(serverConfig)
  312. c.configureLoginBanner(serverConfig, configDir)
  313. c.checkSSHCommands()
  314. exitChannel := make(chan error, 1)
  315. serviceStatus.Bindings = nil
  316. for _, binding := range c.Bindings {
  317. if !binding.IsValid() {
  318. continue
  319. }
  320. serviceStatus.Bindings = append(serviceStatus.Bindings, binding)
  321. go func(binding Binding) {
  322. addr := binding.GetAddress()
  323. util.CheckTCP4Port(binding.Port)
  324. listener, err := net.Listen("tcp", addr)
  325. if err != nil {
  326. logger.Warn(logSender, "", "error starting listener on address %v: %v", addr, err)
  327. exitChannel <- err
  328. return
  329. }
  330. if binding.ApplyProxyConfig && common.Config.ProxyProtocol > 0 {
  331. proxyListener, err := common.Config.GetProxyListener(listener)
  332. if err != nil {
  333. logger.Warn(logSender, "", "error enabling proxy listener: %v", err)
  334. exitChannel <- err
  335. return
  336. }
  337. listener = proxyListener
  338. }
  339. exitChannel <- c.serve(listener, serverConfig)
  340. }(binding)
  341. }
  342. serviceStatus.IsActive = true
  343. serviceStatus.SSHCommands = c.EnabledSSHCommands
  344. c.updateSupportedAuthentications()
  345. return <-exitChannel
  346. }
  347. func (c *Configuration) serve(listener net.Listener, serverConfig *ssh.ServerConfig) error {
  348. logger.Info(logSender, "", "server listener registered, address: %s", listener.Addr().String())
  349. var tempDelay time.Duration // how long to sleep on accept failure
  350. for {
  351. conn, err := listener.Accept()
  352. if err != nil {
  353. // see https://github.com/golang/go/blob/4aa1efed4853ea067d665a952eee77c52faac774/src/net/http/server.go#L3046
  354. if ne, ok := err.(net.Error); ok && ne.Temporary() { //nolint:staticcheck
  355. if tempDelay == 0 {
  356. tempDelay = 5 * time.Millisecond
  357. } else {
  358. tempDelay *= 2
  359. }
  360. if max := 1 * time.Second; tempDelay > max {
  361. tempDelay = max
  362. }
  363. logger.Warn(logSender, "", "accept error: %v; retrying in %v", err, tempDelay)
  364. time.Sleep(tempDelay)
  365. continue
  366. }
  367. logger.Warn(logSender, "", "unrecoverable accept error: %v", err)
  368. return err
  369. }
  370. tempDelay = 0
  371. go c.AcceptInboundConnection(conn, serverConfig)
  372. }
  373. }
  374. func (c *Configuration) configureKeyAlgos(serverConfig *ssh.ServerConfig) error {
  375. if len(c.HostKeyAlgorithms) == 0 {
  376. c.HostKeyAlgorithms = preferredHostKeyAlgos
  377. } else {
  378. c.HostKeyAlgorithms = util.RemoveDuplicates(c.HostKeyAlgorithms, true)
  379. }
  380. for _, hostKeyAlgo := range c.HostKeyAlgorithms {
  381. if !slices.Contains(supportedHostKeyAlgos, hostKeyAlgo) {
  382. return fmt.Errorf("unsupported host key algorithm %q", hostKeyAlgo)
  383. }
  384. }
  385. if len(c.PublicKeyAlgorithms) > 0 {
  386. c.PublicKeyAlgorithms = util.RemoveDuplicates(c.PublicKeyAlgorithms, true)
  387. for _, algo := range c.PublicKeyAlgorithms {
  388. if !slices.Contains(supportedPublicKeyAlgos, algo) {
  389. return fmt.Errorf("unsupported public key authentication algorithm %q", algo)
  390. }
  391. }
  392. } else {
  393. c.PublicKeyAlgorithms = preferredPublicKeyAlgos
  394. }
  395. serverConfig.PublicKeyAuthAlgorithms = c.PublicKeyAlgorithms
  396. serviceStatus.PublicKeyAlgorithms = c.PublicKeyAlgorithms
  397. return nil
  398. }
  399. func (c *Configuration) checkKeyExchangeAlgorithms() {
  400. var kexs []string
  401. for _, k := range c.KexAlgorithms {
  402. if k == "diffie-hellman-group18-sha512" {
  403. logger.Warn(logSender, "", "KEX %q is not supported and will be ignored", k)
  404. continue
  405. }
  406. kexs = append(kexs, k)
  407. if strings.TrimSpace(k) == keyExchangeCurve25519SHA256LibSSH {
  408. kexs = append(kexs, ssh.KeyExchangeCurve25519SHA256)
  409. }
  410. if strings.TrimSpace(k) == ssh.KeyExchangeCurve25519SHA256 {
  411. kexs = append(kexs, keyExchangeCurve25519SHA256LibSSH)
  412. }
  413. }
  414. c.KexAlgorithms = util.RemoveDuplicates(kexs, true)
  415. }
  416. func (c *Configuration) configureSecurityOptions(serverConfig *ssh.ServerConfig) error {
  417. if err := c.configureKeyAlgos(serverConfig); err != nil {
  418. return err
  419. }
  420. if len(c.KexAlgorithms) > 0 {
  421. c.checkKeyExchangeAlgorithms()
  422. for _, kex := range c.KexAlgorithms {
  423. if kex == keyExchangeCurve25519SHA256LibSSH {
  424. continue
  425. }
  426. if !slices.Contains(supportedKexAlgos, kex) {
  427. return fmt.Errorf("unsupported key-exchange algorithm %q", kex)
  428. }
  429. }
  430. } else {
  431. c.KexAlgorithms = preferredKexAlgos
  432. c.checkKeyExchangeAlgorithms()
  433. }
  434. serverConfig.KeyExchanges = c.KexAlgorithms
  435. serviceStatus.KexAlgorithms = c.KexAlgorithms
  436. if len(c.Ciphers) > 0 {
  437. c.Ciphers = util.RemoveDuplicates(c.Ciphers, true)
  438. for _, cipher := range c.Ciphers {
  439. if !slices.Contains(supportedCiphers, cipher) {
  440. return fmt.Errorf("unsupported cipher %q", cipher)
  441. }
  442. }
  443. } else {
  444. c.Ciphers = preferredCiphers
  445. }
  446. serverConfig.Ciphers = c.Ciphers
  447. serviceStatus.Ciphers = c.Ciphers
  448. if len(c.MACs) > 0 {
  449. c.MACs = util.RemoveDuplicates(c.MACs, true)
  450. for _, mac := range c.MACs {
  451. if !slices.Contains(supportedMACs, mac) {
  452. return fmt.Errorf("unsupported MAC algorithm %q", mac)
  453. }
  454. }
  455. } else {
  456. c.MACs = preferredMACs
  457. }
  458. serverConfig.MACs = c.MACs
  459. serviceStatus.MACs = c.MACs
  460. return nil
  461. }
  462. func (c *Configuration) configureLoginBanner(serverConfig *ssh.ServerConfig, configDir string) {
  463. if c.LoginBannerFile != "" {
  464. bannerFilePath := c.LoginBannerFile
  465. if !filepath.IsAbs(bannerFilePath) {
  466. bannerFilePath = filepath.Join(configDir, bannerFilePath)
  467. }
  468. bannerContent, err := os.ReadFile(bannerFilePath)
  469. if err == nil {
  470. banner := util.BytesToString(bannerContent)
  471. serverConfig.BannerCallback = func(_ ssh.ConnMetadata) string {
  472. return banner
  473. }
  474. } else {
  475. logger.WarnToConsole("unable to read SFTPD login banner file: %v", err)
  476. logger.Warn(logSender, "", "unable to read login banner file: %v", err)
  477. }
  478. }
  479. }
  480. func (c *Configuration) configureKeyboardInteractiveAuth(serverConfig *ssh.ServerConfig) {
  481. if !c.KeyboardInteractiveAuthentication {
  482. return
  483. }
  484. if c.KeyboardInteractiveHook != "" {
  485. if !strings.HasPrefix(c.KeyboardInteractiveHook, "http") {
  486. if !filepath.IsAbs(c.KeyboardInteractiveHook) {
  487. c.KeyboardInteractiveAuthentication = false
  488. logger.WarnToConsole("invalid keyboard interactive authentication program: %q must be an absolute path",
  489. c.KeyboardInteractiveHook)
  490. logger.Warn(logSender, "", "invalid keyboard interactive authentication program: %q must be an absolute path",
  491. c.KeyboardInteractiveHook)
  492. return
  493. }
  494. _, err := os.Stat(c.KeyboardInteractiveHook)
  495. if err != nil {
  496. c.KeyboardInteractiveAuthentication = false
  497. logger.WarnToConsole("invalid keyboard interactive authentication program:: %v", err)
  498. logger.Warn(logSender, "", "invalid keyboard interactive authentication program:: %v", err)
  499. return
  500. }
  501. }
  502. }
  503. serverConfig.KeyboardInteractiveCallback = func(conn ssh.ConnMetadata, client ssh.KeyboardInteractiveChallenge) (*ssh.Permissions, error) {
  504. return c.validateKeyboardInteractiveCredentials(conn, client, dataprovider.SSHLoginMethodKeyboardInteractive, false)
  505. }
  506. serviceStatus.Authentications = append(serviceStatus.Authentications, dataprovider.SSHLoginMethodKeyboardInteractive)
  507. }
  508. // AcceptInboundConnection handles an inbound connection to the server instance and determines if the request should be served or not.
  509. func (c *Configuration) AcceptInboundConnection(conn net.Conn, config *ssh.ServerConfig) {
  510. defer func() {
  511. if r := recover(); r != nil {
  512. logger.Error(logSender, "", "panic in AcceptInboundConnection: %q stack trace: %v", r, string(debug.Stack()))
  513. }
  514. }()
  515. ipAddr := util.GetIPFromRemoteAddress(conn.RemoteAddr().String())
  516. common.Connections.AddClientConnection(ipAddr)
  517. defer common.Connections.RemoveClientConnection(ipAddr)
  518. if !canAcceptConnection(ipAddr) {
  519. conn.Close()
  520. return
  521. }
  522. // Before beginning a handshake must be performed on the incoming net.Conn
  523. // we'll set a Deadline for handshake to complete, the default is 2 minutes as OpenSSH
  524. conn.SetDeadline(time.Now().Add(handshakeTimeout)) //nolint:errcheck
  525. sconn, chans, reqs, err := ssh.NewServerConn(conn, config)
  526. if err != nil {
  527. logger.Debug(logSender, "", "failed to accept an incoming connection from ip %q: %v", ipAddr, err)
  528. checkAuthError(ipAddr, err)
  529. return
  530. }
  531. // handshake completed so remove the deadline, we'll use IdleTimeout configuration from now on
  532. conn.SetDeadline(time.Time{}) //nolint:errcheck
  533. go ssh.DiscardRequests(reqs)
  534. defer conn.Close()
  535. var user dataprovider.User
  536. // Unmarshal cannot fails here and even if it fails we'll have a user with no permissions
  537. json.Unmarshal(util.StringToBytes(sconn.Permissions.Extensions["sftpgo_user"]), &user) //nolint:errcheck
  538. loginType := sconn.Permissions.Extensions["sftpgo_login_method"]
  539. connectionID := hex.EncodeToString(sconn.SessionID())
  540. defer user.CloseFs() //nolint:errcheck
  541. if err = user.CheckFsRoot(connectionID); err != nil {
  542. logger.Warn(logSender, connectionID, "unable to check fs root for user %q: %v", user.Username, err)
  543. go discardAllChannels(chans, "invalid root fs", connectionID)
  544. return
  545. }
  546. logger.Log(logger.LevelInfo, common.ProtocolSSH, connectionID,
  547. "User %q logged in with %q, from ip %q, client version %q, negotiated algorithms: %+v",
  548. user.Username, loginType, ipAddr, util.BytesToString(sconn.ClientVersion()),
  549. sconn.Conn.(ssh.AlgorithmsConnMetadata).Algorithms())
  550. dataprovider.UpdateLastLogin(&user)
  551. sshConnection := common.NewSSHConnection(connectionID, conn)
  552. common.Connections.AddSSHConnection(sshConnection)
  553. defer common.Connections.RemoveSSHConnection(connectionID)
  554. channelCounter := int64(0)
  555. for newChannel := range chans {
  556. // If its not a session channel we just move on because its not something we
  557. // know how to handle at this point.
  558. if newChannel.ChannelType() != "session" {
  559. logger.Log(logger.LevelDebug, common.ProtocolSSH, connectionID, "received an unknown channel type: %v",
  560. newChannel.ChannelType())
  561. newChannel.Reject(ssh.UnknownChannelType, "unknown channel type") //nolint:errcheck
  562. continue
  563. }
  564. channel, requests, err := newChannel.Accept()
  565. if err != nil {
  566. logger.Log(logger.LevelWarn, common.ProtocolSSH, connectionID, "could not accept a channel: %v", err)
  567. continue
  568. }
  569. channelCounter++
  570. sshConnection.UpdateLastActivity()
  571. // Channels have a type that is dependent on the protocol. For SFTP this is "subsystem"
  572. // with a payload that (should) be "sftp". Discard anything else we receive ("pty", "shell", etc)
  573. go func(in <-chan *ssh.Request, counter int64) {
  574. for req := range in {
  575. ok := false
  576. connID := fmt.Sprintf("%s_%d", connectionID, counter)
  577. switch req.Type {
  578. case "subsystem":
  579. if util.BytesToString(req.Payload[4:]) == "sftp" {
  580. ok = true
  581. connection := &Connection{
  582. BaseConnection: common.NewBaseConnection(connID, common.ProtocolSFTP, conn.LocalAddr().String(),
  583. conn.RemoteAddr().String(), user),
  584. ClientVersion: util.BytesToString(sconn.ClientVersion()),
  585. RemoteAddr: conn.RemoteAddr(),
  586. LocalAddr: conn.LocalAddr(),
  587. channel: channel,
  588. }
  589. go c.handleSftpConnection(channel, connection)
  590. }
  591. case "exec":
  592. // protocol will be set later inside processSSHCommand it could be SSH or SCP
  593. connection := Connection{
  594. BaseConnection: common.NewBaseConnection(connID, "sshd_exec", conn.LocalAddr().String(),
  595. conn.RemoteAddr().String(), user),
  596. ClientVersion: util.BytesToString(sconn.ClientVersion()),
  597. RemoteAddr: conn.RemoteAddr(),
  598. LocalAddr: conn.LocalAddr(),
  599. channel: channel,
  600. }
  601. ok = processSSHCommand(req.Payload, &connection, c.EnabledSSHCommands)
  602. }
  603. if req.WantReply {
  604. req.Reply(ok, nil) //nolint:errcheck
  605. }
  606. }
  607. }(requests, channelCounter)
  608. }
  609. }
  610. func (c *Configuration) handleSftpConnection(channel ssh.Channel, connection *Connection) {
  611. defer func() {
  612. if r := recover(); r != nil {
  613. logger.Error(logSender, "", "panic in handleSftpConnection: %q stack trace: %v", r, string(debug.Stack()))
  614. }
  615. }()
  616. if err := common.Connections.Add(connection); err != nil {
  617. errClose := connection.Disconnect()
  618. logger.Info(logSender, "", "unable to add connection: %v, close err: %v", err, errClose)
  619. return
  620. }
  621. defer common.Connections.Remove(connection.GetID())
  622. // Create the server instance for the channel using the handler we created above.
  623. server := sftp.NewRequestServer(channel, c.createHandlers(connection), sftp.WithRSAllocator(),
  624. sftp.WithStartDirectory(connection.User.Filters.StartDirectory))
  625. defer server.Close()
  626. if err := server.Serve(); errors.Is(err, io.EOF) {
  627. exitStatus := sshSubsystemExitStatus{Status: uint32(0)}
  628. _, err = channel.SendRequest("exit-status", false, ssh.Marshal(&exitStatus))
  629. connection.Log(logger.LevelInfo, "connection closed, sent exit status %+v error: %v", exitStatus, err)
  630. } else if err != nil {
  631. connection.Log(logger.LevelError, "connection closed with error: %v", err)
  632. }
  633. }
  634. func (c *Configuration) createHandlers(connection *Connection) sftp.Handlers {
  635. return sftp.Handlers{
  636. FileGet: connection,
  637. FilePut: connection,
  638. FileCmd: connection,
  639. FileList: connection,
  640. }
  641. }
  642. func canAcceptConnection(ip string) bool {
  643. if common.IsBanned(ip, common.ProtocolSSH) {
  644. logger.Log(logger.LevelDebug, common.ProtocolSSH, "", "connection refused, ip %q is banned", ip)
  645. return false
  646. }
  647. if err := common.Connections.IsNewConnectionAllowed(ip, common.ProtocolSSH); err != nil {
  648. logger.Log(logger.LevelDebug, common.ProtocolSSH, "", "connection not allowed from ip %q: %v", ip, err)
  649. return false
  650. }
  651. _, err := common.LimitRate(common.ProtocolSSH, ip)
  652. if err != nil {
  653. return false
  654. }
  655. if err := common.Config.ExecutePostConnectHook(ip, common.ProtocolSSH); err != nil {
  656. return false
  657. }
  658. return true
  659. }
  660. func discardAllChannels(in <-chan ssh.NewChannel, message, connectionID string) {
  661. for req := range in {
  662. err := req.Reject(ssh.ConnectionFailed, message)
  663. logger.Debug(logSender, connectionID, "discarded channel request, message %q err: %v", message, err)
  664. }
  665. }
  666. func checkAuthError(ip string, err error) {
  667. var authErrors *ssh.ServerAuthError
  668. if errors.As(err, &authErrors) {
  669. // check public key auth errors here
  670. for _, err := range authErrors.Errors {
  671. var sftpAuthErr *authenticationError
  672. if errors.As(err, &sftpAuthErr) {
  673. if sftpAuthErr.getLoginMethod() == dataprovider.SSHLoginMethodPublicKey {
  674. event := common.HostEventLoginFailed
  675. logEv := notifier.LogEventTypeLoginFailed
  676. if errors.Is(err, util.ErrNotFound) {
  677. event = common.HostEventUserNotFound
  678. logEv = notifier.LogEventTypeLoginNoUser
  679. }
  680. common.AddDefenderEvent(ip, common.ProtocolSSH, event)
  681. plugin.Handler.NotifyLogEvent(logEv, common.ProtocolSSH, sftpAuthErr.getUsername(), ip, "", err)
  682. return
  683. }
  684. }
  685. }
  686. } else {
  687. logger.ConnectionFailedLog("", ip, dataprovider.LoginMethodNoAuthTried, common.ProtocolSSH, err.Error())
  688. metric.AddNoAuthTried()
  689. common.AddDefenderEvent(ip, common.ProtocolSSH, common.HostEventNoLoginTried)
  690. dataprovider.ExecutePostLoginHook(&dataprovider.User{}, dataprovider.LoginMethodNoAuthTried, ip, common.ProtocolSSH, err)
  691. logEv := notifier.LogEventTypeNoLoginTried
  692. var negotiationError *ssh.AlgorithmNegotiationError
  693. if errors.As(err, &negotiationError) {
  694. logEv = notifier.LogEventTypeNotNegotiated
  695. }
  696. plugin.Handler.NotifyLogEvent(logEv, common.ProtocolSSH, "", ip, "", err)
  697. }
  698. }
  699. func loginUser(user *dataprovider.User, loginMethod, publicKey string, conn ssh.ConnMetadata) (*ssh.Permissions, error) {
  700. connectionID := ""
  701. if conn != nil {
  702. connectionID = hex.EncodeToString(conn.SessionID())
  703. }
  704. if !filepath.IsAbs(user.HomeDir) {
  705. logger.Warn(logSender, connectionID, "user %q has an invalid home dir: %q. Home dir must be an absolute path, login not allowed",
  706. user.Username, user.HomeDir)
  707. return nil, fmt.Errorf("cannot login user with invalid home dir: %q", user.HomeDir)
  708. }
  709. if slices.Contains(user.Filters.DeniedProtocols, common.ProtocolSSH) {
  710. logger.Info(logSender, connectionID, "cannot login user %q, protocol SSH is not allowed", user.Username)
  711. return nil, fmt.Errorf("protocol SSH is not allowed for user %q", user.Username)
  712. }
  713. if user.MaxSessions > 0 {
  714. activeSessions := common.Connections.GetActiveSessions(user.Username)
  715. if activeSessions >= user.MaxSessions {
  716. logger.Info(logSender, "", "authentication refused for user: %q, too many open sessions: %v/%v", user.Username,
  717. activeSessions, user.MaxSessions)
  718. return nil, fmt.Errorf("too many open sessions: %v", activeSessions)
  719. }
  720. }
  721. if !user.IsLoginMethodAllowed(loginMethod, common.ProtocolSSH) {
  722. logger.Info(logSender, connectionID, "cannot login user %q, login method %q is not allowed",
  723. user.Username, loginMethod)
  724. return nil, fmt.Errorf("login method %q is not allowed for user %q", loginMethod, user.Username)
  725. }
  726. if user.MustSetSecondFactorForProtocol(common.ProtocolSSH) {
  727. logger.Info(logSender, connectionID, "cannot login user %q, second factor authentication is not set",
  728. user.Username)
  729. return nil, fmt.Errorf("second factor authentication is not set for user %q", user.Username)
  730. }
  731. remoteAddr := util.GetIPFromRemoteAddress(conn.RemoteAddr().String())
  732. if !user.IsLoginFromAddrAllowed(remoteAddr) {
  733. logger.Info(logSender, connectionID, "cannot login user %q, remote address is not allowed: %v",
  734. user.Username, remoteAddr)
  735. return nil, fmt.Errorf("login for user %q is not allowed from this address: %v", user.Username, remoteAddr)
  736. }
  737. json, err := json.Marshal(user)
  738. if err != nil {
  739. logger.Warn(logSender, connectionID, "error serializing user info: %v, authentication rejected", err)
  740. return nil, err
  741. }
  742. if publicKey != "" {
  743. loginMethod = fmt.Sprintf("%v: %v", loginMethod, publicKey)
  744. }
  745. p := &ssh.Permissions{}
  746. p.Extensions = make(map[string]string)
  747. p.Extensions["sftpgo_user"] = util.BytesToString(json)
  748. p.Extensions["sftpgo_login_method"] = loginMethod
  749. return p, nil
  750. }
  751. func (c *Configuration) checkSSHCommands() {
  752. if slices.Contains(c.EnabledSSHCommands, "*") {
  753. c.EnabledSSHCommands = GetSupportedSSHCommands()
  754. return
  755. }
  756. sshCommands := []string{}
  757. for _, command := range c.EnabledSSHCommands {
  758. command = strings.TrimSpace(command)
  759. if slices.Contains(supportedSSHCommands, command) {
  760. sshCommands = append(sshCommands, command)
  761. } else {
  762. logger.Warn(logSender, "", "unsupported ssh command: %q ignored", command)
  763. logger.WarnToConsole("unsupported ssh command: %q ignored", command)
  764. }
  765. }
  766. c.EnabledSSHCommands = sshCommands
  767. logger.Debug(logSender, "", "enabled SSH commands %v", c.EnabledSSHCommands)
  768. }
  769. func (c *Configuration) generateDefaultHostKeys(configDir string) error {
  770. var err error
  771. defaultHostKeys := []string{defaultPrivateRSAKeyName, defaultPrivateECDSAKeyName, defaultPrivateEd25519KeyName}
  772. for _, k := range defaultHostKeys {
  773. autoFile := filepath.Join(configDir, k)
  774. if _, err = os.Stat(autoFile); errors.Is(err, fs.ErrNotExist) {
  775. logger.Info(logSender, "", "No host keys configured and %q does not exist; try to create a new host key", autoFile)
  776. logger.InfoToConsole("No host keys configured and %q does not exist; try to create a new host key", autoFile)
  777. if k == defaultPrivateRSAKeyName {
  778. err = util.GenerateRSAKeys(autoFile)
  779. } else if k == defaultPrivateECDSAKeyName {
  780. err = util.GenerateECDSAKeys(autoFile)
  781. } else {
  782. err = util.GenerateEd25519Keys(autoFile)
  783. }
  784. if err != nil {
  785. logger.Warn(logSender, "", "error creating host key %q: %v", autoFile, err)
  786. logger.WarnToConsole("error creating host key %q: %v", autoFile, err)
  787. return err
  788. }
  789. }
  790. c.HostKeys = append(c.HostKeys, k)
  791. }
  792. return err
  793. }
  794. func (c *Configuration) checkHostKeyAutoGeneration(configDir string) error {
  795. for _, k := range c.HostKeys {
  796. k = strings.TrimSpace(k)
  797. if filepath.IsAbs(k) {
  798. if _, err := os.Stat(k); errors.Is(err, fs.ErrNotExist) {
  799. keyName := filepath.Base(k)
  800. switch keyName {
  801. case defaultPrivateRSAKeyName:
  802. logger.Info(logSender, "", "try to create non-existent host key %q", k)
  803. logger.InfoToConsole("try to create non-existent host key %q", k)
  804. err = util.GenerateRSAKeys(k)
  805. if err != nil {
  806. logger.Warn(logSender, "", "error creating host key %q: %v", k, err)
  807. logger.WarnToConsole("error creating host key %q: %v", k, err)
  808. return err
  809. }
  810. case defaultPrivateECDSAKeyName:
  811. logger.Info(logSender, "", "try to create non-existent host key %q", k)
  812. logger.InfoToConsole("try to create non-existent host key %q", k)
  813. err = util.GenerateECDSAKeys(k)
  814. if err != nil {
  815. logger.Warn(logSender, "", "error creating host key %q: %v", k, err)
  816. logger.WarnToConsole("error creating host key %q: %v", k, err)
  817. return err
  818. }
  819. case defaultPrivateEd25519KeyName:
  820. logger.Info(logSender, "", "try to create non-existent host key %q", k)
  821. logger.InfoToConsole("try to create non-existent host key %q", k)
  822. err = util.GenerateEd25519Keys(k)
  823. if err != nil {
  824. logger.Warn(logSender, "", "error creating host key %q: %v", k, err)
  825. logger.WarnToConsole("error creating host key %q: %v", k, err)
  826. return err
  827. }
  828. default:
  829. logger.Warn(logSender, "", "non-existent host key %q will not be created", k)
  830. logger.WarnToConsole("non-existent host key %q will not be created", k)
  831. }
  832. }
  833. }
  834. }
  835. if len(c.HostKeys) == 0 {
  836. if err := c.generateDefaultHostKeys(configDir); err != nil {
  837. return err
  838. }
  839. }
  840. return nil
  841. }
  842. func (c *Configuration) getHostKeyAlgorithms(keyFormat string) []string {
  843. var algos []string
  844. for _, algo := range algorithmsForKeyFormat(keyFormat) {
  845. if slices.Contains(c.HostKeyAlgorithms, algo) {
  846. algos = append(algos, algo)
  847. }
  848. }
  849. return algos
  850. }
  851. // If no host keys are defined we try to use or generate the default ones.
  852. func (c *Configuration) checkAndLoadHostKeys(configDir string, serverConfig *ssh.ServerConfig) error {
  853. if err := c.checkHostKeyAutoGeneration(configDir); err != nil {
  854. return err
  855. }
  856. hostCertificates, err := c.loadHostCertificates(configDir)
  857. if err != nil {
  858. return err
  859. }
  860. serviceStatus.HostKeys = nil
  861. for _, hostKey := range c.HostKeys {
  862. hostKey = strings.TrimSpace(hostKey)
  863. if !util.IsFileInputValid(hostKey) {
  864. logger.Warn(logSender, "", "unable to load invalid host key %q", hostKey)
  865. logger.WarnToConsole("unable to load invalid host key %q", hostKey)
  866. continue
  867. }
  868. if !filepath.IsAbs(hostKey) {
  869. hostKey = filepath.Join(configDir, hostKey)
  870. }
  871. logger.Info(logSender, "", "Loading private host key %q", hostKey)
  872. privateBytes, err := os.ReadFile(hostKey)
  873. if err != nil {
  874. return err
  875. }
  876. private, err := ssh.ParsePrivateKey(privateBytes)
  877. if err != nil {
  878. return err
  879. }
  880. k := HostKey{
  881. Path: hostKey,
  882. Fingerprint: ssh.FingerprintSHA256(private.PublicKey()),
  883. Algorithms: c.getHostKeyAlgorithms(private.PublicKey().Type()),
  884. }
  885. mas, err := ssh.NewSignerWithAlgorithms(private.(ssh.AlgorithmSigner), k.Algorithms)
  886. if err != nil {
  887. return fmt.Errorf("could not create signer for key %q with algorithms %+v: %w", k.Path, k.Algorithms, err)
  888. }
  889. serviceStatus.HostKeys = append(serviceStatus.HostKeys, k)
  890. logger.Info(logSender, "", "Host key %q loaded, type %q, fingerprint %q, algorithms %+v", hostKey,
  891. private.PublicKey().Type(), k.Fingerprint, k.Algorithms)
  892. // Add private key to the server configuration.
  893. serverConfig.AddHostKey(mas)
  894. for _, cert := range hostCertificates {
  895. signer, err := ssh.NewCertSigner(cert.Certificate, mas)
  896. if err == nil {
  897. var algos []string
  898. for _, algo := range algorithmsForKeyFormat(signer.PublicKey().Type()) {
  899. if underlyingAlgo, ok := certKeyAlgoNames[algo]; ok {
  900. if slices.Contains(mas.Algorithms(), underlyingAlgo) {
  901. algos = append(algos, algo)
  902. }
  903. }
  904. }
  905. serviceStatus.HostKeys = append(serviceStatus.HostKeys, HostKey{
  906. Path: cert.Path,
  907. Fingerprint: ssh.FingerprintSHA256(signer.PublicKey()),
  908. Algorithms: algos,
  909. })
  910. serverConfig.AddHostKey(signer)
  911. logger.Info(logSender, "", "Host certificate loaded for host key %q, fingerprint %q, algorithms %+v",
  912. hostKey, ssh.FingerprintSHA256(signer.PublicKey()), algos)
  913. }
  914. }
  915. }
  916. var fp []string
  917. for idx := range serviceStatus.HostKeys {
  918. h := &serviceStatus.HostKeys[idx]
  919. fp = append(fp, h.Fingerprint)
  920. }
  921. vfs.SetSFTPFingerprints(fp)
  922. return nil
  923. }
  924. func (c *Configuration) loadHostCertificates(configDir string) ([]hostCertificate, error) {
  925. var certs []hostCertificate
  926. for _, certPath := range c.HostCertificates {
  927. certPath = strings.TrimSpace(certPath)
  928. if !util.IsFileInputValid(certPath) {
  929. logger.Warn(logSender, "", "unable to load invalid host certificate %q", certPath)
  930. logger.WarnToConsole("unable to load invalid host certificate %q", certPath)
  931. continue
  932. }
  933. if !filepath.IsAbs(certPath) {
  934. certPath = filepath.Join(configDir, certPath)
  935. }
  936. certBytes, err := os.ReadFile(certPath)
  937. if err != nil {
  938. return certs, fmt.Errorf("unable to load host certificate %q: %w", certPath, err)
  939. }
  940. parsed, _, _, _, err := ssh.ParseAuthorizedKey(certBytes)
  941. if err != nil {
  942. return nil, fmt.Errorf("unable to parse host certificate %q: %w", certPath, err)
  943. }
  944. cert, ok := parsed.(*ssh.Certificate)
  945. if !ok {
  946. return nil, fmt.Errorf("the file %q is not an SSH certificate", certPath)
  947. }
  948. if cert.CertType != ssh.HostCert {
  949. return nil, fmt.Errorf("the file %q is not an host certificate", certPath)
  950. }
  951. certs = append(certs, hostCertificate{
  952. Path: certPath,
  953. Certificate: cert,
  954. })
  955. }
  956. return certs, nil
  957. }
  958. func (c *Configuration) initializeCertChecker(configDir string) error {
  959. for _, keyPath := range c.TrustedUserCAKeys {
  960. keyPath = strings.TrimSpace(keyPath)
  961. if !util.IsFileInputValid(keyPath) {
  962. logger.Warn(logSender, "", "unable to load invalid trusted user CA key %q", keyPath)
  963. logger.WarnToConsole("unable to load invalid trusted user CA key %q", keyPath)
  964. continue
  965. }
  966. if !filepath.IsAbs(keyPath) {
  967. keyPath = filepath.Join(configDir, keyPath)
  968. }
  969. keyBytes, err := os.ReadFile(keyPath)
  970. if err != nil {
  971. logger.Warn(logSender, "", "error loading trusted user CA key %q: %v", keyPath, err)
  972. logger.WarnToConsole("error loading trusted user CA key %q: %v", keyPath, err)
  973. return err
  974. }
  975. parsedKey, _, _, _, err := ssh.ParseAuthorizedKey(keyBytes)
  976. if err != nil {
  977. logger.Warn(logSender, "", "error parsing trusted user CA key %q: %v", keyPath, err)
  978. logger.WarnToConsole("error parsing trusted user CA key %q: %v", keyPath, err)
  979. return err
  980. }
  981. c.parsedUserCAKeys = append(c.parsedUserCAKeys, parsedKey)
  982. }
  983. c.certChecker = &ssh.CertChecker{
  984. SupportedCriticalOptions: []string{
  985. sourceAddressCriticalOption,
  986. },
  987. IsUserAuthority: func(k ssh.PublicKey) bool {
  988. for _, key := range c.parsedUserCAKeys {
  989. if bytes.Equal(k.Marshal(), key.Marshal()) {
  990. return true
  991. }
  992. }
  993. return false
  994. },
  995. }
  996. if c.RevokedUserCertsFile != "" {
  997. if !util.IsFileInputValid(c.RevokedUserCertsFile) {
  998. return fmt.Errorf("invalid revoked user certificate: %q", c.RevokedUserCertsFile)
  999. }
  1000. if !filepath.IsAbs(c.RevokedUserCertsFile) {
  1001. c.RevokedUserCertsFile = filepath.Join(configDir, c.RevokedUserCertsFile)
  1002. }
  1003. }
  1004. revokedCertManager.filePath = c.RevokedUserCertsFile
  1005. return revokedCertManager.load()
  1006. }
  1007. func (c *Configuration) getPartialSuccessError(nextAuthMethods []string) error {
  1008. err := &ssh.PartialSuccessError{}
  1009. if c.PasswordAuthentication && slices.Contains(nextAuthMethods, dataprovider.LoginMethodPassword) {
  1010. err.Next.PasswordCallback = func(conn ssh.ConnMetadata, password []byte) (*ssh.Permissions, error) {
  1011. return c.validatePasswordCredentials(conn, password, dataprovider.SSHLoginMethodKeyAndPassword)
  1012. }
  1013. }
  1014. if c.KeyboardInteractiveAuthentication && slices.Contains(nextAuthMethods, dataprovider.SSHLoginMethodKeyboardInteractive) {
  1015. err.Next.KeyboardInteractiveCallback = func(conn ssh.ConnMetadata, client ssh.KeyboardInteractiveChallenge) (*ssh.Permissions, error) {
  1016. return c.validateKeyboardInteractiveCredentials(conn, client, dataprovider.SSHLoginMethodKeyAndKeyboardInt, true)
  1017. }
  1018. }
  1019. return err
  1020. }
  1021. func (c *Configuration) validatePublicKeyCredentials(conn ssh.ConnMetadata, pubKey ssh.PublicKey) (*ssh.Permissions, error) {
  1022. var err error
  1023. var user dataprovider.User
  1024. var keyID string
  1025. var sshPerm *ssh.Permissions
  1026. var certPerm *ssh.Permissions
  1027. connectionID := hex.EncodeToString(conn.SessionID())
  1028. method := dataprovider.SSHLoginMethodPublicKey
  1029. ipAddr := util.GetIPFromRemoteAddress(conn.RemoteAddr().String())
  1030. cert, ok := pubKey.(*ssh.Certificate)
  1031. var certFingerprint string
  1032. if ok {
  1033. certFingerprint = ssh.FingerprintSHA256(cert.Key)
  1034. if cert.CertType != ssh.UserCert {
  1035. err = fmt.Errorf("ssh: cert has type %d", cert.CertType)
  1036. user.Username = conn.User()
  1037. updateLoginMetrics(&user, ipAddr, method, err)
  1038. return nil, err
  1039. }
  1040. if !c.certChecker.IsUserAuthority(cert.SignatureKey) {
  1041. err = errors.New("ssh: certificate signed by unrecognized authority")
  1042. user.Username = conn.User()
  1043. updateLoginMetrics(&user, ipAddr, method, err)
  1044. return nil, err
  1045. }
  1046. if len(cert.ValidPrincipals) == 0 {
  1047. err = fmt.Errorf("ssh: certificate %s has no valid principals, user: \"%s\"", certFingerprint, conn.User())
  1048. user.Username = conn.User()
  1049. updateLoginMetrics(&user, ipAddr, method, err)
  1050. return nil, err
  1051. }
  1052. if revokedCertManager.isRevoked(certFingerprint) {
  1053. err = fmt.Errorf("ssh: certificate %s is revoked", certFingerprint)
  1054. user.Username = conn.User()
  1055. updateLoginMetrics(&user, ipAddr, method, err)
  1056. return nil, err
  1057. }
  1058. if err := c.certChecker.CheckCert(conn.User(), cert); err != nil {
  1059. user.Username = conn.User()
  1060. updateLoginMetrics(&user, ipAddr, method, err)
  1061. return nil, err
  1062. }
  1063. certPerm = &cert.Permissions
  1064. }
  1065. if user, keyID, err = dataprovider.CheckUserAndPubKey(conn.User(), pubKey.Marshal(), ipAddr, common.ProtocolSSH, ok); err == nil {
  1066. if ok {
  1067. keyID = fmt.Sprintf("%s: ID: %s, serial: %v, CA %s %s", certFingerprint,
  1068. cert.KeyId, cert.Serial, cert.Type(), ssh.FingerprintSHA256(cert.SignatureKey))
  1069. }
  1070. if user.IsPartialAuth() {
  1071. logger.Debug(logSender, connectionID, "user %q authenticated with partial success", conn.User())
  1072. return certPerm, c.getPartialSuccessError(user.GetNextAuthMethods())
  1073. }
  1074. sshPerm, err = loginUser(&user, method, keyID, conn)
  1075. if err == nil && certPerm != nil {
  1076. // if we have a SSH user cert we need to merge certificate permissions with our ones
  1077. // we only set Extensions, so CriticalOptions are always the ones from the certificate
  1078. sshPerm.CriticalOptions = certPerm.CriticalOptions
  1079. if certPerm.Extensions != nil {
  1080. for k, v := range certPerm.Extensions {
  1081. sshPerm.Extensions[k] = v
  1082. }
  1083. }
  1084. }
  1085. }
  1086. user.Username = conn.User()
  1087. updateLoginMetrics(&user, ipAddr, method, err)
  1088. return sshPerm, err
  1089. }
  1090. func (c *Configuration) validatePasswordCredentials(conn ssh.ConnMetadata, pass []byte, method string) (*ssh.Permissions, error) {
  1091. var err error
  1092. var user dataprovider.User
  1093. var sshPerm *ssh.Permissions
  1094. ipAddr := util.GetIPFromRemoteAddress(conn.RemoteAddr().String())
  1095. if user, err = dataprovider.CheckUserAndPass(conn.User(), util.BytesToString(pass), ipAddr, common.ProtocolSSH); err == nil {
  1096. sshPerm, err = loginUser(&user, method, "", conn)
  1097. }
  1098. user.Username = conn.User()
  1099. updateLoginMetrics(&user, ipAddr, method, err)
  1100. if err != nil {
  1101. return nil, newAuthenticationError(fmt.Errorf("could not validate password credentials: %w", err), method, conn.User())
  1102. }
  1103. return sshPerm, nil
  1104. }
  1105. func (c *Configuration) validateKeyboardInteractiveCredentials(conn ssh.ConnMetadata, client ssh.KeyboardInteractiveChallenge,
  1106. method string, isPartialAuth bool,
  1107. ) (*ssh.Permissions, error) {
  1108. var err error
  1109. var user dataprovider.User
  1110. var sshPerm *ssh.Permissions
  1111. ipAddr := util.GetIPFromRemoteAddress(conn.RemoteAddr().String())
  1112. if user, err = dataprovider.CheckKeyboardInteractiveAuth(conn.User(), c.KeyboardInteractiveHook, client,
  1113. ipAddr, common.ProtocolSSH, isPartialAuth); err == nil {
  1114. sshPerm, err = loginUser(&user, method, "", conn)
  1115. }
  1116. user.Username = conn.User()
  1117. updateLoginMetrics(&user, ipAddr, method, err)
  1118. if err != nil {
  1119. return nil, newAuthenticationError(fmt.Errorf("could not validate keyboard interactive credentials: %w", err), method, conn.User())
  1120. }
  1121. return sshPerm, nil
  1122. }
  1123. func updateLoginMetrics(user *dataprovider.User, ip, method string, err error) {
  1124. metric.AddLoginAttempt(method)
  1125. if err == nil {
  1126. plugin.Handler.NotifyLogEvent(notifier.LogEventTypeLoginOK, common.ProtocolSSH, user.Username, ip, "", err)
  1127. common.DelayLogin(nil)
  1128. } else {
  1129. logger.ConnectionFailedLog(user.Username, ip, method, common.ProtocolSSH, err.Error())
  1130. if method != dataprovider.SSHLoginMethodPublicKey {
  1131. // some clients try all available public keys for a user, we
  1132. // record failed login key auth only once for session if the
  1133. // authentication fails in checkAuthError
  1134. event := common.HostEventLoginFailed
  1135. logEv := notifier.LogEventTypeLoginFailed
  1136. if errors.Is(err, util.ErrNotFound) {
  1137. event = common.HostEventUserNotFound
  1138. logEv = notifier.LogEventTypeLoginNoUser
  1139. }
  1140. common.AddDefenderEvent(ip, common.ProtocolSSH, event)
  1141. plugin.Handler.NotifyLogEvent(logEv, common.ProtocolSSH, user.Username, ip, "", err)
  1142. if method != dataprovider.SSHLoginMethodPublicKey {
  1143. common.DelayLogin(err)
  1144. }
  1145. }
  1146. }
  1147. metric.AddLoginResult(method, err)
  1148. dataprovider.ExecutePostLoginHook(user, method, ip, common.ProtocolSSH, err)
  1149. }
  1150. type revokedCertificates struct {
  1151. filePath string
  1152. mu sync.RWMutex
  1153. certs map[string]bool
  1154. }
  1155. func (r *revokedCertificates) load() error {
  1156. if r.filePath == "" {
  1157. return nil
  1158. }
  1159. logger.Debug(logSender, "", "loading revoked user certificate file %q", r.filePath)
  1160. info, err := os.Stat(r.filePath)
  1161. if err != nil {
  1162. return fmt.Errorf("unable to load revoked user certificate file %q: %w", r.filePath, err)
  1163. }
  1164. maxSize := int64(1048576 * 5) // 5MB
  1165. if info.Size() > maxSize {
  1166. return fmt.Errorf("unable to load revoked user certificate file %q size too big: %v/%v bytes",
  1167. r.filePath, info.Size(), maxSize)
  1168. }
  1169. content, err := os.ReadFile(r.filePath)
  1170. if err != nil {
  1171. return fmt.Errorf("unable to read revoked user certificate file %q: %w", r.filePath, err)
  1172. }
  1173. var certs []string
  1174. err = json.Unmarshal(content, &certs)
  1175. if err != nil {
  1176. return fmt.Errorf("unable to parse revoked user certificate file %q: %w", r.filePath, err)
  1177. }
  1178. r.mu.Lock()
  1179. defer r.mu.Unlock()
  1180. r.certs = map[string]bool{}
  1181. for _, fp := range certs {
  1182. r.certs[fp] = true
  1183. }
  1184. logger.Debug(logSender, "", "revoked user certificate file %q loaded, entries: %v", r.filePath, len(r.certs))
  1185. return nil
  1186. }
  1187. func (r *revokedCertificates) isRevoked(fp string) bool {
  1188. r.mu.RLock()
  1189. defer r.mu.RUnlock()
  1190. return r.certs[fp]
  1191. }
  1192. // Reload reloads the list of revoked user certificates
  1193. func Reload() error {
  1194. return revokedCertManager.load()
  1195. }
  1196. func algorithmsForKeyFormat(keyFormat string) []string {
  1197. switch keyFormat {
  1198. case ssh.KeyAlgoRSA:
  1199. return []string{ssh.KeyAlgoRSASHA256, ssh.KeyAlgoRSASHA512, ssh.KeyAlgoRSA}
  1200. case ssh.CertAlgoRSAv01:
  1201. return []string{ssh.CertAlgoRSASHA256v01, ssh.CertAlgoRSASHA512v01, ssh.CertAlgoRSAv01}
  1202. default:
  1203. return []string{keyFormat}
  1204. }
  1205. }