dataprovider.go 84 KB

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