dataprovider.go 83 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453
  1. // Package dataprovider provides data access.
  2. // It abstracts different data providers and exposes a common API.
  3. package dataprovider
  4. import (
  5. "bufio"
  6. "bytes"
  7. "context"
  8. "crypto/sha1"
  9. "crypto/sha256"
  10. "crypto/sha512"
  11. "crypto/subtle"
  12. "crypto/x509"
  13. "encoding/base64"
  14. "encoding/json"
  15. "errors"
  16. "fmt"
  17. "hash"
  18. "io"
  19. "net"
  20. "net/http"
  21. "net/url"
  22. "os"
  23. "os/exec"
  24. "path"
  25. "path/filepath"
  26. "regexp"
  27. "runtime"
  28. "strconv"
  29. "strings"
  30. "sync"
  31. "sync/atomic"
  32. "time"
  33. "github.com/GehirnInc/crypt"
  34. "github.com/GehirnInc/crypt/apr1_crypt"
  35. "github.com/GehirnInc/crypt/md5_crypt"
  36. "github.com/GehirnInc/crypt/sha512_crypt"
  37. "github.com/alexedwards/argon2id"
  38. "github.com/go-chi/render"
  39. "github.com/rs/xid"
  40. "golang.org/x/crypto/bcrypt"
  41. "golang.org/x/crypto/pbkdf2"
  42. "golang.org/x/crypto/ssh"
  43. "github.com/drakkan/sftpgo/v2/httpclient"
  44. "github.com/drakkan/sftpgo/v2/kms"
  45. "github.com/drakkan/sftpgo/v2/logger"
  46. "github.com/drakkan/sftpgo/v2/metric"
  47. "github.com/drakkan/sftpgo/v2/sdk"
  48. "github.com/drakkan/sftpgo/v2/sdk/plugin"
  49. "github.com/drakkan/sftpgo/v2/util"
  50. "github.com/drakkan/sftpgo/v2/vfs"
  51. )
  52. const (
  53. // SQLiteDataProviderName defines the name for SQLite database provider
  54. SQLiteDataProviderName = "sqlite"
  55. // PGSQLDataProviderName defines the name for PostgreSQL database provider
  56. PGSQLDataProviderName = "postgresql"
  57. // MySQLDataProviderName defines the name for MySQL database provider
  58. MySQLDataProviderName = "mysql"
  59. // BoltDataProviderName defines the name for bbolt key/value store provider
  60. BoltDataProviderName = "bolt"
  61. // MemoryDataProviderName defines the name for memory provider
  62. MemoryDataProviderName = "memory"
  63. // CockroachDataProviderName defines the for CockroachDB provider
  64. CockroachDataProviderName = "cockroachdb"
  65. // DumpVersion defines the version for the dump.
  66. // For restore/load we support the current version and the previous one
  67. DumpVersion = 8
  68. argonPwdPrefix = "$argon2id$"
  69. bcryptPwdPrefix = "$2a$"
  70. pbkdf2SHA1Prefix = "$pbkdf2-sha1$"
  71. pbkdf2SHA256Prefix = "$pbkdf2-sha256$"
  72. pbkdf2SHA512Prefix = "$pbkdf2-sha512$"
  73. pbkdf2SHA256B64SaltPrefix = "$pbkdf2-b64salt-sha256$"
  74. md5cryptPwdPrefix = "$1$"
  75. md5cryptApr1PwdPrefix = "$apr1$"
  76. sha512cryptPwdPrefix = "$6$"
  77. trackQuotaDisabledError = "please enable track_quota in your configuration to use this method"
  78. operationAdd = "add"
  79. operationUpdate = "update"
  80. operationDelete = "delete"
  81. sqlPrefixValidChars = "abcdefghijklmnopqrstuvwxyz_0123456789"
  82. maxHookResponseSize = 1048576 // 1MB
  83. )
  84. // Supported algorithms for hashing passwords.
  85. // These algorithms can be used when SFTPGo hashes a plain text password
  86. const (
  87. HashingAlgoBcrypt = "bcrypt"
  88. HashingAlgoArgon2ID = "argon2id"
  89. )
  90. // ordering constants
  91. const (
  92. OrderASC = "ASC"
  93. OrderDESC = "DESC"
  94. )
  95. var (
  96. // SupportedProviders defines the supported data providers
  97. SupportedProviders = []string{SQLiteDataProviderName, PGSQLDataProviderName, MySQLDataProviderName,
  98. BoltDataProviderName, MemoryDataProviderName, CockroachDataProviderName}
  99. // ValidPerms defines all the valid permissions for a user
  100. ValidPerms = []string{PermAny, PermListItems, PermDownload, PermUpload, PermOverwrite, PermRename, PermDelete,
  101. PermCreateDirs, PermCreateSymlinks, PermChmod, PermChown, PermChtimes}
  102. // ValidLoginMethods defines all the valid login methods
  103. ValidLoginMethods = []string{SSHLoginMethodPublicKey, LoginMethodPassword, SSHLoginMethodKeyboardInteractive,
  104. SSHLoginMethodKeyAndPassword, SSHLoginMethodKeyAndKeyboardInt, LoginMethodTLSCertificate,
  105. LoginMethodTLSCertificateAndPwd}
  106. // SSHMultiStepsLoginMethods defines the supported Multi-Step Authentications
  107. SSHMultiStepsLoginMethods = []string{SSHLoginMethodKeyAndPassword, SSHLoginMethodKeyAndKeyboardInt}
  108. // ErrNoAuthTryed defines the error for connection closed before authentication
  109. ErrNoAuthTryed = errors.New("no auth tryed")
  110. // ValidProtocols defines all the valid protcols
  111. ValidProtocols = []string{"SSH", "FTP", "DAV", "HTTP"}
  112. // ErrNoInitRequired defines the error returned by InitProvider if no inizialization/update is required
  113. ErrNoInitRequired = errors.New("the data provider is up to date")
  114. // ErrInvalidCredentials defines the error to return if the supplied credentials are invalid
  115. ErrInvalidCredentials = errors.New("invalid credentials")
  116. isAdminCreated = int32(0)
  117. validTLSUsernames = []string{string(sdk.TLSUsernameNone), string(sdk.TLSUsernameCN)}
  118. config Config
  119. provider Provider
  120. sqlPlaceholders []string
  121. internalHashPwdPrefixes = []string{argonPwdPrefix, bcryptPwdPrefix}
  122. hashPwdPrefixes = []string{argonPwdPrefix, bcryptPwdPrefix, pbkdf2SHA1Prefix, pbkdf2SHA256Prefix,
  123. pbkdf2SHA512Prefix, pbkdf2SHA256B64SaltPrefix, md5cryptPwdPrefix, md5cryptApr1PwdPrefix, sha512cryptPwdPrefix}
  124. pbkdfPwdPrefixes = []string{pbkdf2SHA1Prefix, pbkdf2SHA256Prefix, pbkdf2SHA512Prefix, pbkdf2SHA256B64SaltPrefix}
  125. pbkdfPwdB64SaltPrefixes = []string{pbkdf2SHA256B64SaltPrefix}
  126. unixPwdPrefixes = []string{md5cryptPwdPrefix, md5cryptApr1PwdPrefix, sha512cryptPwdPrefix}
  127. logSender = "dataProvider"
  128. availabilityTicker *time.Ticker
  129. availabilityTickerDone chan bool
  130. credentialsDirPath string
  131. sqlTableUsers = "users"
  132. sqlTableFolders = "folders"
  133. sqlTableFoldersMapping = "folders_mapping"
  134. sqlTableAdmins = "admins"
  135. sqlTableSchemaVersion = "schema_version"
  136. argon2Params *argon2id.Params
  137. lastLoginMinDelay = 10 * time.Minute
  138. usernameRegex = regexp.MustCompile("^[a-zA-Z0-9-_.~]+$")
  139. tempPath string
  140. )
  141. type schemaVersion struct {
  142. Version int
  143. }
  144. // BcryptOptions defines the options for bcrypt password hashing
  145. type BcryptOptions struct {
  146. Cost int `json:"cost" mapstructure:"cost"`
  147. }
  148. // Argon2Options defines the options for argon2 password hashing
  149. type Argon2Options struct {
  150. Memory uint32 `json:"memory" mapstructure:"memory"`
  151. Iterations uint32 `json:"iterations" mapstructure:"iterations"`
  152. Parallelism uint8 `json:"parallelism" mapstructure:"parallelism"`
  153. }
  154. // PasswordHashing defines the configuration for password hashing
  155. type PasswordHashing struct {
  156. BcryptOptions BcryptOptions `json:"bcrypt_options" mapstructure:"bcrypt_options"`
  157. Argon2Options Argon2Options `json:"argon2_options" mapstructure:"argon2_options"`
  158. // Algorithm to use for hashing passwords. Available algorithms: argon2id, bcrypt. Default: bcrypt
  159. Algo string `json:"algo" mapstructure:"algo"`
  160. }
  161. // UserActions defines the action to execute on user create, update, delete.
  162. type UserActions struct {
  163. // Valid values are add, update, delete. Empty slice to disable
  164. ExecuteOn []string `json:"execute_on" mapstructure:"execute_on"`
  165. // Absolute path to an external program or an HTTP URL
  166. Hook string `json:"hook" mapstructure:"hook"`
  167. }
  168. // ProviderStatus defines the provider status
  169. type ProviderStatus struct {
  170. Driver string `json:"driver"`
  171. IsActive bool `json:"is_active"`
  172. Error string `json:"error"`
  173. }
  174. // Config provider configuration
  175. type Config struct {
  176. // Driver name, must be one of the SupportedProviders
  177. Driver string `json:"driver" mapstructure:"driver"`
  178. // Database name. For driver sqlite this can be the database name relative to the config dir
  179. // or the absolute path to the SQLite database.
  180. Name string `json:"name" mapstructure:"name"`
  181. // Database host
  182. Host string `json:"host" mapstructure:"host"`
  183. // Database port
  184. Port int `json:"port" mapstructure:"port"`
  185. // Database username
  186. Username string `json:"username" mapstructure:"username"`
  187. // Database password
  188. Password string `json:"password" mapstructure:"password"`
  189. // Used for drivers mysql and postgresql.
  190. // 0 disable SSL/TLS connections.
  191. // 1 require ssl.
  192. // 2 set ssl mode to verify-ca for driver postgresql and skip-verify for driver mysql.
  193. // 3 set ssl mode to verify-full for driver postgresql and preferred for driver mysql.
  194. SSLMode int `json:"sslmode" mapstructure:"sslmode"`
  195. // Custom database connection string.
  196. // If not empty this connection string will be used instead of build one using the previous parameters
  197. ConnectionString string `json:"connection_string" mapstructure:"connection_string"`
  198. // prefix for SQL tables
  199. SQLTablesPrefix string `json:"sql_tables_prefix" mapstructure:"sql_tables_prefix"`
  200. // Set the preferred way to track users quota between the following choices:
  201. // 0, disable quota tracking. REST API to scan user dir and update quota will do nothing
  202. // 1, quota is updated each time a user upload or delete a file even if the user has no quota restrictions
  203. // 2, quota is updated each time a user upload or delete a file but only for users with quota restrictions
  204. // and for virtual folders.
  205. // With this configuration the "quota scan" REST API can still be used to periodically update space usage
  206. // for users without quota restrictions
  207. TrackQuota int `json:"track_quota" mapstructure:"track_quota"`
  208. // Sets the maximum number of open connections for mysql and postgresql driver.
  209. // Default 0 (unlimited)
  210. PoolSize int `json:"pool_size" mapstructure:"pool_size"`
  211. // Users default base directory.
  212. // If no home dir is defined while adding a new user, and this value is
  213. // a valid absolute path, then the user home dir will be automatically
  214. // defined as the path obtained joining the base dir and the username
  215. UsersBaseDir string `json:"users_base_dir" mapstructure:"users_base_dir"`
  216. // Actions to execute on user add, update, delete.
  217. // Update action will not be fired for internal updates such as the last login or the user quota fields.
  218. Actions UserActions `json:"actions" mapstructure:"actions"`
  219. // Absolute path to an external program or an HTTP URL to invoke for users authentication.
  220. // Leave empty to use builtin authentication.
  221. // If the authentication succeed the user will be automatically added/updated inside the defined data provider.
  222. // Actions defined for user added/updated will not be executed in this case.
  223. // This method is slower than built-in authentication methods, but it's very flexible as anyone can
  224. // easily write his own authentication hooks.
  225. ExternalAuthHook string `json:"external_auth_hook" mapstructure:"external_auth_hook"`
  226. // ExternalAuthScope defines the scope for the external authentication hook.
  227. // - 0 means all supported authentication scopes, the external hook will be executed for password,
  228. // public key, keyboard interactive authentication and TLS certificates
  229. // - 1 means passwords only
  230. // - 2 means public keys only
  231. // - 4 means keyboard interactive only
  232. // - 8 means TLS certificates only
  233. // you can combine the scopes, for example 3 means password and public key, 5 password and keyboard
  234. // interactive and so on
  235. ExternalAuthScope int `json:"external_auth_scope" mapstructure:"external_auth_scope"`
  236. // CredentialsPath defines the directory for storing user provided credential files such as
  237. // Google Cloud Storage credentials. It can be a path relative to the config dir or an
  238. // absolute path
  239. CredentialsPath string `json:"credentials_path" mapstructure:"credentials_path"`
  240. // Absolute path to an external program or an HTTP URL to invoke just before the user login.
  241. // This program/URL allows to modify or create the user trying to login.
  242. // It is useful if you have users with dynamic fields to update just before the login.
  243. // Please note that if you want to create a new user, the pre-login hook response must
  244. // include all the mandatory user fields.
  245. //
  246. // The pre-login hook must finish within 30 seconds.
  247. //
  248. // If an error happens while executing the "PreLoginHook" then login will be denied.
  249. // PreLoginHook and ExternalAuthHook are mutally exclusive.
  250. // Leave empty to disable.
  251. PreLoginHook string `json:"pre_login_hook" mapstructure:"pre_login_hook"`
  252. // Absolute path to an external program or an HTTP URL to invoke after the user login.
  253. // Based on the configured scope you can choose if notify failed or successful logins
  254. // or both
  255. PostLoginHook string `json:"post_login_hook" mapstructure:"post_login_hook"`
  256. // PostLoginScope defines the scope for the post-login hook.
  257. // - 0 means notify both failed and successful logins
  258. // - 1 means notify failed logins
  259. // - 2 means notify successful logins
  260. PostLoginScope int `json:"post_login_scope" mapstructure:"post_login_scope"`
  261. // Absolute path to an external program or an HTTP URL to invoke just before password
  262. // authentication. This hook allows you to externally check the provided password,
  263. // its main use case is to allow to easily support things like password+OTP for protocols
  264. // without keyboard interactive support such as FTP and WebDAV. You can ask your users
  265. // to login using a string consisting of a fixed password and a One Time Token, you
  266. // can verify the token inside the hook and ask to SFTPGo to verify the fixed part.
  267. CheckPasswordHook string `json:"check_password_hook" mapstructure:"check_password_hook"`
  268. // CheckPasswordScope defines the scope for the check password hook.
  269. // - 0 means all protocols
  270. // - 1 means SSH
  271. // - 2 means FTP
  272. // - 4 means WebDAV
  273. // you can combine the scopes, for example 6 means FTP and WebDAV
  274. CheckPasswordScope int `json:"check_password_scope" mapstructure:"check_password_scope"`
  275. // Defines how the database will be initialized/updated:
  276. // - 0 means automatically
  277. // - 1 means manually using the initprovider sub-command
  278. UpdateMode int `json:"update_mode" mapstructure:"update_mode"`
  279. // PasswordHashing defines the configuration for password hashing
  280. PasswordHashing PasswordHashing `json:"password_hashing" mapstructure:"password_hashing"`
  281. // PreferDatabaseCredentials indicates whether credential files (currently used for Google
  282. // Cloud Storage) should be stored in the database instead of in the directory specified by
  283. // CredentialsPath.
  284. PreferDatabaseCredentials bool `json:"prefer_database_credentials" mapstructure:"prefer_database_credentials"`
  285. // SkipNaturalKeysValidation allows to use any UTF-8 character for natural keys as username, admin name,
  286. // folder name. These keys are used in URIs for REST API and Web admin. By default only unreserved URI
  287. // characters are allowed: ALPHA / DIGIT / "-" / "." / "_" / "~".
  288. SkipNaturalKeysValidation bool `json:"skip_natural_keys_validation" mapstructure:"skip_natural_keys_validation"`
  289. // Verifying argon2 passwords has a high memory and computational cost,
  290. // by enabling, in memory, password caching you reduce this cost.
  291. PasswordCaching bool `json:"password_caching" mapstructure:"password_caching"`
  292. // DelayedQuotaUpdate defines the number of seconds to accumulate quota updates.
  293. // If there are a lot of close uploads, accumulating quota updates can save you many
  294. // queries to the data provider.
  295. // If you want to track quotas, a scheduled quota update is recommended in any case, the stored
  296. // quota size may be incorrect for several reasons, such as an unexpected shutdown, temporary provider
  297. // failures, file copied outside of SFTPGo, and so on.
  298. // 0 means immediate quota update.
  299. DelayedQuotaUpdate int `json:"delayed_quota_update" mapstructure:"delayed_quota_update"`
  300. // If enabled, a default admin user with username "admin" and password "password" will be created
  301. // on first start.
  302. // You can also create the first admin user by using the web interface or by loading initial data.
  303. CreateDefaultAdmin bool `json:"create_default_admin" mapstructure:"create_default_admin"`
  304. }
  305. // BackupData defines the structure for the backup/restore files
  306. type BackupData struct {
  307. Users []User `json:"users"`
  308. Folders []vfs.BaseVirtualFolder `json:"folders"`
  309. Admins []Admin `json:"admins"`
  310. Version int `json:"version"`
  311. }
  312. // HasFolder returns true if the folder with the given name is included
  313. func (d *BackupData) HasFolder(name string) bool {
  314. for _, folder := range d.Folders {
  315. if folder.Name == name {
  316. return true
  317. }
  318. }
  319. return false
  320. }
  321. type keyboardAuthHookRequest struct {
  322. RequestID string `json:"request_id"`
  323. Username string `json:"username,omitempty"`
  324. IP string `json:"ip,omitempty"`
  325. Password string `json:"password,omitempty"`
  326. Answers []string `json:"answers,omitempty"`
  327. Questions []string `json:"questions,omitempty"`
  328. }
  329. type keyboardAuthHookResponse struct {
  330. Instruction string `json:"instruction"`
  331. Questions []string `json:"questions"`
  332. Echos []bool `json:"echos"`
  333. AuthResult int `json:"auth_result"`
  334. CheckPwd int `json:"check_password"`
  335. }
  336. type checkPasswordRequest struct {
  337. Username string `json:"username"`
  338. IP string `json:"ip"`
  339. Password string `json:"password"`
  340. Protocol string `json:"protocol"`
  341. }
  342. type checkPasswordResponse struct {
  343. // 0 KO, 1 OK, 2 partial success, -1 not executed
  344. Status int `json:"status"`
  345. // for status = 2 this is the password to check against the one stored
  346. // inside the SFTPGo data provider
  347. ToVerify string `json:"to_verify"`
  348. }
  349. // GetQuotaTracking returns the configured mode for user's quota tracking
  350. func GetQuotaTracking() int {
  351. return config.TrackQuota
  352. }
  353. // Provider defines the interface that data providers must implement.
  354. type Provider interface {
  355. validateUserAndPass(username, password, ip, protocol string) (User, error)
  356. validateUserAndPubKey(username string, pubKey []byte) (User, string, error)
  357. validateUserAndTLSCert(username, protocol string, tlsCert *x509.Certificate) (User, error)
  358. updateQuota(username string, filesAdd int, sizeAdd int64, reset bool) error
  359. getUsedQuota(username string) (int, int64, error)
  360. userExists(username string) (User, error)
  361. addUser(user *User) error
  362. updateUser(user *User) error
  363. deleteUser(user *User) error
  364. getUsers(limit int, offset int, order string) ([]User, error)
  365. dumpUsers() ([]User, error)
  366. updateLastLogin(username string) error
  367. getFolders(limit, offset int, order string) ([]vfs.BaseVirtualFolder, error)
  368. getFolderByName(name string) (vfs.BaseVirtualFolder, error)
  369. addFolder(folder *vfs.BaseVirtualFolder) error
  370. updateFolder(folder *vfs.BaseVirtualFolder) error
  371. deleteFolder(folder *vfs.BaseVirtualFolder) error
  372. updateFolderQuota(name string, filesAdd int, sizeAdd int64, reset bool) error
  373. getUsedFolderQuota(name string) (int, int64, error)
  374. dumpFolders() ([]vfs.BaseVirtualFolder, error)
  375. adminExists(username string) (Admin, error)
  376. addAdmin(admin *Admin) error
  377. updateAdmin(admin *Admin) error
  378. deleteAdmin(admin *Admin) error
  379. getAdmins(limit int, offset int, order string) ([]Admin, error)
  380. dumpAdmins() ([]Admin, error)
  381. validateAdminAndPass(username, password, ip string) (Admin, error)
  382. checkAvailability() error
  383. close() error
  384. reloadConfig() error
  385. initializeDatabase() error
  386. migrateDatabase() error
  387. revertDatabase(targetVersion int) error
  388. }
  389. // SetTempPath sets the path for temporary files
  390. func SetTempPath(fsPath string) {
  391. tempPath = fsPath
  392. }
  393. // Initialize the data provider.
  394. // An error is returned if the configured driver is invalid or if the data provider cannot be initialized
  395. func Initialize(cnf Config, basePath string, checkAdmins bool) error {
  396. var err error
  397. config = cnf
  398. if filepath.IsAbs(config.CredentialsPath) {
  399. credentialsDirPath = config.CredentialsPath
  400. } else {
  401. credentialsDirPath = filepath.Join(basePath, config.CredentialsPath)
  402. }
  403. vfs.SetCredentialsDirPath(credentialsDirPath)
  404. if err = initializeHashingAlgo(&cnf); err != nil {
  405. return err
  406. }
  407. if err = validateHooks(); err != nil {
  408. return err
  409. }
  410. err = createProvider(basePath)
  411. if err != nil {
  412. return err
  413. }
  414. if cnf.UpdateMode == 0 {
  415. err = provider.initializeDatabase()
  416. if err != nil && err != ErrNoInitRequired {
  417. logger.WarnToConsole("Unable to initialize data provider: %v", err)
  418. providerLog(logger.LevelWarn, "Unable to initialize data provider: %v", err)
  419. return err
  420. }
  421. if err == nil {
  422. logger.DebugToConsole("Data provider successfully initialized")
  423. }
  424. err = provider.migrateDatabase()
  425. if err != nil && err != ErrNoInitRequired {
  426. providerLog(logger.LevelWarn, "database migration error: %v", err)
  427. return err
  428. }
  429. if checkAdmins && cnf.CreateDefaultAdmin {
  430. err = checkDefaultAdmin()
  431. if err != nil {
  432. providerLog(logger.LevelWarn, "check default admin error: %v", err)
  433. return err
  434. }
  435. }
  436. } else {
  437. providerLog(logger.LevelInfo, "database initialization/migration skipped, manual mode is configured")
  438. }
  439. admins, err := provider.getAdmins(1, 0, OrderASC)
  440. if err != nil {
  441. return err
  442. }
  443. atomic.StoreInt32(&isAdminCreated, int32(len(admins)))
  444. startAvailabilityTimer()
  445. delayedQuotaUpdater.start()
  446. return nil
  447. }
  448. func validateHooks() error {
  449. var hooks []string
  450. if config.PreLoginHook != "" && !strings.HasPrefix(config.PreLoginHook, "http") {
  451. hooks = append(hooks, config.PreLoginHook)
  452. }
  453. if config.ExternalAuthHook != "" && !strings.HasPrefix(config.ExternalAuthHook, "http") {
  454. hooks = append(hooks, config.ExternalAuthHook)
  455. }
  456. if config.PostLoginHook != "" && !strings.HasPrefix(config.PostLoginHook, "http") {
  457. hooks = append(hooks, config.PostLoginHook)
  458. }
  459. if config.CheckPasswordHook != "" && !strings.HasPrefix(config.CheckPasswordHook, "http") {
  460. hooks = append(hooks, config.CheckPasswordHook)
  461. }
  462. for _, hook := range hooks {
  463. if !filepath.IsAbs(hook) {
  464. return fmt.Errorf("invalid hook: %#v must be an absolute path", hook)
  465. }
  466. _, err := os.Stat(hook)
  467. if err != nil {
  468. providerLog(logger.LevelWarn, "invalid hook: %v", err)
  469. return err
  470. }
  471. }
  472. return nil
  473. }
  474. func initializeHashingAlgo(cnf *Config) error {
  475. argon2Params = &argon2id.Params{
  476. Memory: cnf.PasswordHashing.Argon2Options.Memory,
  477. Iterations: cnf.PasswordHashing.Argon2Options.Iterations,
  478. Parallelism: cnf.PasswordHashing.Argon2Options.Parallelism,
  479. SaltLength: 16,
  480. KeyLength: 32,
  481. }
  482. if config.PasswordHashing.Algo == HashingAlgoBcrypt {
  483. if config.PasswordHashing.BcryptOptions.Cost > bcrypt.MaxCost {
  484. err := fmt.Errorf("invalid bcrypt cost %v, max allowed %v", config.PasswordHashing.BcryptOptions.Cost, bcrypt.MaxCost)
  485. logger.WarnToConsole("Unable to initialize data provider: %v", err)
  486. providerLog(logger.LevelWarn, "Unable to initialize data provider: %v", err)
  487. return err
  488. }
  489. }
  490. return nil
  491. }
  492. func validateSQLTablesPrefix() error {
  493. if config.SQLTablesPrefix != "" {
  494. for _, char := range config.SQLTablesPrefix {
  495. if !strings.Contains(sqlPrefixValidChars, strings.ToLower(string(char))) {
  496. return errors.New("invalid sql_tables_prefix only chars in range 'a..z', 'A..Z', '0-9' and '_' are allowed")
  497. }
  498. }
  499. sqlTableUsers = config.SQLTablesPrefix + sqlTableUsers
  500. sqlTableFolders = config.SQLTablesPrefix + sqlTableFolders
  501. sqlTableFoldersMapping = config.SQLTablesPrefix + sqlTableFoldersMapping
  502. sqlTableAdmins = config.SQLTablesPrefix + sqlTableAdmins
  503. sqlTableSchemaVersion = config.SQLTablesPrefix + sqlTableSchemaVersion
  504. providerLog(logger.LevelDebug, "sql table for users %#v, folders %#v folders mapping %#v admins %#v schema version %#v",
  505. sqlTableUsers, sqlTableFolders, sqlTableFoldersMapping, sqlTableAdmins, sqlTableSchemaVersion)
  506. }
  507. return nil
  508. }
  509. func checkDefaultAdmin() error {
  510. admins, err := provider.getAdmins(1, 0, OrderASC)
  511. if err != nil {
  512. return err
  513. }
  514. if len(admins) > 0 {
  515. return nil
  516. }
  517. logger.Debug(logSender, "", "no admins found, try to create the default one")
  518. // we need to create the default admin
  519. admin := &Admin{}
  520. admin.setDefaults()
  521. return provider.addAdmin(admin)
  522. }
  523. // InitializeDatabase creates the initial database structure
  524. func InitializeDatabase(cnf Config, basePath string) error {
  525. config = cnf
  526. if filepath.IsAbs(config.CredentialsPath) {
  527. credentialsDirPath = config.CredentialsPath
  528. } else {
  529. credentialsDirPath = filepath.Join(basePath, config.CredentialsPath)
  530. }
  531. err := createProvider(basePath)
  532. if err != nil {
  533. return err
  534. }
  535. err = provider.initializeDatabase()
  536. if err != nil && err != ErrNoInitRequired {
  537. return err
  538. }
  539. return provider.migrateDatabase()
  540. }
  541. // RevertDatabase restores schema and/or data to a previous version
  542. func RevertDatabase(cnf Config, basePath string, targetVersion int) error {
  543. config = cnf
  544. if filepath.IsAbs(config.CredentialsPath) {
  545. credentialsDirPath = config.CredentialsPath
  546. } else {
  547. credentialsDirPath = filepath.Join(basePath, config.CredentialsPath)
  548. }
  549. err := createProvider(basePath)
  550. if err != nil {
  551. return err
  552. }
  553. err = provider.initializeDatabase()
  554. if err != nil && err != ErrNoInitRequired {
  555. return err
  556. }
  557. return provider.revertDatabase(targetVersion)
  558. }
  559. // CheckAdminAndPass validates the given admin and password connecting from ip
  560. func CheckAdminAndPass(username, password, ip string) (Admin, error) {
  561. return provider.validateAdminAndPass(username, password, ip)
  562. }
  563. // CheckCachedUserCredentials checks the credentials for a cached user
  564. func CheckCachedUserCredentials(user *CachedUser, password, loginMethod, protocol string, tlsCert *x509.Certificate) error {
  565. if loginMethod != LoginMethodPassword {
  566. _, err := checkUserAndTLSCertificate(&user.User, protocol, tlsCert)
  567. if err != nil {
  568. return err
  569. }
  570. if loginMethod == LoginMethodTLSCertificate {
  571. if !user.User.IsLoginMethodAllowed(LoginMethodTLSCertificate, nil) {
  572. return fmt.Errorf("certificate login method is not allowed for user %#v", user.User.Username)
  573. }
  574. return nil
  575. }
  576. }
  577. if err := checkLoginConditions(&user.User); err != nil {
  578. return err
  579. }
  580. if password == "" {
  581. return ErrInvalidCredentials
  582. }
  583. if user.Password != "" {
  584. if password == user.Password {
  585. return nil
  586. }
  587. } else {
  588. if ok, _ := isPasswordOK(&user.User, password); ok {
  589. return nil
  590. }
  591. }
  592. return ErrInvalidCredentials
  593. }
  594. // CheckCompositeCredentials checks multiple credentials.
  595. // WebDAV users can send both a password and a TLS certificate within the same request
  596. func CheckCompositeCredentials(username, password, ip, loginMethod, protocol string, tlsCert *x509.Certificate) (User, string, error) {
  597. if loginMethod == LoginMethodPassword {
  598. user, err := CheckUserAndPass(username, password, ip, protocol)
  599. return user, loginMethod, err
  600. }
  601. user, err := CheckUserBeforeTLSAuth(username, ip, protocol, tlsCert)
  602. if err != nil {
  603. return user, loginMethod, err
  604. }
  605. if !user.IsTLSUsernameVerificationEnabled() {
  606. // for backward compatibility with 2.0.x we only check the password and change the login method here
  607. // in future updates we have to return an error
  608. user, err := CheckUserAndPass(username, password, ip, protocol)
  609. return user, LoginMethodPassword, err
  610. }
  611. user, err = checkUserAndTLSCertificate(&user, protocol, tlsCert)
  612. if err != nil {
  613. return user, loginMethod, err
  614. }
  615. if loginMethod == LoginMethodTLSCertificate && !user.IsLoginMethodAllowed(LoginMethodTLSCertificate, nil) {
  616. return user, loginMethod, fmt.Errorf("certificate login method is not allowed for user %#v", user.Username)
  617. }
  618. if loginMethod == LoginMethodTLSCertificateAndPwd {
  619. if config.ExternalAuthHook != "" && (config.ExternalAuthScope == 0 || config.ExternalAuthScope&1 != 0) {
  620. user, err = doExternalAuth(username, password, nil, "", ip, protocol, nil)
  621. if err != nil {
  622. return user, loginMethod, err
  623. }
  624. }
  625. if config.PreLoginHook != "" {
  626. user, err = executePreLoginHook(username, LoginMethodPassword, ip, protocol)
  627. if err != nil {
  628. return user, loginMethod, err
  629. }
  630. }
  631. user, err = checkUserAndPass(&user, password, ip, protocol)
  632. }
  633. return user, loginMethod, err
  634. }
  635. // CheckUserBeforeTLSAuth checks if a user exits before trying mutual TLS
  636. func CheckUserBeforeTLSAuth(username, ip, protocol string, tlsCert *x509.Certificate) (User, error) {
  637. if config.ExternalAuthHook != "" && (config.ExternalAuthScope == 0 || config.ExternalAuthScope&8 != 0) {
  638. return doExternalAuth(username, "", nil, "", ip, protocol, tlsCert)
  639. }
  640. if config.PreLoginHook != "" {
  641. return executePreLoginHook(username, LoginMethodTLSCertificate, ip, protocol)
  642. }
  643. return UserExists(username)
  644. }
  645. // CheckUserAndTLSCert returns the SFTPGo user with the given username and check if the
  646. // given TLS certificate allow authentication without password
  647. func CheckUserAndTLSCert(username, ip, protocol string, tlsCert *x509.Certificate) (User, error) {
  648. if config.ExternalAuthHook != "" && (config.ExternalAuthScope == 0 || config.ExternalAuthScope&8 != 0) {
  649. user, err := doExternalAuth(username, "", nil, "", ip, protocol, tlsCert)
  650. if err != nil {
  651. return user, err
  652. }
  653. return checkUserAndTLSCertificate(&user, protocol, tlsCert)
  654. }
  655. if config.PreLoginHook != "" {
  656. user, err := executePreLoginHook(username, LoginMethodTLSCertificate, ip, protocol)
  657. if err != nil {
  658. return user, err
  659. }
  660. return checkUserAndTLSCertificate(&user, protocol, tlsCert)
  661. }
  662. return provider.validateUserAndTLSCert(username, protocol, tlsCert)
  663. }
  664. // CheckUserAndPass retrieves the SFTPGo user with the given username and password if a match is found or an error
  665. func CheckUserAndPass(username, password, ip, protocol string) (User, error) {
  666. if config.ExternalAuthHook != "" && (config.ExternalAuthScope == 0 || config.ExternalAuthScope&1 != 0) {
  667. user, err := doExternalAuth(username, password, nil, "", ip, protocol, nil)
  668. if err != nil {
  669. return user, err
  670. }
  671. return checkUserAndPass(&user, password, ip, protocol)
  672. }
  673. if config.PreLoginHook != "" {
  674. user, err := executePreLoginHook(username, LoginMethodPassword, ip, protocol)
  675. if err != nil {
  676. return user, err
  677. }
  678. return checkUserAndPass(&user, password, ip, protocol)
  679. }
  680. return provider.validateUserAndPass(username, password, ip, protocol)
  681. }
  682. // CheckUserAndPubKey retrieves the SFTP user with the given username and public key if a match is found or an error
  683. func CheckUserAndPubKey(username string, pubKey []byte, ip, protocol string) (User, string, error) {
  684. if config.ExternalAuthHook != "" && (config.ExternalAuthScope == 0 || config.ExternalAuthScope&2 != 0) {
  685. user, err := doExternalAuth(username, "", pubKey, "", ip, protocol, nil)
  686. if err != nil {
  687. return user, "", err
  688. }
  689. return checkUserAndPubKey(&user, pubKey)
  690. }
  691. if config.PreLoginHook != "" {
  692. user, err := executePreLoginHook(username, SSHLoginMethodPublicKey, ip, protocol)
  693. if err != nil {
  694. return user, "", err
  695. }
  696. return checkUserAndPubKey(&user, pubKey)
  697. }
  698. return provider.validateUserAndPubKey(username, pubKey)
  699. }
  700. // CheckKeyboardInteractiveAuth checks the keyboard interactive authentication and returns
  701. // the authenticated user or an error
  702. func CheckKeyboardInteractiveAuth(username, authHook string, client ssh.KeyboardInteractiveChallenge, ip, protocol string) (User, error) {
  703. var user User
  704. var err error
  705. if config.ExternalAuthHook != "" && (config.ExternalAuthScope == 0 || config.ExternalAuthScope&4 != 0) {
  706. user, err = doExternalAuth(username, "", nil, "1", ip, protocol, nil)
  707. } else if config.PreLoginHook != "" {
  708. user, err = executePreLoginHook(username, SSHLoginMethodKeyboardInteractive, ip, protocol)
  709. } else {
  710. user, err = provider.userExists(username)
  711. }
  712. if err != nil {
  713. return user, err
  714. }
  715. return doKeyboardInteractiveAuth(&user, authHook, client, ip, protocol)
  716. }
  717. // UpdateLastLogin updates the last login fields for the given SFTP user
  718. func UpdateLastLogin(user *User) error {
  719. lastLogin := util.GetTimeFromMsecSinceEpoch(user.LastLogin)
  720. diff := -time.Until(lastLogin)
  721. if diff < 0 || diff > lastLoginMinDelay {
  722. err := provider.updateLastLogin(user.Username)
  723. if err == nil {
  724. webDAVUsersCache.updateLastLogin(user.Username)
  725. }
  726. return err
  727. }
  728. return nil
  729. }
  730. // UpdateUserQuota updates the quota for the given SFTP user adding filesAdd and sizeAdd.
  731. // If reset is true filesAdd and sizeAdd indicates the total files and the total size instead of the difference.
  732. func UpdateUserQuota(user *User, filesAdd int, sizeAdd int64, reset bool) error {
  733. if config.TrackQuota == 0 {
  734. return util.NewMethodDisabledError(trackQuotaDisabledError)
  735. } else if config.TrackQuota == 2 && !reset && !user.HasQuotaRestrictions() {
  736. return nil
  737. }
  738. if filesAdd == 0 && sizeAdd == 0 && !reset {
  739. return nil
  740. }
  741. if config.DelayedQuotaUpdate == 0 || reset {
  742. if reset {
  743. delayedQuotaUpdater.resetUserQuota(user.Username)
  744. }
  745. return provider.updateQuota(user.Username, filesAdd, sizeAdd, reset)
  746. }
  747. delayedQuotaUpdater.updateUserQuota(user.Username, filesAdd, sizeAdd)
  748. return nil
  749. }
  750. // UpdateVirtualFolderQuota updates the quota for the given virtual folder adding filesAdd and sizeAdd.
  751. // If reset is true filesAdd and sizeAdd indicates the total files and the total size instead of the difference.
  752. func UpdateVirtualFolderQuota(vfolder *vfs.BaseVirtualFolder, filesAdd int, sizeAdd int64, reset bool) error {
  753. if config.TrackQuota == 0 {
  754. return util.NewMethodDisabledError(trackQuotaDisabledError)
  755. }
  756. if filesAdd == 0 && sizeAdd == 0 && !reset {
  757. return nil
  758. }
  759. if config.DelayedQuotaUpdate == 0 || reset {
  760. if reset {
  761. delayedQuotaUpdater.resetFolderQuota(vfolder.Name)
  762. }
  763. return provider.updateFolderQuota(vfolder.Name, filesAdd, sizeAdd, reset)
  764. }
  765. delayedQuotaUpdater.updateFolderQuota(vfolder.Name, filesAdd, sizeAdd)
  766. return nil
  767. }
  768. // GetUsedQuota returns the used quota for the given SFTP user.
  769. func GetUsedQuota(username string) (int, int64, error) {
  770. if config.TrackQuota == 0 {
  771. return 0, 0, util.NewMethodDisabledError(trackQuotaDisabledError)
  772. }
  773. files, size, err := provider.getUsedQuota(username)
  774. if err != nil {
  775. return files, size, err
  776. }
  777. delayedFiles, delayedSize := delayedQuotaUpdater.getUserPendingQuota(username)
  778. return files + delayedFiles, size + delayedSize, err
  779. }
  780. // GetUsedVirtualFolderQuota returns the used quota for the given virtual folder.
  781. func GetUsedVirtualFolderQuota(name string) (int, int64, error) {
  782. if config.TrackQuota == 0 {
  783. return 0, 0, util.NewMethodDisabledError(trackQuotaDisabledError)
  784. }
  785. files, size, err := provider.getUsedFolderQuota(name)
  786. if err != nil {
  787. return files, size, err
  788. }
  789. delayedFiles, delayedSize := delayedQuotaUpdater.getFolderPendingQuota(name)
  790. return files + delayedFiles, size + delayedSize, err
  791. }
  792. // HasAdmin returns true if the first admin has been created
  793. // and so SFTPGo is ready to be used
  794. func HasAdmin() bool {
  795. return atomic.LoadInt32(&isAdminCreated) > 0
  796. }
  797. // AddAdmin adds a new SFTPGo admin
  798. func AddAdmin(admin *Admin) error {
  799. err := provider.addAdmin(admin)
  800. if err == nil {
  801. atomic.StoreInt32(&isAdminCreated, 1)
  802. }
  803. return err
  804. }
  805. // UpdateAdmin updates an existing SFTPGo admin
  806. func UpdateAdmin(admin *Admin) error {
  807. return provider.updateAdmin(admin)
  808. }
  809. // DeleteAdmin deletes an existing SFTPGo admin
  810. func DeleteAdmin(username string) error {
  811. admin, err := provider.adminExists(username)
  812. if err != nil {
  813. return err
  814. }
  815. return provider.deleteAdmin(&admin)
  816. }
  817. // AdminExists returns the given admins if it exists
  818. func AdminExists(username string) (Admin, error) {
  819. return provider.adminExists(username)
  820. }
  821. // UserExists checks if the given SFTPGo username exists, returns an error if no match is found
  822. func UserExists(username string) (User, error) {
  823. return provider.userExists(username)
  824. }
  825. // AddUser adds a new SFTPGo user.
  826. func AddUser(user *User) error {
  827. err := provider.addUser(user)
  828. if err == nil {
  829. executeAction(operationAdd, user)
  830. }
  831. return err
  832. }
  833. // UpdateUser updates an existing SFTPGo user.
  834. func UpdateUser(user *User) error {
  835. err := provider.updateUser(user)
  836. if err == nil {
  837. webDAVUsersCache.swap(user)
  838. cachedPasswords.Remove(user.Username)
  839. executeAction(operationUpdate, user)
  840. }
  841. return err
  842. }
  843. // DeleteUser deletes an existing SFTPGo user.
  844. func DeleteUser(username string) error {
  845. user, err := provider.userExists(username)
  846. if err != nil {
  847. return err
  848. }
  849. err = provider.deleteUser(&user)
  850. if err == nil {
  851. RemoveCachedWebDAVUser(user.Username)
  852. delayedQuotaUpdater.resetUserQuota(username)
  853. cachedPasswords.Remove(username)
  854. executeAction(operationDelete, &user)
  855. }
  856. return err
  857. }
  858. // ReloadConfig reloads provider configuration.
  859. // Currently only implemented for memory provider, allows to reload the users
  860. // from the configured file, if defined
  861. func ReloadConfig() error {
  862. return provider.reloadConfig()
  863. }
  864. // GetAdmins returns an array of admins respecting limit and offset
  865. func GetAdmins(limit, offset int, order string) ([]Admin, error) {
  866. return provider.getAdmins(limit, offset, order)
  867. }
  868. // GetUsers returns an array of users respecting limit and offset and filtered by username exact match if not empty
  869. func GetUsers(limit, offset int, order string) ([]User, error) {
  870. return provider.getUsers(limit, offset, order)
  871. }
  872. // AddFolder adds a new virtual folder.
  873. func AddFolder(folder *vfs.BaseVirtualFolder) error {
  874. return provider.addFolder(folder)
  875. }
  876. // UpdateFolder updates the specified virtual folder
  877. func UpdateFolder(folder *vfs.BaseVirtualFolder, users []string) error {
  878. err := provider.updateFolder(folder)
  879. if err == nil {
  880. for _, user := range users {
  881. RemoveCachedWebDAVUser(user)
  882. }
  883. }
  884. return err
  885. }
  886. // DeleteFolder deletes an existing folder.
  887. func DeleteFolder(folderName string) error {
  888. folder, err := provider.getFolderByName(folderName)
  889. if err != nil {
  890. return err
  891. }
  892. err = provider.deleteFolder(&folder)
  893. if err == nil {
  894. for _, user := range folder.Users {
  895. RemoveCachedWebDAVUser(user)
  896. }
  897. delayedQuotaUpdater.resetFolderQuota(folderName)
  898. }
  899. return err
  900. }
  901. // GetFolderByName returns the folder with the specified name if any
  902. func GetFolderByName(name string) (vfs.BaseVirtualFolder, error) {
  903. return provider.getFolderByName(name)
  904. }
  905. // GetFolders returns an array of folders respecting limit and offset
  906. func GetFolders(limit, offset int, order string) ([]vfs.BaseVirtualFolder, error) {
  907. return provider.getFolders(limit, offset, order)
  908. }
  909. // DumpData returns all users and folders
  910. func DumpData() (BackupData, error) {
  911. var data BackupData
  912. users, err := provider.dumpUsers()
  913. if err != nil {
  914. return data, err
  915. }
  916. folders, err := provider.dumpFolders()
  917. if err != nil {
  918. return data, err
  919. }
  920. admins, err := provider.dumpAdmins()
  921. if err != nil {
  922. return data, err
  923. }
  924. data.Users = users
  925. data.Folders = folders
  926. data.Admins = admins
  927. data.Version = DumpVersion
  928. return data, err
  929. }
  930. // ParseDumpData tries to parse data as BackupData
  931. func ParseDumpData(data []byte) (BackupData, error) {
  932. var dump BackupData
  933. err := json.Unmarshal(data, &dump)
  934. return dump, err
  935. }
  936. // GetProviderStatus returns an error if the provider is not available
  937. func GetProviderStatus() ProviderStatus {
  938. err := provider.checkAvailability()
  939. status := ProviderStatus{
  940. Driver: config.Driver,
  941. }
  942. if err == nil {
  943. status.IsActive = true
  944. } else {
  945. status.IsActive = false
  946. status.Error = err.Error()
  947. }
  948. return status
  949. }
  950. // Close releases all provider resources.
  951. // This method is used in test cases.
  952. // Closing an uninitialized provider is not supported
  953. func Close() error {
  954. if availabilityTicker != nil {
  955. availabilityTicker.Stop()
  956. availabilityTickerDone <- true
  957. availabilityTicker = nil
  958. }
  959. return provider.close()
  960. }
  961. func createProvider(basePath string) error {
  962. var err error
  963. sqlPlaceholders = getSQLPlaceholders()
  964. if err = validateSQLTablesPrefix(); err != nil {
  965. return err
  966. }
  967. logSender = fmt.Sprintf("dataprovider_%v", config.Driver)
  968. switch config.Driver {
  969. case SQLiteDataProviderName:
  970. return initializeSQLiteProvider(basePath)
  971. case PGSQLDataProviderName, CockroachDataProviderName:
  972. return initializePGSQLProvider()
  973. case MySQLDataProviderName:
  974. return initializeMySQLProvider()
  975. case BoltDataProviderName:
  976. return initializeBoltProvider(basePath)
  977. case MemoryDataProviderName:
  978. initializeMemoryProvider(basePath)
  979. return nil
  980. default:
  981. return fmt.Errorf("unsupported data provider: %v", config.Driver)
  982. }
  983. }
  984. func buildUserHomeDir(user *User) {
  985. if user.HomeDir == "" {
  986. if config.UsersBaseDir != "" {
  987. user.HomeDir = filepath.Join(config.UsersBaseDir, user.Username)
  988. return
  989. }
  990. switch user.FsConfig.Provider {
  991. case sdk.SFTPFilesystemProvider, sdk.S3FilesystemProvider, sdk.AzureBlobFilesystemProvider, sdk.GCSFilesystemProvider:
  992. if tempPath != "" {
  993. user.HomeDir = filepath.Join(tempPath, user.Username)
  994. } else {
  995. user.HomeDir = filepath.Join(os.TempDir(), user.Username)
  996. }
  997. }
  998. }
  999. }
  1000. func isVirtualDirOverlapped(dir1, dir2 string, fullCheck bool) bool {
  1001. if dir1 == dir2 {
  1002. return true
  1003. }
  1004. if fullCheck {
  1005. if len(dir1) > len(dir2) {
  1006. if strings.HasPrefix(dir1, dir2+"/") {
  1007. return true
  1008. }
  1009. }
  1010. if len(dir2) > len(dir1) {
  1011. if strings.HasPrefix(dir2, dir1+"/") {
  1012. return true
  1013. }
  1014. }
  1015. }
  1016. return false
  1017. }
  1018. func isMappedDirOverlapped(dir1, dir2 string, fullCheck bool) bool {
  1019. if dir1 == dir2 {
  1020. return true
  1021. }
  1022. if fullCheck {
  1023. if len(dir1) > len(dir2) {
  1024. if strings.HasPrefix(dir1, dir2+string(os.PathSeparator)) {
  1025. return true
  1026. }
  1027. }
  1028. if len(dir2) > len(dir1) {
  1029. if strings.HasPrefix(dir2, dir1+string(os.PathSeparator)) {
  1030. return true
  1031. }
  1032. }
  1033. }
  1034. return false
  1035. }
  1036. func validateFolderQuotaLimits(folder vfs.VirtualFolder) error {
  1037. if folder.QuotaSize < -1 {
  1038. return util.NewValidationError(fmt.Sprintf("invalid quota_size: %v folder path %#v", folder.QuotaSize, folder.MappedPath))
  1039. }
  1040. if folder.QuotaFiles < -1 {
  1041. return util.NewValidationError(fmt.Sprintf("invalid quota_file: %v folder path %#v", folder.QuotaFiles, folder.MappedPath))
  1042. }
  1043. if (folder.QuotaSize == -1 && folder.QuotaFiles != -1) || (folder.QuotaFiles == -1 && folder.QuotaSize != -1) {
  1044. return util.NewValidationError(fmt.Sprintf("virtual folder quota_size and quota_files must be both -1 or >= 0, quota_size: %v quota_files: %v",
  1045. folder.QuotaFiles, folder.QuotaSize))
  1046. }
  1047. return nil
  1048. }
  1049. func getVirtualFolderIfInvalid(folder *vfs.BaseVirtualFolder) *vfs.BaseVirtualFolder {
  1050. if err := ValidateFolder(folder); err == nil {
  1051. return folder
  1052. }
  1053. // we try to get the folder from the data provider if only the Name is populated
  1054. if folder.MappedPath != "" {
  1055. return folder
  1056. }
  1057. if folder.Name == "" {
  1058. return folder
  1059. }
  1060. if folder.FsConfig.Provider != sdk.LocalFilesystemProvider {
  1061. return folder
  1062. }
  1063. if f, err := GetFolderByName(folder.Name); err == nil {
  1064. return &f
  1065. }
  1066. return folder
  1067. }
  1068. func validateUserVirtualFolders(user *User) error {
  1069. if len(user.VirtualFolders) == 0 {
  1070. user.VirtualFolders = []vfs.VirtualFolder{}
  1071. return nil
  1072. }
  1073. var virtualFolders []vfs.VirtualFolder
  1074. mappedPaths := make(map[string]bool)
  1075. virtualPaths := make(map[string]bool)
  1076. for _, v := range user.VirtualFolders {
  1077. cleanedVPath := filepath.ToSlash(path.Clean(v.VirtualPath))
  1078. if !path.IsAbs(cleanedVPath) || cleanedVPath == "/" {
  1079. return util.NewValidationError(fmt.Sprintf("invalid virtual folder %#v", v.VirtualPath))
  1080. }
  1081. if err := validateFolderQuotaLimits(v); err != nil {
  1082. return err
  1083. }
  1084. folder := getVirtualFolderIfInvalid(&v.BaseVirtualFolder)
  1085. if err := ValidateFolder(folder); err != nil {
  1086. return err
  1087. }
  1088. cleanedMPath := folder.MappedPath
  1089. if folder.IsLocalOrLocalCrypted() {
  1090. if isMappedDirOverlapped(cleanedMPath, user.GetHomeDir(), true) {
  1091. return util.NewValidationError(fmt.Sprintf("invalid mapped folder %#v cannot be inside or contain the user home dir %#v",
  1092. folder.MappedPath, user.GetHomeDir()))
  1093. }
  1094. for mPath := range mappedPaths {
  1095. if folder.IsLocalOrLocalCrypted() && isMappedDirOverlapped(mPath, cleanedMPath, false) {
  1096. return util.NewValidationError(fmt.Sprintf("invalid mapped folder %#v overlaps with mapped folder %#v",
  1097. v.MappedPath, mPath))
  1098. }
  1099. }
  1100. mappedPaths[cleanedMPath] = true
  1101. }
  1102. for vPath := range virtualPaths {
  1103. if isVirtualDirOverlapped(vPath, cleanedVPath, false) {
  1104. return util.NewValidationError(fmt.Sprintf("invalid virtual folder %#v overlaps with virtual folder %#v",
  1105. v.VirtualPath, vPath))
  1106. }
  1107. }
  1108. virtualPaths[cleanedVPath] = true
  1109. virtualFolders = append(virtualFolders, vfs.VirtualFolder{
  1110. BaseVirtualFolder: *folder,
  1111. VirtualPath: cleanedVPath,
  1112. QuotaSize: v.QuotaSize,
  1113. QuotaFiles: v.QuotaFiles,
  1114. })
  1115. }
  1116. user.VirtualFolders = virtualFolders
  1117. return nil
  1118. }
  1119. func validatePermissions(user *User) error {
  1120. if len(user.Permissions) == 0 {
  1121. return util.NewValidationError("please grant some permissions to this user")
  1122. }
  1123. permissions := make(map[string][]string)
  1124. if _, ok := user.Permissions["/"]; !ok {
  1125. return util.NewValidationError("permissions for the root dir \"/\" must be set")
  1126. }
  1127. for dir, perms := range user.Permissions {
  1128. if len(perms) == 0 && dir == "/" {
  1129. return util.NewValidationError(fmt.Sprintf("no permissions granted for the directory: %#v", dir))
  1130. }
  1131. if len(perms) > len(ValidPerms) {
  1132. return util.NewValidationError("invalid permissions")
  1133. }
  1134. for _, p := range perms {
  1135. if !util.IsStringInSlice(p, ValidPerms) {
  1136. return util.NewValidationError(fmt.Sprintf("invalid permission: %#v", p))
  1137. }
  1138. }
  1139. cleanedDir := filepath.ToSlash(path.Clean(dir))
  1140. if cleanedDir != "/" {
  1141. cleanedDir = strings.TrimSuffix(cleanedDir, "/")
  1142. }
  1143. if !path.IsAbs(cleanedDir) {
  1144. return util.NewValidationError(fmt.Sprintf("cannot set permissions for non absolute path: %#v", dir))
  1145. }
  1146. if dir != cleanedDir && cleanedDir == "/" {
  1147. return util.NewValidationError(fmt.Sprintf("cannot set permissions for invalid subdirectory: %#v is an alias for \"/\"", dir))
  1148. }
  1149. if util.IsStringInSlice(PermAny, perms) {
  1150. permissions[cleanedDir] = []string{PermAny}
  1151. } else {
  1152. permissions[cleanedDir] = util.RemoveDuplicates(perms)
  1153. }
  1154. }
  1155. user.Permissions = permissions
  1156. return nil
  1157. }
  1158. func validatePublicKeys(user *User) error {
  1159. if len(user.PublicKeys) == 0 {
  1160. user.PublicKeys = []string{}
  1161. }
  1162. var validatedKeys []string
  1163. for i, k := range user.PublicKeys {
  1164. if k == "" {
  1165. continue
  1166. }
  1167. _, _, _, _, err := ssh.ParseAuthorizedKey([]byte(k))
  1168. if err != nil {
  1169. return util.NewValidationError(fmt.Sprintf("could not parse key nr. %d: %s", i+1, err))
  1170. }
  1171. validatedKeys = append(validatedKeys, k)
  1172. }
  1173. user.PublicKeys = util.RemoveDuplicates(validatedKeys)
  1174. return nil
  1175. }
  1176. func validateFiltersPatternExtensions(user *User) error {
  1177. if len(user.Filters.FilePatterns) == 0 {
  1178. user.Filters.FilePatterns = []sdk.PatternsFilter{}
  1179. return nil
  1180. }
  1181. filteredPaths := []string{}
  1182. var filters []sdk.PatternsFilter
  1183. for _, f := range user.Filters.FilePatterns {
  1184. cleanedPath := filepath.ToSlash(path.Clean(f.Path))
  1185. if !path.IsAbs(cleanedPath) {
  1186. return util.NewValidationError(fmt.Sprintf("invalid path %#v for file patterns filter", f.Path))
  1187. }
  1188. if util.IsStringInSlice(cleanedPath, filteredPaths) {
  1189. return util.NewValidationError(fmt.Sprintf("duplicate file patterns filter for path %#v", f.Path))
  1190. }
  1191. if len(f.AllowedPatterns) == 0 && len(f.DeniedPatterns) == 0 {
  1192. return util.NewValidationError(fmt.Sprintf("empty file patterns filter for path %#v", f.Path))
  1193. }
  1194. f.Path = cleanedPath
  1195. allowed := make([]string, 0, len(f.AllowedPatterns))
  1196. denied := make([]string, 0, len(f.DeniedPatterns))
  1197. for _, pattern := range f.AllowedPatterns {
  1198. _, err := path.Match(pattern, "abc")
  1199. if err != nil {
  1200. return util.NewValidationError(fmt.Sprintf("invalid file pattern filter %#v", pattern))
  1201. }
  1202. allowed = append(allowed, strings.ToLower(pattern))
  1203. }
  1204. for _, pattern := range f.DeniedPatterns {
  1205. _, err := path.Match(pattern, "abc")
  1206. if err != nil {
  1207. return util.NewValidationError(fmt.Sprintf("invalid file pattern filter %#v", pattern))
  1208. }
  1209. denied = append(denied, strings.ToLower(pattern))
  1210. }
  1211. f.AllowedPatterns = allowed
  1212. f.DeniedPatterns = denied
  1213. filters = append(filters, f)
  1214. filteredPaths = append(filteredPaths, cleanedPath)
  1215. }
  1216. user.Filters.FilePatterns = filters
  1217. return nil
  1218. }
  1219. func checkEmptyFiltersStruct(user *User) {
  1220. if len(user.Filters.AllowedIP) == 0 {
  1221. user.Filters.AllowedIP = []string{}
  1222. }
  1223. if len(user.Filters.DeniedIP) == 0 {
  1224. user.Filters.DeniedIP = []string{}
  1225. }
  1226. if len(user.Filters.DeniedLoginMethods) == 0 {
  1227. user.Filters.DeniedLoginMethods = []string{}
  1228. }
  1229. if len(user.Filters.DeniedProtocols) == 0 {
  1230. user.Filters.DeniedProtocols = []string{}
  1231. }
  1232. }
  1233. func validateFilters(user *User) error {
  1234. checkEmptyFiltersStruct(user)
  1235. for _, IPMask := range user.Filters.DeniedIP {
  1236. _, _, err := net.ParseCIDR(IPMask)
  1237. if err != nil {
  1238. return util.NewValidationError(fmt.Sprintf("could not parse denied IP/Mask %#v : %v", IPMask, err))
  1239. }
  1240. }
  1241. for _, IPMask := range user.Filters.AllowedIP {
  1242. _, _, err := net.ParseCIDR(IPMask)
  1243. if err != nil {
  1244. return util.NewValidationError(fmt.Sprintf("could not parse allowed IP/Mask %#v : %v", IPMask, err))
  1245. }
  1246. }
  1247. if len(user.Filters.DeniedLoginMethods) >= len(ValidLoginMethods) {
  1248. return util.NewValidationError("invalid denied_login_methods")
  1249. }
  1250. for _, loginMethod := range user.Filters.DeniedLoginMethods {
  1251. if !util.IsStringInSlice(loginMethod, ValidLoginMethods) {
  1252. return util.NewValidationError(fmt.Sprintf("invalid login method: %#v", loginMethod))
  1253. }
  1254. }
  1255. if len(user.Filters.DeniedProtocols) >= len(ValidProtocols) {
  1256. return util.NewValidationError("invalid denied_protocols")
  1257. }
  1258. for _, p := range user.Filters.DeniedProtocols {
  1259. if !util.IsStringInSlice(p, ValidProtocols) {
  1260. return util.NewValidationError(fmt.Sprintf("invalid protocol: %#v", p))
  1261. }
  1262. }
  1263. if user.Filters.TLSUsername != "" {
  1264. if !util.IsStringInSlice(string(user.Filters.TLSUsername), validTLSUsernames) {
  1265. return util.NewValidationError(fmt.Sprintf("invalid TLS username: %#v", user.Filters.TLSUsername))
  1266. }
  1267. }
  1268. for _, opts := range user.Filters.WebClient {
  1269. if !util.IsStringInSlice(opts, sdk.WebClientOptions) {
  1270. return util.NewValidationError(fmt.Sprintf("invalid web client options %#v", opts))
  1271. }
  1272. }
  1273. return validateFiltersPatternExtensions(user)
  1274. }
  1275. func saveGCSCredentials(fsConfig *vfs.Filesystem, helper vfs.ValidatorHelper) error {
  1276. if fsConfig.Provider != sdk.GCSFilesystemProvider {
  1277. return nil
  1278. }
  1279. if fsConfig.GCSConfig.Credentials.GetPayload() == "" {
  1280. return nil
  1281. }
  1282. if config.PreferDatabaseCredentials {
  1283. if fsConfig.GCSConfig.Credentials.IsPlain() {
  1284. fsConfig.GCSConfig.Credentials.SetAdditionalData(helper.GetEncryptionAdditionalData())
  1285. err := fsConfig.GCSConfig.Credentials.Encrypt()
  1286. if err != nil {
  1287. return err
  1288. }
  1289. }
  1290. return nil
  1291. }
  1292. if fsConfig.GCSConfig.Credentials.IsPlain() {
  1293. fsConfig.GCSConfig.Credentials.SetAdditionalData(helper.GetEncryptionAdditionalData())
  1294. err := fsConfig.GCSConfig.Credentials.Encrypt()
  1295. if err != nil {
  1296. return util.NewValidationError(fmt.Sprintf("could not encrypt GCS credentials: %v", err))
  1297. }
  1298. }
  1299. creds, err := json.Marshal(fsConfig.GCSConfig.Credentials)
  1300. if err != nil {
  1301. return util.NewValidationError(fmt.Sprintf("could not marshal GCS credentials: %v", err))
  1302. }
  1303. credentialsFilePath := helper.GetGCSCredentialsFilePath()
  1304. err = os.MkdirAll(filepath.Dir(credentialsFilePath), 0700)
  1305. if err != nil {
  1306. return util.NewValidationError(fmt.Sprintf("could not create GCS credentials dir: %v", err))
  1307. }
  1308. err = os.WriteFile(credentialsFilePath, creds, 0600)
  1309. if err != nil {
  1310. return util.NewValidationError(fmt.Sprintf("could not save GCS credentials: %v", err))
  1311. }
  1312. fsConfig.GCSConfig.Credentials = kms.NewEmptySecret()
  1313. return nil
  1314. }
  1315. func validateBaseParams(user *User) error {
  1316. if user.Username == "" {
  1317. return util.NewValidationError("username is mandatory")
  1318. }
  1319. if !config.SkipNaturalKeysValidation && !usernameRegex.MatchString(user.Username) {
  1320. return util.NewValidationError(fmt.Sprintf("username %#v is not valid, the following characters are allowed: a-zA-Z0-9-_.~",
  1321. user.Username))
  1322. }
  1323. if user.HomeDir == "" {
  1324. return util.NewValidationError("home_dir is mandatory")
  1325. }
  1326. if user.Password == "" && len(user.PublicKeys) == 0 {
  1327. return util.NewValidationError("please set a password or at least a public_key")
  1328. }
  1329. if !filepath.IsAbs(user.HomeDir) {
  1330. return util.NewValidationError(fmt.Sprintf("home_dir must be an absolute path, actual value: %v", user.HomeDir))
  1331. }
  1332. return nil
  1333. }
  1334. func createUserPasswordHash(user *User) error {
  1335. if user.Password != "" && !user.IsPasswordHashed() {
  1336. if config.PasswordHashing.Algo == HashingAlgoBcrypt {
  1337. pwd, err := bcrypt.GenerateFromPassword([]byte(user.Password), config.PasswordHashing.BcryptOptions.Cost)
  1338. if err != nil {
  1339. return err
  1340. }
  1341. user.Password = string(pwd)
  1342. } else {
  1343. pwd, err := argon2id.CreateHash(user.Password, argon2Params)
  1344. if err != nil {
  1345. return err
  1346. }
  1347. user.Password = pwd
  1348. }
  1349. }
  1350. return nil
  1351. }
  1352. // ValidateFolder returns an error if the folder is not valid
  1353. // FIXME: this should be defined as Folder struct method
  1354. func ValidateFolder(folder *vfs.BaseVirtualFolder) error {
  1355. if folder.Name == "" {
  1356. return util.NewValidationError("folder name is mandatory")
  1357. }
  1358. if !config.SkipNaturalKeysValidation && !usernameRegex.MatchString(folder.Name) {
  1359. return util.NewValidationError(fmt.Sprintf("folder name %#v is not valid, the following characters are allowed: a-zA-Z0-9-_.~",
  1360. folder.Name))
  1361. }
  1362. if folder.FsConfig.Provider == sdk.LocalFilesystemProvider || folder.FsConfig.Provider == sdk.CryptedFilesystemProvider ||
  1363. folder.MappedPath != "" {
  1364. cleanedMPath := filepath.Clean(folder.MappedPath)
  1365. if !filepath.IsAbs(cleanedMPath) {
  1366. return util.NewValidationError(fmt.Sprintf("invalid folder mapped path %#v", folder.MappedPath))
  1367. }
  1368. folder.MappedPath = cleanedMPath
  1369. }
  1370. if folder.HasRedactedSecret() {
  1371. return errors.New("cannot save a folder with a redacted secret")
  1372. }
  1373. if err := folder.FsConfig.Validate(folder); err != nil {
  1374. return err
  1375. }
  1376. return saveGCSCredentials(&folder.FsConfig, folder)
  1377. }
  1378. // ValidateUser returns an error if the user is not valid
  1379. // FIXME: this should be defined as User struct method
  1380. func ValidateUser(user *User) error {
  1381. user.SetEmptySecretsIfNil()
  1382. buildUserHomeDir(user)
  1383. if err := validateBaseParams(user); err != nil {
  1384. return err
  1385. }
  1386. if err := validatePermissions(user); err != nil {
  1387. return err
  1388. }
  1389. if user.hasRedactedSecret() {
  1390. return errors.New("cannot save a user with a redacted secret")
  1391. }
  1392. if err := user.FsConfig.Validate(user); err != nil {
  1393. return err
  1394. }
  1395. if err := validateUserVirtualFolders(user); err != nil {
  1396. return err
  1397. }
  1398. if user.Status < 0 || user.Status > 1 {
  1399. return util.NewValidationError(fmt.Sprintf("invalid user status: %v", user.Status))
  1400. }
  1401. if err := createUserPasswordHash(user); err != nil {
  1402. return err
  1403. }
  1404. if err := validatePublicKeys(user); err != nil {
  1405. return err
  1406. }
  1407. if err := validateFilters(user); err != nil {
  1408. return err
  1409. }
  1410. return saveGCSCredentials(&user.FsConfig, user)
  1411. }
  1412. func checkLoginConditions(user *User) error {
  1413. if user.Status < 1 {
  1414. return fmt.Errorf("user %#v is disabled", user.Username)
  1415. }
  1416. if user.ExpirationDate > 0 && user.ExpirationDate < util.GetTimeAsMsSinceEpoch(time.Now()) {
  1417. return fmt.Errorf("user %#v is expired, expiration timestamp: %v current timestamp: %v", user.Username,
  1418. user.ExpirationDate, util.GetTimeAsMsSinceEpoch(time.Now()))
  1419. }
  1420. return nil
  1421. }
  1422. func isPasswordOK(user *User, password string) (bool, error) {
  1423. if config.PasswordCaching {
  1424. found, match := cachedPasswords.Check(user.Username, password)
  1425. if found {
  1426. return match, nil
  1427. }
  1428. }
  1429. match := false
  1430. var err error
  1431. if strings.HasPrefix(user.Password, argonPwdPrefix) {
  1432. match, err = argon2id.ComparePasswordAndHash(password, user.Password)
  1433. if err != nil {
  1434. providerLog(logger.LevelWarn, "error comparing password with argon hash: %v", err)
  1435. return match, err
  1436. }
  1437. } else if strings.HasPrefix(user.Password, bcryptPwdPrefix) {
  1438. if err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)); err != nil {
  1439. return match, ErrInvalidCredentials
  1440. }
  1441. match = true
  1442. } else if util.IsStringPrefixInSlice(user.Password, pbkdfPwdPrefixes) {
  1443. match, err = comparePbkdf2PasswordAndHash(password, user.Password)
  1444. if err != nil {
  1445. return match, err
  1446. }
  1447. } else if util.IsStringPrefixInSlice(user.Password, unixPwdPrefixes) {
  1448. match, err = compareUnixPasswordAndHash(user, password)
  1449. if err != nil {
  1450. return match, err
  1451. }
  1452. }
  1453. if err == nil && match {
  1454. cachedPasswords.Add(user.Username, password)
  1455. }
  1456. return match, err
  1457. }
  1458. func checkUserAndTLSCertificate(user *User, protocol string, tlsCert *x509.Certificate) (User, error) {
  1459. err := checkLoginConditions(user)
  1460. if err != nil {
  1461. return *user, err
  1462. }
  1463. switch protocol {
  1464. case "FTP", "DAV":
  1465. if user.Filters.TLSUsername == sdk.TLSUsernameCN {
  1466. if user.Username == tlsCert.Subject.CommonName {
  1467. return *user, nil
  1468. }
  1469. return *user, fmt.Errorf("CN %#v does not match username %#v", tlsCert.Subject.CommonName, user.Username)
  1470. }
  1471. return *user, errors.New("TLS certificate is not valid")
  1472. default:
  1473. return *user, fmt.Errorf("certificate authentication is not supported for protocol %v", protocol)
  1474. }
  1475. }
  1476. func checkUserAndPass(user *User, password, ip, protocol string) (User, error) {
  1477. err := checkLoginConditions(user)
  1478. if err != nil {
  1479. return *user, err
  1480. }
  1481. if user.Password == "" {
  1482. return *user, errors.New("credentials cannot be null or empty")
  1483. }
  1484. if !user.Filters.Hooks.CheckPasswordDisabled {
  1485. hookResponse, err := executeCheckPasswordHook(user.Username, password, ip, protocol)
  1486. if err != nil {
  1487. providerLog(logger.LevelDebug, "error executing check password hook for user %#v, ip %v, protocol %v: %v",
  1488. user.Username, ip, protocol, err)
  1489. return *user, errors.New("unable to check credentials")
  1490. }
  1491. switch hookResponse.Status {
  1492. case -1:
  1493. // no hook configured
  1494. case 1:
  1495. providerLog(logger.LevelDebug, "password accepted by check password hook for user %#v, ip %v, protocol %v",
  1496. user.Username, ip, protocol)
  1497. return *user, nil
  1498. case 2:
  1499. providerLog(logger.LevelDebug, "partial success from check password hook for user %#v, ip %v, protocol %v",
  1500. user.Username, ip, protocol)
  1501. password = hookResponse.ToVerify
  1502. default:
  1503. providerLog(logger.LevelDebug, "password rejected by check password hook for user %#v, ip %v, protocol %v, status: %v",
  1504. user.Username, ip, protocol, hookResponse.Status)
  1505. return *user, ErrInvalidCredentials
  1506. }
  1507. }
  1508. match, err := isPasswordOK(user, password)
  1509. if !match {
  1510. err = ErrInvalidCredentials
  1511. }
  1512. return *user, err
  1513. }
  1514. func checkUserAndPubKey(user *User, pubKey []byte) (User, string, error) {
  1515. err := checkLoginConditions(user)
  1516. if err != nil {
  1517. return *user, "", err
  1518. }
  1519. if len(user.PublicKeys) == 0 {
  1520. return *user, "", ErrInvalidCredentials
  1521. }
  1522. for i, k := range user.PublicKeys {
  1523. storedPubKey, comment, _, _, err := ssh.ParseAuthorizedKey([]byte(k))
  1524. if err != nil {
  1525. providerLog(logger.LevelWarn, "error parsing stored public key %d for user %v: %v", i, user.Username, err)
  1526. return *user, "", err
  1527. }
  1528. if bytes.Equal(storedPubKey.Marshal(), pubKey) {
  1529. certInfo := ""
  1530. cert, ok := storedPubKey.(*ssh.Certificate)
  1531. if ok {
  1532. certInfo = fmt.Sprintf(" %v ID: %v Serial: %v CA: %v", cert.Type(), cert.KeyId, cert.Serial,
  1533. ssh.FingerprintSHA256(cert.SignatureKey))
  1534. }
  1535. return *user, fmt.Sprintf("%v:%v%v", ssh.FingerprintSHA256(storedPubKey), comment, certInfo), nil
  1536. }
  1537. }
  1538. return *user, "", ErrInvalidCredentials
  1539. }
  1540. func compareUnixPasswordAndHash(user *User, password string) (bool, error) {
  1541. var crypter crypt.Crypter
  1542. if strings.HasPrefix(user.Password, sha512cryptPwdPrefix) {
  1543. crypter = sha512_crypt.New()
  1544. } else if strings.HasPrefix(user.Password, md5cryptPwdPrefix) {
  1545. crypter = md5_crypt.New()
  1546. } else if strings.HasPrefix(user.Password, md5cryptApr1PwdPrefix) {
  1547. crypter = apr1_crypt.New()
  1548. } else {
  1549. return false, errors.New("unix crypt: invalid or unsupported hash format")
  1550. }
  1551. if err := crypter.Verify(user.Password, []byte(password)); err != nil {
  1552. return false, err
  1553. }
  1554. return true, nil
  1555. }
  1556. func comparePbkdf2PasswordAndHash(password, hashedPassword string) (bool, error) {
  1557. vals := strings.Split(hashedPassword, "$")
  1558. if len(vals) != 5 {
  1559. return false, fmt.Errorf("pbkdf2: hash is not in the correct format")
  1560. }
  1561. iterations, err := strconv.Atoi(vals[2])
  1562. if err != nil {
  1563. return false, err
  1564. }
  1565. expected, err := base64.StdEncoding.DecodeString(vals[4])
  1566. if err != nil {
  1567. return false, err
  1568. }
  1569. var salt []byte
  1570. if util.IsStringPrefixInSlice(hashedPassword, pbkdfPwdB64SaltPrefixes) {
  1571. salt, err = base64.StdEncoding.DecodeString(vals[3])
  1572. if err != nil {
  1573. return false, err
  1574. }
  1575. } else {
  1576. salt = []byte(vals[3])
  1577. }
  1578. var hashFunc func() hash.Hash
  1579. if strings.HasPrefix(hashedPassword, pbkdf2SHA256Prefix) || strings.HasPrefix(hashedPassword, pbkdf2SHA256B64SaltPrefix) {
  1580. hashFunc = sha256.New
  1581. } else if strings.HasPrefix(hashedPassword, pbkdf2SHA512Prefix) {
  1582. hashFunc = sha512.New
  1583. } else if strings.HasPrefix(hashedPassword, pbkdf2SHA1Prefix) {
  1584. hashFunc = sha1.New
  1585. } else {
  1586. return false, fmt.Errorf("pbkdf2: invalid or unsupported hash format %v", vals[1])
  1587. }
  1588. df := pbkdf2.Key([]byte(password), salt, iterations, len(expected), hashFunc)
  1589. return subtle.ConstantTimeCompare(df, expected) == 1, nil
  1590. }
  1591. func addCredentialsToUser(user *User) error {
  1592. if err := addFolderCredentialsToUser(user); err != nil {
  1593. return err
  1594. }
  1595. if user.FsConfig.Provider != sdk.GCSFilesystemProvider {
  1596. return nil
  1597. }
  1598. if user.FsConfig.GCSConfig.AutomaticCredentials > 0 {
  1599. return nil
  1600. }
  1601. // Don't read from file if credentials have already been set
  1602. if user.FsConfig.GCSConfig.Credentials.IsValid() {
  1603. return nil
  1604. }
  1605. cred, err := os.ReadFile(user.GetGCSCredentialsFilePath())
  1606. if err != nil {
  1607. return err
  1608. }
  1609. return json.Unmarshal(cred, &user.FsConfig.GCSConfig.Credentials)
  1610. }
  1611. func addFolderCredentialsToUser(user *User) error {
  1612. for idx := range user.VirtualFolders {
  1613. f := &user.VirtualFolders[idx]
  1614. if f.FsConfig.Provider != sdk.GCSFilesystemProvider {
  1615. continue
  1616. }
  1617. if f.FsConfig.GCSConfig.AutomaticCredentials > 0 {
  1618. continue
  1619. }
  1620. // Don't read from file if credentials have already been set
  1621. if f.FsConfig.GCSConfig.Credentials.IsValid() {
  1622. continue
  1623. }
  1624. cred, err := os.ReadFile(f.GetGCSCredentialsFilePath())
  1625. if err != nil {
  1626. return err
  1627. }
  1628. err = json.Unmarshal(cred, f.FsConfig.GCSConfig.Credentials)
  1629. if err != nil {
  1630. return err
  1631. }
  1632. }
  1633. return nil
  1634. }
  1635. func getSSLMode() string {
  1636. if config.Driver == PGSQLDataProviderName || config.Driver == CockroachDataProviderName {
  1637. if config.SSLMode == 0 {
  1638. return "disable"
  1639. } else if config.SSLMode == 1 {
  1640. return "require"
  1641. } else if config.SSLMode == 2 {
  1642. return "verify-ca"
  1643. } else if config.SSLMode == 3 {
  1644. return "verify-full"
  1645. }
  1646. } else if config.Driver == MySQLDataProviderName {
  1647. if config.SSLMode == 0 {
  1648. return "false"
  1649. } else if config.SSLMode == 1 {
  1650. return "true"
  1651. } else if config.SSLMode == 2 {
  1652. return "skip-verify"
  1653. } else if config.SSLMode == 3 {
  1654. return "preferred"
  1655. }
  1656. }
  1657. return ""
  1658. }
  1659. func startAvailabilityTimer() {
  1660. availabilityTicker = time.NewTicker(30 * time.Second)
  1661. availabilityTickerDone = make(chan bool)
  1662. checkDataprovider()
  1663. go func() {
  1664. for {
  1665. select {
  1666. case <-availabilityTickerDone:
  1667. return
  1668. case <-availabilityTicker.C:
  1669. checkDataprovider()
  1670. }
  1671. }
  1672. }()
  1673. }
  1674. func checkDataprovider() {
  1675. err := provider.checkAvailability()
  1676. if err != nil {
  1677. providerLog(logger.LevelWarn, "check availability error: %v", err)
  1678. }
  1679. metric.UpdateDataProviderAvailability(err)
  1680. }
  1681. func terminateInteractiveAuthProgram(cmd *exec.Cmd, isFinished bool) {
  1682. if isFinished {
  1683. return
  1684. }
  1685. providerLog(logger.LevelInfo, "kill interactive auth program after an unexpected error")
  1686. err := cmd.Process.Kill()
  1687. if err != nil {
  1688. providerLog(logger.LevelDebug, "error killing interactive auth program: %v", err)
  1689. }
  1690. }
  1691. func validateKeyboardAuthResponse(response keyboardAuthHookResponse) error {
  1692. if len(response.Questions) == 0 {
  1693. err := errors.New("interactive auth error: hook response does not contain questions")
  1694. providerLog(logger.LevelInfo, "%v", err)
  1695. return err
  1696. }
  1697. if len(response.Questions) != len(response.Echos) {
  1698. err := fmt.Errorf("interactive auth error, http hook response questions don't match echos: %v %v",
  1699. len(response.Questions), len(response.Echos))
  1700. providerLog(logger.LevelInfo, "%v", err)
  1701. return err
  1702. }
  1703. return nil
  1704. }
  1705. func sendKeyboardAuthHTTPReq(url string, request keyboardAuthHookRequest) (keyboardAuthHookResponse, error) {
  1706. var response keyboardAuthHookResponse
  1707. reqAsJSON, err := json.Marshal(request)
  1708. if err != nil {
  1709. providerLog(logger.LevelWarn, "error serializing keyboard interactive auth request: %v", err)
  1710. return response, err
  1711. }
  1712. resp, err := httpclient.Post(url, "application/json", bytes.NewBuffer(reqAsJSON))
  1713. if err != nil {
  1714. providerLog(logger.LevelWarn, "error getting keyboard interactive auth hook HTTP response: %v", err)
  1715. return response, err
  1716. }
  1717. defer resp.Body.Close()
  1718. if resp.StatusCode != http.StatusOK {
  1719. return response, fmt.Errorf("wrong keyboard interactive auth http status code: %v, expected 200", resp.StatusCode)
  1720. }
  1721. err = render.DecodeJSON(resp.Body, &response)
  1722. return response, err
  1723. }
  1724. func executeKeyboardInteractiveHTTPHook(user *User, authHook string, client ssh.KeyboardInteractiveChallenge, ip, protocol string) (int, error) {
  1725. authResult := 0
  1726. requestID := xid.New().String()
  1727. req := keyboardAuthHookRequest{
  1728. Username: user.Username,
  1729. IP: ip,
  1730. Password: user.Password,
  1731. RequestID: requestID,
  1732. }
  1733. var response keyboardAuthHookResponse
  1734. var err error
  1735. for {
  1736. response, err = sendKeyboardAuthHTTPReq(authHook, req)
  1737. if err != nil {
  1738. return authResult, err
  1739. }
  1740. if response.AuthResult != 0 {
  1741. return response.AuthResult, err
  1742. }
  1743. if err = validateKeyboardAuthResponse(response); err != nil {
  1744. return authResult, err
  1745. }
  1746. answers, err := getKeyboardInteractiveAnswers(client, response, user, ip, protocol)
  1747. if err != nil {
  1748. return authResult, err
  1749. }
  1750. req = keyboardAuthHookRequest{
  1751. RequestID: requestID,
  1752. Username: user.Username,
  1753. Password: user.Password,
  1754. Answers: answers,
  1755. Questions: response.Questions,
  1756. }
  1757. }
  1758. }
  1759. func getKeyboardInteractiveAnswers(client ssh.KeyboardInteractiveChallenge, response keyboardAuthHookResponse,
  1760. user *User, ip, protocol string) ([]string, error) {
  1761. questions := response.Questions
  1762. answers, err := client(user.Username, response.Instruction, questions, response.Echos)
  1763. if err != nil {
  1764. providerLog(logger.LevelInfo, "error getting interactive auth client response: %v", err)
  1765. return answers, err
  1766. }
  1767. if len(answers) != len(questions) {
  1768. err = fmt.Errorf("client answers does not match questions, expected: %v actual: %v", questions, answers)
  1769. providerLog(logger.LevelInfo, "keyboard interactive auth error: %v", err)
  1770. return answers, err
  1771. }
  1772. if len(answers) == 1 && response.CheckPwd > 0 {
  1773. _, err = checkUserAndPass(user, answers[0], ip, protocol)
  1774. providerLog(logger.LevelInfo, "interactive auth hook requested password validation for user %#v, validation error: %v",
  1775. user.Username, err)
  1776. if err != nil {
  1777. return answers, err
  1778. }
  1779. answers[0] = "OK"
  1780. }
  1781. return answers, err
  1782. }
  1783. func handleProgramInteractiveQuestions(client ssh.KeyboardInteractiveChallenge, response keyboardAuthHookResponse,
  1784. user *User, stdin io.WriteCloser, ip, protocol string) error {
  1785. answers, err := getKeyboardInteractiveAnswers(client, response, user, ip, protocol)
  1786. if err != nil {
  1787. return err
  1788. }
  1789. for _, answer := range answers {
  1790. if runtime.GOOS == "windows" {
  1791. answer += "\r"
  1792. }
  1793. answer += "\n"
  1794. _, err = stdin.Write([]byte(answer))
  1795. if err != nil {
  1796. providerLog(logger.LevelError, "unable to write client answer to keyboard interactive program: %v", err)
  1797. return err
  1798. }
  1799. }
  1800. return nil
  1801. }
  1802. func executeKeyboardInteractiveProgram(user *User, authHook string, client ssh.KeyboardInteractiveChallenge, ip, protocol string) (int, error) {
  1803. authResult := 0
  1804. ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
  1805. defer cancel()
  1806. cmd := exec.CommandContext(ctx, authHook)
  1807. cmd.Env = append(os.Environ(),
  1808. fmt.Sprintf("SFTPGO_AUTHD_USERNAME=%v", user.Username),
  1809. fmt.Sprintf("SFTPGO_AUTHD_IP=%v", ip),
  1810. fmt.Sprintf("SFTPGO_AUTHD_PASSWORD=%v", user.Password))
  1811. stdout, err := cmd.StdoutPipe()
  1812. if err != nil {
  1813. return authResult, err
  1814. }
  1815. stdin, err := cmd.StdinPipe()
  1816. if err != nil {
  1817. return authResult, err
  1818. }
  1819. err = cmd.Start()
  1820. if err != nil {
  1821. return authResult, err
  1822. }
  1823. var once sync.Once
  1824. scanner := bufio.NewScanner(stdout)
  1825. for scanner.Scan() {
  1826. var response keyboardAuthHookResponse
  1827. err = json.Unmarshal(scanner.Bytes(), &response)
  1828. if err != nil {
  1829. providerLog(logger.LevelInfo, "interactive auth error parsing response: %v", err)
  1830. once.Do(func() { terminateInteractiveAuthProgram(cmd, false) })
  1831. break
  1832. }
  1833. if response.AuthResult != 0 {
  1834. authResult = response.AuthResult
  1835. break
  1836. }
  1837. if err = validateKeyboardAuthResponse(response); err != nil {
  1838. once.Do(func() { terminateInteractiveAuthProgram(cmd, false) })
  1839. break
  1840. }
  1841. go func() {
  1842. err := handleProgramInteractiveQuestions(client, response, user, stdin, ip, protocol)
  1843. if err != nil {
  1844. once.Do(func() { terminateInteractiveAuthProgram(cmd, false) })
  1845. }
  1846. }()
  1847. }
  1848. stdin.Close()
  1849. once.Do(func() { terminateInteractiveAuthProgram(cmd, true) })
  1850. go func() {
  1851. _, err := cmd.Process.Wait()
  1852. if err != nil {
  1853. providerLog(logger.LevelWarn, "error waiting for #%v process to exit: %v", authHook, err)
  1854. }
  1855. }()
  1856. return authResult, err
  1857. }
  1858. func doKeyboardInteractiveAuth(user *User, authHook string, client ssh.KeyboardInteractiveChallenge, ip, protocol string) (User, error) {
  1859. var authResult int
  1860. var err error
  1861. if strings.HasPrefix(authHook, "http") {
  1862. authResult, err = executeKeyboardInteractiveHTTPHook(user, authHook, client, ip, protocol)
  1863. } else {
  1864. authResult, err = executeKeyboardInteractiveProgram(user, authHook, client, ip, protocol)
  1865. }
  1866. if err != nil {
  1867. return *user, err
  1868. }
  1869. if authResult != 1 {
  1870. return *user, fmt.Errorf("keyboard interactive auth failed, result: %v", authResult)
  1871. }
  1872. err = checkLoginConditions(user)
  1873. if err != nil {
  1874. return *user, err
  1875. }
  1876. return *user, nil
  1877. }
  1878. func isCheckPasswordHookDefined(protocol string) bool {
  1879. if config.CheckPasswordHook == "" {
  1880. return false
  1881. }
  1882. if config.CheckPasswordScope == 0 {
  1883. return true
  1884. }
  1885. switch protocol {
  1886. case "SSH":
  1887. return config.CheckPasswordScope&1 != 0
  1888. case "FTP":
  1889. return config.CheckPasswordScope&2 != 0
  1890. case "DAV":
  1891. return config.CheckPasswordScope&4 != 0
  1892. default:
  1893. return false
  1894. }
  1895. }
  1896. func getPasswordHookResponse(username, password, ip, protocol string) ([]byte, error) {
  1897. if strings.HasPrefix(config.CheckPasswordHook, "http") {
  1898. var result []byte
  1899. req := checkPasswordRequest{
  1900. Username: username,
  1901. Password: password,
  1902. IP: ip,
  1903. Protocol: protocol,
  1904. }
  1905. reqAsJSON, err := json.Marshal(req)
  1906. if err != nil {
  1907. return result, err
  1908. }
  1909. resp, err := httpclient.Post(config.CheckPasswordHook, "application/json", bytes.NewBuffer(reqAsJSON))
  1910. if err != nil {
  1911. providerLog(logger.LevelWarn, "error getting check password hook response: %v", err)
  1912. return result, err
  1913. }
  1914. defer resp.Body.Close()
  1915. if resp.StatusCode != http.StatusOK {
  1916. return result, fmt.Errorf("wrong http status code from chek password hook: %v, expected 200", resp.StatusCode)
  1917. }
  1918. return io.ReadAll(io.LimitReader(resp.Body, maxHookResponseSize))
  1919. }
  1920. ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
  1921. defer cancel()
  1922. cmd := exec.CommandContext(ctx, config.CheckPasswordHook)
  1923. cmd.Env = append(os.Environ(),
  1924. fmt.Sprintf("SFTPGO_AUTHD_USERNAME=%v", username),
  1925. fmt.Sprintf("SFTPGO_AUTHD_PASSWORD=%v", password),
  1926. fmt.Sprintf("SFTPGO_AUTHD_IP=%v", ip),
  1927. fmt.Sprintf("SFTPGO_AUTHD_PROTOCOL=%v", protocol),
  1928. )
  1929. return cmd.Output()
  1930. }
  1931. func executeCheckPasswordHook(username, password, ip, protocol string) (checkPasswordResponse, error) {
  1932. var response checkPasswordResponse
  1933. if !isCheckPasswordHookDefined(protocol) {
  1934. response.Status = -1
  1935. return response, nil
  1936. }
  1937. startTime := time.Now()
  1938. out, err := getPasswordHookResponse(username, password, ip, protocol)
  1939. providerLog(logger.LevelDebug, "check password hook executed, error: %v, elapsed: %v", err, time.Since(startTime))
  1940. if err != nil {
  1941. return response, err
  1942. }
  1943. err = json.Unmarshal(out, &response)
  1944. return response, err
  1945. }
  1946. func getPreLoginHookResponse(loginMethod, ip, protocol string, userAsJSON []byte) ([]byte, error) {
  1947. if strings.HasPrefix(config.PreLoginHook, "http") {
  1948. var url *url.URL
  1949. var result []byte
  1950. url, err := url.Parse(config.PreLoginHook)
  1951. if err != nil {
  1952. providerLog(logger.LevelWarn, "invalid url for pre-login hook %#v, error: %v", config.PreLoginHook, err)
  1953. return result, err
  1954. }
  1955. q := url.Query()
  1956. q.Add("login_method", loginMethod)
  1957. q.Add("ip", ip)
  1958. q.Add("protocol", protocol)
  1959. url.RawQuery = q.Encode()
  1960. resp, err := httpclient.Post(url.String(), "application/json", bytes.NewBuffer(userAsJSON))
  1961. if err != nil {
  1962. providerLog(logger.LevelWarn, "error getting pre-login hook response: %v", err)
  1963. return result, err
  1964. }
  1965. defer resp.Body.Close()
  1966. if resp.StatusCode == http.StatusNoContent {
  1967. return result, nil
  1968. }
  1969. if resp.StatusCode != http.StatusOK {
  1970. return result, fmt.Errorf("wrong pre-login hook http status code: %v, expected 200", resp.StatusCode)
  1971. }
  1972. return io.ReadAll(io.LimitReader(resp.Body, maxHookResponseSize))
  1973. }
  1974. ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
  1975. defer cancel()
  1976. cmd := exec.CommandContext(ctx, config.PreLoginHook)
  1977. cmd.Env = append(os.Environ(),
  1978. fmt.Sprintf("SFTPGO_LOGIND_USER=%v", string(userAsJSON)),
  1979. fmt.Sprintf("SFTPGO_LOGIND_METHOD=%v", loginMethod),
  1980. fmt.Sprintf("SFTPGO_LOGIND_IP=%v", ip),
  1981. fmt.Sprintf("SFTPGO_LOGIND_PROTOCOL=%v", protocol),
  1982. )
  1983. return cmd.Output()
  1984. }
  1985. func executePreLoginHook(username, loginMethod, ip, protocol string) (User, error) {
  1986. u, userAsJSON, err := getUserAndJSONForHook(username)
  1987. if err != nil {
  1988. return u, err
  1989. }
  1990. if u.Filters.Hooks.PreLoginDisabled {
  1991. return u, nil
  1992. }
  1993. startTime := time.Now()
  1994. out, err := getPreLoginHookResponse(loginMethod, ip, protocol, userAsJSON)
  1995. if err != nil {
  1996. return u, fmt.Errorf("pre-login hook error: %v, username %#v, ip %v, protocol %v elapsed %v",
  1997. err, username, ip, protocol, time.Since(startTime))
  1998. }
  1999. providerLog(logger.LevelDebug, "pre-login hook completed, elapsed: %v", time.Since(startTime))
  2000. if util.IsByteArrayEmpty(out) {
  2001. providerLog(logger.LevelDebug, "empty response from pre-login hook, no modification requested for user %#v id: %v",
  2002. username, u.ID)
  2003. if u.ID == 0 {
  2004. return u, util.NewRecordNotFoundError(fmt.Sprintf("username %#v does not exist", username))
  2005. }
  2006. return u, nil
  2007. }
  2008. userID := u.ID
  2009. userPwd := u.Password
  2010. userUsedQuotaSize := u.UsedQuotaSize
  2011. userUsedQuotaFiles := u.UsedQuotaFiles
  2012. userLastQuotaUpdate := u.LastQuotaUpdate
  2013. userLastLogin := u.LastLogin
  2014. err = json.Unmarshal(out, &u)
  2015. if err != nil {
  2016. return u, fmt.Errorf("invalid pre-login hook response %#v, error: %v", string(out), err)
  2017. }
  2018. u.ID = userID
  2019. u.UsedQuotaSize = userUsedQuotaSize
  2020. u.UsedQuotaFiles = userUsedQuotaFiles
  2021. u.LastQuotaUpdate = userLastQuotaUpdate
  2022. u.LastLogin = userLastLogin
  2023. if userID == 0 {
  2024. err = provider.addUser(&u)
  2025. } else {
  2026. err = provider.updateUser(&u)
  2027. if err == nil {
  2028. webDAVUsersCache.swap(&u)
  2029. if u.Password != userPwd {
  2030. cachedPasswords.Remove(username)
  2031. }
  2032. }
  2033. }
  2034. if err != nil {
  2035. return u, err
  2036. }
  2037. providerLog(logger.LevelDebug, "user %#v added/updated from pre-login hook response, id: %v", username, userID)
  2038. if userID == 0 {
  2039. return provider.userExists(username)
  2040. }
  2041. return u, nil
  2042. }
  2043. // ExecutePostLoginHook executes the post login hook if defined
  2044. func ExecutePostLoginHook(user *User, loginMethod, ip, protocol string, err error) {
  2045. if config.PostLoginHook == "" {
  2046. return
  2047. }
  2048. if config.PostLoginScope == 1 && err == nil {
  2049. return
  2050. }
  2051. if config.PostLoginScope == 2 && err != nil {
  2052. return
  2053. }
  2054. go func() {
  2055. status := "0"
  2056. if err == nil {
  2057. status = "1"
  2058. }
  2059. user.PrepareForRendering()
  2060. userAsJSON, err := json.Marshal(user)
  2061. if err != nil {
  2062. providerLog(logger.LevelWarn, "error serializing user in post login hook: %v", err)
  2063. return
  2064. }
  2065. if strings.HasPrefix(config.PostLoginHook, "http") {
  2066. var url *url.URL
  2067. url, err := url.Parse(config.PostLoginHook)
  2068. if err != nil {
  2069. providerLog(logger.LevelDebug, "Invalid post-login hook %#v", config.PostLoginHook)
  2070. return
  2071. }
  2072. q := url.Query()
  2073. q.Add("login_method", loginMethod)
  2074. q.Add("ip", ip)
  2075. q.Add("protocol", protocol)
  2076. q.Add("status", status)
  2077. url.RawQuery = q.Encode()
  2078. startTime := time.Now()
  2079. respCode := 0
  2080. resp, err := httpclient.RetryablePost(url.String(), "application/json", bytes.NewBuffer(userAsJSON))
  2081. if err == nil {
  2082. respCode = resp.StatusCode
  2083. resp.Body.Close()
  2084. }
  2085. providerLog(logger.LevelDebug, "post login hook executed for user %#v, ip %v, protocol %v, response code: %v, elapsed: %v err: %v",
  2086. user.Username, ip, protocol, respCode, time.Since(startTime), err)
  2087. return
  2088. }
  2089. ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
  2090. defer cancel()
  2091. cmd := exec.CommandContext(ctx, config.PostLoginHook)
  2092. cmd.Env = append(os.Environ(),
  2093. fmt.Sprintf("SFTPGO_LOGIND_USER=%v", string(userAsJSON)),
  2094. fmt.Sprintf("SFTPGO_LOGIND_IP=%v", ip),
  2095. fmt.Sprintf("SFTPGO_LOGIND_METHOD=%v", loginMethod),
  2096. fmt.Sprintf("SFTPGO_LOGIND_STATUS=%v", status),
  2097. fmt.Sprintf("SFTPGO_LOGIND_PROTOCOL=%v", protocol))
  2098. startTime := time.Now()
  2099. err = cmd.Run()
  2100. providerLog(logger.LevelDebug, "post login hook executed for user %#v, ip %v, protocol %v, elapsed %v err: %v",
  2101. user.Username, ip, protocol, time.Since(startTime), err)
  2102. }()
  2103. }
  2104. func getExternalAuthResponse(username, password, pkey, keyboardInteractive, ip, protocol string, cert *x509.Certificate, userAsJSON []byte) ([]byte, error) {
  2105. var tlsCert string
  2106. if cert != nil {
  2107. var err error
  2108. tlsCert, err = util.EncodeTLSCertToPem(cert)
  2109. if err != nil {
  2110. return nil, err
  2111. }
  2112. }
  2113. if strings.HasPrefix(config.ExternalAuthHook, "http") {
  2114. var result []byte
  2115. authRequest := make(map[string]string)
  2116. authRequest["username"] = username
  2117. authRequest["ip"] = ip
  2118. authRequest["password"] = password
  2119. authRequest["public_key"] = pkey
  2120. authRequest["protocol"] = protocol
  2121. authRequest["keyboard_interactive"] = keyboardInteractive
  2122. authRequest["tls_cert"] = tlsCert
  2123. if len(userAsJSON) > 0 {
  2124. authRequest["user"] = string(userAsJSON)
  2125. }
  2126. authRequestAsJSON, err := json.Marshal(authRequest)
  2127. if err != nil {
  2128. providerLog(logger.LevelWarn, "error serializing external auth request: %v", err)
  2129. return result, err
  2130. }
  2131. resp, err := httpclient.Post(config.ExternalAuthHook, "application/json", bytes.NewBuffer(authRequestAsJSON))
  2132. if err != nil {
  2133. providerLog(logger.LevelWarn, "error getting external auth hook HTTP response: %v", err)
  2134. return result, err
  2135. }
  2136. defer resp.Body.Close()
  2137. providerLog(logger.LevelDebug, "external auth hook executed, response code: %v", resp.StatusCode)
  2138. if resp.StatusCode != http.StatusOK {
  2139. return result, fmt.Errorf("wrong external auth http status code: %v, expected 200", resp.StatusCode)
  2140. }
  2141. return io.ReadAll(io.LimitReader(resp.Body, maxHookResponseSize))
  2142. }
  2143. ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
  2144. defer cancel()
  2145. cmd := exec.CommandContext(ctx, config.ExternalAuthHook)
  2146. cmd.Env = append(os.Environ(),
  2147. fmt.Sprintf("SFTPGO_AUTHD_USERNAME=%v", username),
  2148. fmt.Sprintf("SFTPGO_AUTHD_USER=%v", string(userAsJSON)),
  2149. fmt.Sprintf("SFTPGO_AUTHD_IP=%v", ip),
  2150. fmt.Sprintf("SFTPGO_AUTHD_PASSWORD=%v", password),
  2151. fmt.Sprintf("SFTPGO_AUTHD_PUBLIC_KEY=%v", pkey),
  2152. fmt.Sprintf("SFTPGO_AUTHD_PROTOCOL=%v", protocol),
  2153. fmt.Sprintf("SFTPGO_AUTHD_TLS_CERT=%v", strings.ReplaceAll(tlsCert, "\n", "\\n")),
  2154. fmt.Sprintf("SFTPGO_AUTHD_KEYBOARD_INTERACTIVE=%v", keyboardInteractive))
  2155. return cmd.Output()
  2156. }
  2157. func updateUserFromExtAuthResponse(user *User, password, pkey string) {
  2158. if password != "" {
  2159. user.Password = password
  2160. }
  2161. if pkey != "" && !util.IsStringPrefixInSlice(pkey, user.PublicKeys) {
  2162. user.PublicKeys = append(user.PublicKeys, pkey)
  2163. }
  2164. }
  2165. func doExternalAuth(username, password string, pubKey []byte, keyboardInteractive, ip, protocol string, tlsCert *x509.Certificate) (User, error) {
  2166. var user User
  2167. u, userAsJSON, err := getUserAndJSONForHook(username)
  2168. if err != nil {
  2169. return user, err
  2170. }
  2171. if u.Filters.Hooks.ExternalAuthDisabled {
  2172. return u, nil
  2173. }
  2174. pkey, err := util.GetSSHPublicKeyAsString(pubKey)
  2175. if err != nil {
  2176. return user, err
  2177. }
  2178. startTime := time.Now()
  2179. out, err := getExternalAuthResponse(username, password, pkey, keyboardInteractive, ip, protocol, tlsCert, userAsJSON)
  2180. if err != nil {
  2181. return user, fmt.Errorf("external auth error for user %#v: %v, elapsed: %v", username, err, time.Since(startTime))
  2182. }
  2183. providerLog(logger.LevelDebug, "external auth completed for user %#v, elapsed: %v", username, time.Since(startTime))
  2184. if util.IsByteArrayEmpty(out) {
  2185. providerLog(logger.LevelDebug, "empty response from external hook, no modification requested for user %#v id: %v",
  2186. username, u.ID)
  2187. if u.ID == 0 {
  2188. return u, util.NewRecordNotFoundError(fmt.Sprintf("username %#v does not exist", username))
  2189. }
  2190. return u, nil
  2191. }
  2192. err = json.Unmarshal(out, &user)
  2193. if err != nil {
  2194. return user, fmt.Errorf("invalid external auth response: %v", err)
  2195. }
  2196. // an empty username means authentication failure
  2197. if user.Username == "" {
  2198. return user, ErrInvalidCredentials
  2199. }
  2200. updateUserFromExtAuthResponse(&user, password, pkey)
  2201. // some users want to map multiple login usernames with a single SFTPGo account
  2202. // for example an SFTP user logins using "user1" or "user2" and the external auth
  2203. // returns "user" in both cases, so we use the username returned from
  2204. // external auth and not the one used to login
  2205. if user.Username != username {
  2206. u, err = provider.userExists(user.Username)
  2207. }
  2208. if u.ID > 0 && err == nil {
  2209. user.ID = u.ID
  2210. user.UsedQuotaSize = u.UsedQuotaSize
  2211. user.UsedQuotaFiles = u.UsedQuotaFiles
  2212. user.LastQuotaUpdate = u.LastQuotaUpdate
  2213. user.LastLogin = u.LastLogin
  2214. err = provider.updateUser(&user)
  2215. if err == nil {
  2216. webDAVUsersCache.swap(&user)
  2217. cachedPasswords.Add(user.Username, password)
  2218. }
  2219. return user, err
  2220. }
  2221. err = provider.addUser(&user)
  2222. if err != nil {
  2223. return user, err
  2224. }
  2225. return provider.userExists(user.Username)
  2226. }
  2227. func getUserAndJSONForHook(username string) (User, []byte, error) {
  2228. var userAsJSON []byte
  2229. u, err := provider.userExists(username)
  2230. if err != nil {
  2231. if _, ok := err.(*util.RecordNotFoundError); !ok {
  2232. return u, userAsJSON, err
  2233. }
  2234. u = User{
  2235. BaseUser: sdk.BaseUser{
  2236. ID: 0,
  2237. Username: username,
  2238. },
  2239. }
  2240. }
  2241. userAsJSON, err = json.Marshal(u)
  2242. if err != nil {
  2243. return u, userAsJSON, err
  2244. }
  2245. return u, userAsJSON, err
  2246. }
  2247. func providerLog(level logger.LogLevel, format string, v ...interface{}) {
  2248. logger.Log(level, logSender, "", format, v...)
  2249. }
  2250. func executeNotificationCommand(operation string, commandArgs []string, userAsJSON []byte) error {
  2251. if !filepath.IsAbs(config.Actions.Hook) {
  2252. err := fmt.Errorf("invalid notification command %#v", config.Actions.Hook)
  2253. logger.Warn(logSender, "", "unable to execute notification command: %v", err)
  2254. return err
  2255. }
  2256. ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
  2257. defer cancel()
  2258. cmd := exec.CommandContext(ctx, config.Actions.Hook, commandArgs...)
  2259. cmd.Env = append(os.Environ(),
  2260. fmt.Sprintf("SFTPGO_USER_ACTION=%v", operation),
  2261. fmt.Sprintf("SFTPGO_USER=%v", string(userAsJSON)))
  2262. startTime := time.Now()
  2263. err := cmd.Run()
  2264. providerLog(logger.LevelDebug, "executed command %#v with arguments: %+v, elapsed: %v, error: %v",
  2265. config.Actions.Hook, commandArgs, time.Since(startTime), err)
  2266. return err
  2267. }
  2268. func executeAction(operation string, user *User) {
  2269. plugin.Handler.NotifyUserEvent(time.Now(), operation, user)
  2270. if !util.IsStringInSlice(operation, config.Actions.ExecuteOn) {
  2271. return
  2272. }
  2273. if config.Actions.Hook == "" {
  2274. return
  2275. }
  2276. go func() {
  2277. user.PrepareForRendering()
  2278. userAsJSON, err := user.RenderAsJSON(operation != operationDelete)
  2279. if err != nil {
  2280. providerLog(logger.LevelWarn, "unable to serialize user as JSON for operation %#v: %v", operation, err)
  2281. return
  2282. }
  2283. if strings.HasPrefix(config.Actions.Hook, "http") {
  2284. var url *url.URL
  2285. url, err := url.Parse(config.Actions.Hook)
  2286. if err != nil {
  2287. providerLog(logger.LevelWarn, "Invalid http_notification_url %#v for operation %#v: %v", config.Actions.Hook, operation, err)
  2288. return
  2289. }
  2290. q := url.Query()
  2291. q.Add("action", operation)
  2292. url.RawQuery = q.Encode()
  2293. startTime := time.Now()
  2294. resp, err := httpclient.RetryablePost(url.String(), "application/json", bytes.NewBuffer(userAsJSON))
  2295. respCode := 0
  2296. if err == nil {
  2297. respCode = resp.StatusCode
  2298. resp.Body.Close()
  2299. }
  2300. providerLog(logger.LevelDebug, "notified operation %#v to URL: %v status code: %v, elapsed: %v err: %v",
  2301. operation, url.Redacted(), respCode, time.Since(startTime), err)
  2302. } else {
  2303. executeNotificationCommand(operation, user.getNotificationFieldsAsSlice(operation), userAsJSON) //nolint:errcheck // the error is used in test cases only
  2304. }
  2305. }()
  2306. }